bfs.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package dao
  2. import (
  3. "context"
  4. "errors"
  5. "io/ioutil"
  6. "net"
  7. "net/http"
  8. nurl "net/url"
  9. "strings"
  10. "time"
  11. "go-common/library/conf/env"
  12. "go-common/library/ecode"
  13. "go-common/library/log"
  14. )
  15. //Capture performs a HTTP Get request for the image url and upload bfs.
  16. func (d *Dao) Capture(c context.Context, url string) (loc string, size int, err error) {
  17. if err = checkURL(url); err != nil {
  18. return
  19. }
  20. bs, ct, err := d.download(c, url)
  21. if err != nil {
  22. return
  23. }
  24. size = len(bs)
  25. if size == 0 {
  26. log.Error("capture image size(%d)|url(%s)", size, url)
  27. return
  28. }
  29. if ct != "image/jpeg" && ct != "image/jpg" && ct != "image/png" && ct != "image/gif" {
  30. log.Error("capture not allow image file type(%s)", ct)
  31. err = ecode.CreativeArticleImageTypeErr
  32. return
  33. }
  34. loc, err = d.UploadImage(c, ct, bs)
  35. return loc, size, err
  36. }
  37. func checkURL(url string) (err error) {
  38. // http || https
  39. if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
  40. log.Error("capture url invalid(%s)", url)
  41. err = ecode.RequestErr
  42. return
  43. }
  44. u, err := nurl.Parse(url)
  45. if err != nil {
  46. log.Error("capture url.Parse error(%v)", err)
  47. err = ecode.RequestErr
  48. return
  49. }
  50. // make sure ip is public. avoid ssrf
  51. ips, err := net.LookupIP(u.Host) // take from 1st argument
  52. if err != nil {
  53. log.Error("capture url(%s) LookupIP failed", url)
  54. err = ecode.RequestErr
  55. return
  56. }
  57. if len(ips) == 0 {
  58. log.Error("capture url(%s) LookupIP length 0", url)
  59. err = ecode.RequestErr
  60. return
  61. }
  62. for _, v := range ips {
  63. if !isPublicIP(v) {
  64. log.Error("capture url(%s) is not public ip(%v)", url, v)
  65. err = ecode.RequestErr
  66. return
  67. }
  68. }
  69. return
  70. }
  71. func isPublicIP(IP net.IP) bool {
  72. if env.DeployEnv == env.DeployEnvDev || env.DeployEnv == env.DeployEnvFat1 || env.DeployEnv == env.DeployEnvUat {
  73. return true
  74. }
  75. if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
  76. return false
  77. }
  78. if ip4 := IP.To4(); ip4 != nil {
  79. switch true {
  80. case ip4[0] == 10:
  81. return false
  82. case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
  83. return false
  84. case ip4[0] == 192 && ip4[1] == 168:
  85. return false
  86. default:
  87. return true
  88. }
  89. }
  90. return false
  91. }
  92. func (d *Dao) download(c context.Context, url string) (bs []byte, ct string, err error) {
  93. req, err := http.NewRequest("GET", url, nil)
  94. if err != nil {
  95. log.Error("capture http.NewRequest error(%v)|url (%s)", err, url)
  96. return
  97. }
  98. // timeout
  99. ctx, cancel := context.WithTimeout(c, 800*time.Millisecond)
  100. req = req.WithContext(ctx)
  101. defer cancel()
  102. resp, err := d.bfsClient.Do(req)
  103. if err != nil {
  104. log.Error("capture d.client.Do error(%v)|url(%s)", err, url)
  105. return
  106. }
  107. defer resp.Body.Close()
  108. if resp.StatusCode != http.StatusOK {
  109. log.Error("capture http.StatusCode nq http.StatusOK(%d)|url(%s)", resp.StatusCode, url)
  110. err = errors.New("Download out image link failed")
  111. return
  112. }
  113. if bs, err = ioutil.ReadAll(resp.Body); err != nil {
  114. log.Error("capture ioutil.ReadAll error(%v)", err)
  115. err = errors.New("Download out image link failed")
  116. return
  117. }
  118. ct = http.DetectContentType(bs)
  119. return
  120. }