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.

353 lines
7.6 KiB

9 years ago
10 years ago
5 years ago
5 years ago
5 years ago
5 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 CloseResponse(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 CloseResponse(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 CloseResponse(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, cipherKey []byte, isContentCompressed bool, isFullChunk bool, offset int64, size int, buf []byte) (int64, error) {
  178. if cipherKey != nil {
  179. var n int
  180. err := readEncryptedUrl(fileUrl, cipherKey, isContentCompressed, isFullChunk, offset, size, func(data []byte) {
  181. n = copy(buf, data)
  182. })
  183. return int64(n), err
  184. }
  185. req, err := http.NewRequest("GET", fileUrl, nil)
  186. if err != nil {
  187. return 0, err
  188. }
  189. if !isFullChunk {
  190. req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+int64(size)-1))
  191. } else {
  192. req.Header.Set("Accept-Encoding", "gzip")
  193. }
  194. r, err := client.Do(req)
  195. if err != nil {
  196. return 0, err
  197. }
  198. defer r.Body.Close()
  199. if r.StatusCode >= 400 {
  200. return 0, fmt.Errorf("%s: %s", fileUrl, r.Status)
  201. }
  202. var reader io.ReadCloser
  203. contentEncoding := r.Header.Get("Content-Encoding")
  204. switch contentEncoding {
  205. case "gzip":
  206. reader, err = gzip.NewReader(r.Body)
  207. defer reader.Close()
  208. default:
  209. reader = r.Body
  210. }
  211. var (
  212. i, m int
  213. n int64
  214. )
  215. // refers to https://github.com/golang/go/blob/master/src/bytes/buffer.go#L199
  216. // commit id c170b14c2c1cfb2fd853a37add92a82fd6eb4318
  217. for {
  218. m, err = reader.Read(buf[i:])
  219. i += m
  220. n += int64(m)
  221. if err == io.EOF {
  222. return n, nil
  223. }
  224. if err != nil {
  225. return n, err
  226. }
  227. if n == int64(len(buf)) {
  228. break
  229. }
  230. }
  231. // drains the response body to avoid memory leak
  232. data, _ := ioutil.ReadAll(reader)
  233. if len(data) != 0 {
  234. glog.V(1).Infof("%s reader has remaining %d bytes", contentEncoding, len(data))
  235. }
  236. return n, err
  237. }
  238. func ReadUrlAsStream(fileUrl string, cipherKey []byte, isContentGzipped bool, isFullChunk bool, offset int64, size int, fn func(data []byte)) error {
  239. if cipherKey != nil {
  240. return readEncryptedUrl(fileUrl, cipherKey, isContentGzipped, isFullChunk, offset, size, fn)
  241. }
  242. req, err := http.NewRequest("GET", fileUrl, nil)
  243. if err != nil {
  244. return err
  245. }
  246. if !isFullChunk {
  247. req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+int64(size)-1))
  248. }
  249. r, err := client.Do(req)
  250. if err != nil {
  251. return err
  252. }
  253. defer CloseResponse(r)
  254. if r.StatusCode >= 400 {
  255. return fmt.Errorf("%s: %s", fileUrl, r.Status)
  256. }
  257. var (
  258. m int
  259. )
  260. buf := make([]byte, 64*1024)
  261. for {
  262. m, err = r.Body.Read(buf)
  263. fn(buf[:m])
  264. if err == io.EOF {
  265. return nil
  266. }
  267. if err != nil {
  268. return err
  269. }
  270. }
  271. }
  272. func readEncryptedUrl(fileUrl string, cipherKey []byte, isContentCompressed bool, isFullChunk bool, offset int64, size int, fn func(data []byte)) error {
  273. encryptedData, err := Get(fileUrl)
  274. if err != nil {
  275. return fmt.Errorf("fetch %s: %v", fileUrl, err)
  276. }
  277. decryptedData, err := Decrypt(encryptedData, CipherKey(cipherKey))
  278. if err != nil {
  279. return fmt.Errorf("decrypt %s: %v", fileUrl, err)
  280. }
  281. if isContentCompressed {
  282. decryptedData, err = DecompressData(decryptedData)
  283. if err != nil {
  284. return fmt.Errorf("unzip decrypt %s: %v", fileUrl, err)
  285. }
  286. }
  287. if len(decryptedData) < int(offset)+size {
  288. return fmt.Errorf("read decrypted %s size %d [%d, %d)", fileUrl, len(decryptedData), offset, int(offset)+size)
  289. }
  290. if isFullChunk {
  291. fn(decryptedData)
  292. } else {
  293. fn(decryptedData[int(offset) : int(offset)+size])
  294. }
  295. return nil
  296. }
  297. func ReadUrlAsReaderCloser(fileUrl string, rangeHeader string) (io.ReadCloser, error) {
  298. req, err := http.NewRequest("GET", fileUrl, nil)
  299. if err != nil {
  300. return nil, err
  301. }
  302. if rangeHeader != "" {
  303. req.Header.Add("Range", rangeHeader)
  304. }
  305. r, err := client.Do(req)
  306. if err != nil {
  307. return nil, err
  308. }
  309. if r.StatusCode >= 400 {
  310. return nil, fmt.Errorf("%s: %s", fileUrl, r.Status)
  311. }
  312. return r.Body, nil
  313. }
  314. func CloseResponse(resp *http.Response) {
  315. io.Copy(ioutil.Discard, resp.Body)
  316. resp.Body.Close()
  317. }