proxy.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package proxy
  2. import (
  3. "bytes"
  4. "io"
  5. "io/ioutil"
  6. stdlog "log"
  7. "net/http"
  8. "net/http/httputil"
  9. "net/url"
  10. "go-common/library/conf/env"
  11. "go-common/library/log"
  12. bm "go-common/library/net/http/blademaster"
  13. "go-common/library/net/metadata"
  14. "github.com/pkg/errors"
  15. )
  16. type endpoint struct {
  17. url *url.URL
  18. proxy *httputil.ReverseProxy
  19. condition func(ctx *bm.Context) bool
  20. }
  21. type logger struct{}
  22. func (logger) Write(p []byte) (int, error) {
  23. log.Warn("%s", string(p))
  24. return len(p), nil
  25. }
  26. func newep(rawurl string, condition func(ctx *bm.Context) bool) *endpoint {
  27. u, err := url.Parse(rawurl)
  28. if err != nil {
  29. panic(errors.Errorf("Invalid URL: %s", rawurl))
  30. }
  31. e := &endpoint{
  32. url: u,
  33. }
  34. e.proxy = &httputil.ReverseProxy{
  35. Director: e.director,
  36. ErrorLog: stdlog.New(logger{}, "bm.proxy: ", stdlog.LstdFlags),
  37. }
  38. e.condition = condition
  39. return e
  40. }
  41. func (e *endpoint) director(req *http.Request) {
  42. req.URL.Scheme = e.url.Scheme
  43. req.URL.Host = e.url.Host
  44. // keep the origin request path
  45. if e.url.Path != "" {
  46. req.URL.Path = e.url.Path
  47. }
  48. body, length := rebuildBody(req)
  49. req.Body = body
  50. req.ContentLength = int64(length)
  51. }
  52. func (e *endpoint) ServeHTTP(ctx *bm.Context) {
  53. req := ctx.Request
  54. ip := metadata.String(ctx, metadata.RemoteIP)
  55. logArgs := []log.D{
  56. log.KV("method", req.Method),
  57. log.KV("ip", ip),
  58. log.KV("path", req.URL.Path),
  59. log.KV("params", req.Form.Encode()),
  60. }
  61. if !e.condition(ctx) {
  62. logArgs = append(logArgs, log.KV("proxied", "false"))
  63. log.Infov(ctx, logArgs...)
  64. return
  65. }
  66. logArgs = append(logArgs, log.KV("proxied", "true"))
  67. log.Infov(ctx, logArgs...)
  68. e.proxy.ServeHTTP(ctx.Writer, ctx.Request)
  69. ctx.Abort()
  70. }
  71. func rebuildBody(req *http.Request) (io.ReadCloser, int) {
  72. // GET request
  73. if req.Body == nil {
  74. return nil, 0
  75. }
  76. // Submit with form
  77. if len(req.PostForm) > 0 {
  78. br := bytes.NewReader([]byte(req.PostForm.Encode()))
  79. return ioutil.NopCloser(br), br.Len()
  80. }
  81. // copy the original body
  82. bodyBytes, _ := ioutil.ReadAll(req.Body)
  83. br := bytes.NewReader(bodyBytes)
  84. return ioutil.NopCloser(br), br.Len()
  85. }
  86. func always(ctx *bm.Context) bool {
  87. return true
  88. }
  89. // NewZoneProxy is
  90. func NewZoneProxy(matchZone, dst string) bm.HandlerFunc {
  91. ep := newep(dst, func(*bm.Context) bool {
  92. if env.Zone == matchZone {
  93. return true
  94. }
  95. return false
  96. })
  97. return ep.ServeHTTP
  98. }
  99. // NewAlways is
  100. func NewAlways(dst string) bm.HandlerFunc {
  101. ep := newep(dst, always)
  102. return ep.ServeHTTP
  103. }