You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
6.3 KiB

9 years ago
10 years ago
  1. package util
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "github.com/chrislusf/seaweedfs/weed/glog"
  14. )
  15. var (
  16. client *http.Client
  17. Transport *http.Transport
  18. )
  19. func init() {
  20. Transport = &http.Transport{
  21. MaxIdleConnsPerHost: 1024,
  22. }
  23. client = &http.Client{
  24. Transport: Transport,
  25. }
  26. }
  27. func PostBytes(url string, body []byte) ([]byte, error) {
  28. r, err := client.Post(url, "", bytes.NewReader(body))
  29. if err != nil {
  30. return nil, fmt.Errorf("Post to %s: %v", url, err)
  31. }
  32. defer r.Body.Close()
  33. b, err := ioutil.ReadAll(r.Body)
  34. if err != nil {
  35. return nil, fmt.Errorf("Read response body: %v", err)
  36. }
  37. if r.StatusCode >= 400 {
  38. return nil, fmt.Errorf("%s: %s", url, r.Status)
  39. }
  40. return b, nil
  41. }
  42. func Post(url string, values url.Values) ([]byte, error) {
  43. r, err := client.PostForm(url, values)
  44. if err != nil {
  45. return nil, err
  46. }
  47. defer r.Body.Close()
  48. b, err := ioutil.ReadAll(r.Body)
  49. if r.StatusCode >= 400 {
  50. if err != nil {
  51. return nil, fmt.Errorf("%s: %d - %s", url, r.StatusCode, string(b))
  52. } else {
  53. return nil, fmt.Errorf("%s: %s", url, r.Status)
  54. }
  55. }
  56. if err != nil {
  57. return nil, err
  58. }
  59. return b, nil
  60. }
  61. // github.com/chrislusf/seaweedfs/unmaintained/repeated_vacuum/repeated_vacuum.go
  62. // may need increasing http.Client.Timeout
  63. func Get(url string) ([]byte, error) {
  64. r, err := client.Get(url)
  65. if err != nil {
  66. return nil, err
  67. }
  68. defer r.Body.Close()
  69. b, err := ioutil.ReadAll(r.Body)
  70. if r.StatusCode >= 400 {
  71. return nil, fmt.Errorf("%s: %s", url, r.Status)
  72. }
  73. if err != nil {
  74. return nil, err
  75. }
  76. return b, nil
  77. }
  78. func Head(url string) (http.Header, error) {
  79. r, err := client.Head(url)
  80. if err != nil {
  81. return nil, err
  82. }
  83. defer closeResp(r)
  84. if r.StatusCode >= 400 {
  85. return nil, fmt.Errorf("%s: %s", url, r.Status)
  86. }
  87. return r.Header, nil
  88. }
  89. func Delete(url string, jwt string) error {
  90. req, err := http.NewRequest("DELETE", url, nil)
  91. if jwt != "" {
  92. req.Header.Set("Authorization", "BEARER "+string(jwt))
  93. }
  94. if err != nil {
  95. return err
  96. }
  97. resp, e := client.Do(req)
  98. if e != nil {
  99. return e
  100. }
  101. defer resp.Body.Close()
  102. body, err := ioutil.ReadAll(resp.Body)
  103. if err != nil {
  104. return err
  105. }
  106. switch resp.StatusCode {
  107. case http.StatusNotFound, http.StatusAccepted, http.StatusOK:
  108. return nil
  109. }
  110. m := make(map[string]interface{})
  111. if e := json.Unmarshal(body, m); e == nil {
  112. if s, ok := m["error"].(string); ok {
  113. return errors.New(s)
  114. }
  115. }
  116. return errors.New(string(body))
  117. }
  118. func GetBufferStream(url string, values url.Values, allocatedBytes []byte, eachBuffer func([]byte)) error {
  119. r, err := client.PostForm(url, values)
  120. if err != nil {
  121. return err
  122. }
  123. defer closeResp(r)
  124. if r.StatusCode != 200 {
  125. return fmt.Errorf("%s: %s", url, r.Status)
  126. }
  127. for {
  128. n, err := r.Body.Read(allocatedBytes)
  129. if n > 0 {
  130. eachBuffer(allocatedBytes[:n])
  131. }
  132. if err != nil {
  133. if err == io.EOF {
  134. return nil
  135. }
  136. return err
  137. }
  138. }
  139. }
  140. func GetUrlStream(url string, values url.Values, readFn func(io.Reader) error) error {
  141. r, err := client.PostForm(url, values)
  142. if err != nil {
  143. return err
  144. }
  145. defer closeResp(r)
  146. if r.StatusCode != 200 {
  147. return fmt.Errorf("%s: %s", url, r.Status)
  148. }
  149. return readFn(r.Body)
  150. }
  151. func DownloadFile(fileUrl string) (filename string, header http.Header, rc io.ReadCloser, e error) {
  152. response, err := client.Get(fileUrl)
  153. if err != nil {
  154. return "", nil, nil, err
  155. }
  156. header = response.Header
  157. contentDisposition := response.Header["Content-Disposition"]
  158. if len(contentDisposition) > 0 {
  159. idx := strings.Index(contentDisposition[0], "filename=")
  160. if idx != -1 {
  161. filename = contentDisposition[0][idx+len("filename="):]
  162. filename = strings.Trim(filename, "\"")
  163. }
  164. }
  165. rc = response.Body
  166. return
  167. }
  168. func Do(req *http.Request) (resp *http.Response, err error) {
  169. return client.Do(req)
  170. }
  171. func NormalizeUrl(url string) string {
  172. if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
  173. return url
  174. }
  175. return "http://" + url
  176. }
  177. func ReadUrl(fileUrl string, offset int64, size int, buf []byte, isReadRange bool) (int64, error) {
  178. req, err := http.NewRequest("GET", fileUrl, nil)
  179. if err != nil {
  180. return 0, err
  181. }
  182. if isReadRange {
  183. req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+int64(size)-1))
  184. } else {
  185. req.Header.Set("Accept-Encoding", "gzip")
  186. }
  187. r, err := client.Do(req)
  188. if err != nil {
  189. return 0, err
  190. }
  191. defer r.Body.Close()
  192. if r.StatusCode >= 400 {
  193. return 0, fmt.Errorf("%s: %s", fileUrl, r.Status)
  194. }
  195. var reader io.ReadCloser
  196. contentEncoding := r.Header.Get("Content-Encoding")
  197. switch contentEncoding {
  198. case "gzip":
  199. reader, err = gzip.NewReader(r.Body)
  200. defer reader.Close()
  201. default:
  202. reader = r.Body
  203. }
  204. var (
  205. i, m int
  206. n int64
  207. )
  208. // refers to https://github.com/golang/go/blob/master/src/bytes/buffer.go#L199
  209. // commit id c170b14c2c1cfb2fd853a37add92a82fd6eb4318
  210. for {
  211. m, err = reader.Read(buf[i:])
  212. i += m
  213. n += int64(m)
  214. if err == io.EOF {
  215. return n, nil
  216. }
  217. if err != nil {
  218. return n, err
  219. }
  220. if n == int64(len(buf)) {
  221. break
  222. }
  223. }
  224. // drains the response body to avoid memory leak
  225. data, _ := ioutil.ReadAll(reader)
  226. if len(data) != 0 {
  227. glog.V(1).Infof("%s reader has remaining %d bytes", contentEncoding, len(data))
  228. }
  229. return n, err
  230. }
  231. func ReadUrlAsStream(fileUrl string, offset int64, size int, fn func(data []byte)) (int64, error) {
  232. req, err := http.NewRequest("GET", fileUrl, nil)
  233. if err != nil {
  234. return 0, err
  235. }
  236. req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+int64(size)-1))
  237. r, err := client.Do(req)
  238. if err != nil {
  239. return 0, err
  240. }
  241. defer closeResp(r)
  242. if r.StatusCode >= 400 {
  243. return 0, fmt.Errorf("%s: %s", fileUrl, r.Status)
  244. }
  245. var (
  246. m int
  247. n int64
  248. )
  249. buf := make([]byte, 64*1024)
  250. for {
  251. m, err = r.Body.Read(buf)
  252. fn(buf[:m])
  253. n += int64(m)
  254. if err == io.EOF {
  255. return n, nil
  256. }
  257. if err != nil {
  258. return n, err
  259. }
  260. }
  261. }
  262. func ReadUrlAsReaderCloser(fileUrl string, rangeHeader string) (io.ReadCloser, error) {
  263. req, err := http.NewRequest("GET", fileUrl, nil)
  264. if err != nil {
  265. return nil, err
  266. }
  267. if rangeHeader != "" {
  268. req.Header.Add("Range", rangeHeader)
  269. }
  270. r, err := client.Do(req)
  271. if err != nil {
  272. return nil, err
  273. }
  274. if r.StatusCode >= 400 {
  275. return nil, fmt.Errorf("%s: %s", fileUrl, r.Status)
  276. }
  277. return r.Body, nil
  278. }
  279. func closeResp(resp *http.Response) {
  280. io.Copy(ioutil.Discard, resp.Body)
  281. resp.Body.Close()
  282. }