http.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package utils
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "os"
  10. "path"
  11. "zhiyuan/pkg/logger"
  12. )
  13. type NotFoundError struct {
  14. Message string
  15. }
  16. func (e NotFoundError) Error() string {
  17. return e.Message
  18. }
  19. type RemoteError struct {
  20. Host string
  21. Err error
  22. }
  23. func (e *RemoteError) Error() string {
  24. return e.Err.Error()
  25. }
  26. var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36"
  27. // HttpCall makes HTTP method call.
  28. func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) {
  29. req, err := http.NewRequest(method, url, body)
  30. if err != nil {
  31. return nil, err
  32. }
  33. if req.Header.Get("User-Agent") == "" {
  34. req.Header.Set("User-Agent", UserAgent)
  35. }
  36. for k, vs := range header {
  37. req.Header[k] = vs
  38. }
  39. resp, err := client.Do(req)
  40. if err != nil {
  41. logger.Sugar.Error(err)
  42. return nil, err
  43. }
  44. return resp.Body, nil
  45. //if resp.StatusCode == 200 {
  46. // return resp.Body, nil
  47. //}
  48. resp.Body.Close()
  49. if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 {
  50. err = fmt.Errorf("resource not found: %s", url)
  51. } else {
  52. err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode)
  53. }
  54. return nil, err
  55. }
  56. // HttpGet gets the specified resource.
  57. // ErrNotFound is returned if the server responds with status 404.
  58. func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
  59. return HttpCall(client, "GET", url, header, nil)
  60. }
  61. // HttpPost posts the specified resource.
  62. // ErrNotFound is returned if the server responds with status 404.
  63. func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) {
  64. return HttpCall(client, "POST", url, header, bytes.NewBuffer(body))
  65. }
  66. // HttpGetToFile gets the specified resource and writes to file.
  67. // ErrNotFound is returned if the server responds with status 404.
  68. func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error {
  69. rc, err := HttpGet(client, url, header)
  70. if err != nil {
  71. return err
  72. }
  73. defer rc.Close()
  74. os.MkdirAll(path.Dir(fileName), os.ModePerm)
  75. f, err := os.Create(fileName)
  76. if err != nil {
  77. return err
  78. }
  79. defer f.Close()
  80. _, err = io.Copy(f, rc)
  81. return err
  82. }
  83. // HttpGetBytes gets the specified resource. ErrNotFound is returned if the server
  84. // responds with status 404.
  85. func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) {
  86. rc, err := HttpGet(client, url, header)
  87. if err != nil {
  88. return nil, err
  89. }
  90. defer rc.Close()
  91. return ioutil.ReadAll(rc)
  92. }
  93. // HttpGetJSON gets the specified resource and mapping to struct.
  94. // ErrNotFound is returned if the server responds with status 404.
  95. func HttpGetJSON(client *http.Client, url string, v interface{}) error {
  96. rc, err := HttpGet(client, url, nil)
  97. if err != nil {
  98. return err
  99. }
  100. defer rc.Close()
  101. err = json.NewDecoder(rc).Decode(v)
  102. if _, ok := err.(*json.SyntaxError); ok {
  103. return fmt.Errorf("JSON syntax error at %s", url)
  104. }
  105. return nil
  106. }
  107. // HttpPostJSON posts the specified resource with struct values,
  108. // and maps results to struct.
  109. // ErrNotFound is returned if the server responds with status 404.
  110. func HttpPostJSON(client *http.Client, url string, header http.Header, body, v interface{}) error {
  111. data, err := json.Marshal(body)
  112. if err != nil {
  113. return err
  114. }
  115. header.Set("content-type", "application/json")
  116. rc, err := HttpPost(client, url, header, data)
  117. if err != nil {
  118. return err
  119. }
  120. defer rc.Close()
  121. err = json.NewDecoder(rc).Decode(v)
  122. if _, ok := err.(*json.SyntaxError); ok {
  123. return fmt.Errorf("JSON syntax error at %s", url)
  124. }
  125. return nil
  126. }
  127. // A RawFile describes a file that can be downloaded.
  128. type RawFile interface {
  129. Name() string
  130. RawUrl() string
  131. Data() []byte
  132. SetData([]byte)
  133. }
  134. // FetchFiles fetches files specified by the rawURL field in parallel.
  135. func FetchFiles(client *http.Client, files []RawFile, header http.Header) error {
  136. ch := make(chan error, len(files))
  137. for i := range files {
  138. go func(i int) {
  139. p, err := HttpGetBytes(client, files[i].RawUrl(), nil)
  140. if err != nil {
  141. ch <- err
  142. return
  143. }
  144. files[i].SetData(p)
  145. ch <- nil
  146. }(i)
  147. }
  148. for range files {
  149. if err := <-ch; err != nil {
  150. return err
  151. }
  152. }
  153. return nil
  154. }