fasthttp_util.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package util
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/valyala/fasthttp"
  6. "sync"
  7. "time"
  8. )
  9. var (
  10. fastClient = &fasthttp.Client{
  11. NoDefaultUserAgentHeader: true, // Don't send: User-Agent: fasthttp
  12. MaxConnsPerHost: 1024,
  13. ReadBufferSize: 4096, // Make sure to set this big enough that your whole request can be read at once.
  14. WriteBufferSize: 64 * 1024, // Same but for your response.
  15. ReadTimeout: time.Second,
  16. WriteTimeout: time.Second,
  17. MaxIdleConnDuration: time.Minute,
  18. DisableHeaderNamesNormalizing: true, // If you set the case on your headers correctly you can enable this.
  19. DialDualStack: true,
  20. }
  21. // Put everything in pools to prevent garbage.
  22. bytesPool = sync.Pool{
  23. New: func() interface{} {
  24. b := make([]byte, 0)
  25. return &b
  26. },
  27. }
  28. responsePool = sync.Pool{
  29. New: func() interface{} {
  30. return make(chan *fasthttp.Response)
  31. },
  32. }
  33. )
  34. func FastGet(url string) ([]byte, bool, error) {
  35. req := fasthttp.AcquireRequest()
  36. res := fasthttp.AcquireResponse()
  37. defer fasthttp.ReleaseRequest(req)
  38. defer fasthttp.ReleaseResponse(res)
  39. req.SetRequestURIBytes([]byte(url))
  40. req.Header.Add("Accept-Encoding", "gzip")
  41. err := fastClient.Do(req, res)
  42. if err != nil {
  43. return nil, true, err
  44. }
  45. var data []byte
  46. contentEncoding := res.Header.Peek("Content-Encoding")
  47. if bytes.Compare(contentEncoding, []byte("gzip")) == 0 {
  48. data, err = res.BodyGunzip()
  49. } else {
  50. data = res.Body()
  51. }
  52. out := make([]byte, len(data))
  53. copy(out, data)
  54. if res.StatusCode() >= 400 {
  55. retryable := res.StatusCode() >= 500
  56. return nil, retryable, fmt.Errorf("%s: %d", url, res.StatusCode())
  57. }
  58. if err != nil {
  59. return nil, false, err
  60. }
  61. return out, false, nil
  62. }
  63. func FastReadUrlAsStream(fileUrl string, cipherKey []byte, isContentGzipped bool, isFullChunk bool, offset int64, size int, fn func(data []byte)) (retryable bool, err error) {
  64. if cipherKey != nil {
  65. return readEncryptedUrl(fileUrl, cipherKey, isContentGzipped, isFullChunk, offset, size, fn)
  66. }
  67. req := fasthttp.AcquireRequest()
  68. res := fasthttp.AcquireResponse()
  69. defer fasthttp.ReleaseRequest(req)
  70. defer fasthttp.ReleaseResponse(res)
  71. req.SetRequestURIBytes([]byte(fileUrl))
  72. if isFullChunk {
  73. req.Header.Add("Accept-Encoding", "gzip")
  74. } else {
  75. req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+int64(size)-1))
  76. }
  77. if err = fastClient.Do(req, res); err != nil {
  78. return true, err
  79. }
  80. if res.StatusCode() >= 400 {
  81. retryable = res.StatusCode() >= 500
  82. return retryable, fmt.Errorf("%s: %d", fileUrl, res.StatusCode())
  83. }
  84. contentEncoding := res.Header.Peek("Content-Encoding")
  85. if bytes.Compare(contentEncoding, []byte("gzip")) == 0 {
  86. bodyData, err := res.BodyGunzip()
  87. if err != nil {
  88. return false, err
  89. }
  90. fn(bodyData)
  91. } else {
  92. fn(res.Body())
  93. }
  94. return false, nil
  95. }