proxy.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package middleware
  2. import (
  3. "fmt"
  4. "io"
  5. "math/rand"
  6. "net"
  7. "net/http"
  8. "net/http/httputil"
  9. "net/url"
  10. "regexp"
  11. "strings"
  12. "sync"
  13. "sync/atomic"
  14. "time"
  15. "github.com/labstack/echo"
  16. )
  17. // TODO: Handle TLS proxy
  18. type (
  19. // ProxyConfig defines the config for Proxy middleware.
  20. ProxyConfig struct {
  21. // Skipper defines a function to skip middleware.
  22. Skipper Skipper
  23. // Balancer defines a load balancing technique.
  24. // Required.
  25. Balancer ProxyBalancer
  26. // Rewrite defines URL path rewrite rules. The values captured in asterisk can be
  27. // retrieved by index e.g. $1, $2 and so on.
  28. // Examples:
  29. // "/old": "/new",
  30. // "/api/*": "/$1",
  31. // "/js/*": "/public/javascripts/$1",
  32. // "/users/*/orders/*": "/user/$1/order/$2",
  33. Rewrite map[string]string
  34. rewriteRegex map[*regexp.Regexp]string
  35. }
  36. // ProxyTarget defines the upstream target.
  37. ProxyTarget struct {
  38. Name string
  39. URL *url.URL
  40. }
  41. // ProxyBalancer defines an interface to implement a load balancing technique.
  42. ProxyBalancer interface {
  43. AddTarget(*ProxyTarget) bool
  44. RemoveTarget(string) bool
  45. Next() *ProxyTarget
  46. }
  47. commonBalancer struct {
  48. targets []*ProxyTarget
  49. mutex sync.RWMutex
  50. }
  51. // RandomBalancer implements a random load balancing technique.
  52. randomBalancer struct {
  53. *commonBalancer
  54. random *rand.Rand
  55. }
  56. // RoundRobinBalancer implements a round-robin load balancing technique.
  57. roundRobinBalancer struct {
  58. *commonBalancer
  59. i uint32
  60. }
  61. )
  62. var (
  63. // DefaultProxyConfig is the default Proxy middleware config.
  64. DefaultProxyConfig = ProxyConfig{
  65. Skipper: DefaultSkipper,
  66. }
  67. )
  68. func proxyHTTP(t *ProxyTarget) http.Handler {
  69. return httputil.NewSingleHostReverseProxy(t.URL)
  70. }
  71. func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
  72. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  73. in, _, err := c.Response().Hijack()
  74. if err != nil {
  75. c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
  76. return
  77. }
  78. defer in.Close()
  79. out, err := net.Dial("tcp", t.URL.Host)
  80. if err != nil {
  81. he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))
  82. c.Error(he)
  83. return
  84. }
  85. defer out.Close()
  86. // Write header
  87. err = r.Write(out)
  88. if err != nil {
  89. he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))
  90. c.Error(he)
  91. return
  92. }
  93. errCh := make(chan error, 2)
  94. cp := func(dst io.Writer, src io.Reader) {
  95. _, err = io.Copy(dst, src)
  96. errCh <- err
  97. }
  98. go cp(out, in)
  99. go cp(in, out)
  100. err = <-errCh
  101. if err != nil && err != io.EOF {
  102. c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)
  103. }
  104. })
  105. }
  106. // NewRandomBalancer returns a random proxy balancer.
  107. func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
  108. b := &randomBalancer{commonBalancer: new(commonBalancer)}
  109. b.targets = targets
  110. return b
  111. }
  112. // NewRoundRobinBalancer returns a round-robin proxy balancer.
  113. func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
  114. b := &roundRobinBalancer{commonBalancer: new(commonBalancer)}
  115. b.targets = targets
  116. return b
  117. }
  118. // AddTarget adds an upstream target to the list.
  119. func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
  120. for _, t := range b.targets {
  121. if t.Name == target.Name {
  122. return false
  123. }
  124. }
  125. b.mutex.Lock()
  126. defer b.mutex.Unlock()
  127. b.targets = append(b.targets, target)
  128. return true
  129. }
  130. // RemoveTarget removes an upstream target from the list.
  131. func (b *commonBalancer) RemoveTarget(name string) bool {
  132. b.mutex.Lock()
  133. defer b.mutex.Unlock()
  134. for i, t := range b.targets {
  135. if t.Name == name {
  136. b.targets = append(b.targets[:i], b.targets[i+1:]...)
  137. return true
  138. }
  139. }
  140. return false
  141. }
  142. // Next randomly returns an upstream target.
  143. func (b *randomBalancer) Next() *ProxyTarget {
  144. if b.random == nil {
  145. b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
  146. }
  147. b.mutex.RLock()
  148. defer b.mutex.RUnlock()
  149. return b.targets[b.random.Intn(len(b.targets))]
  150. }
  151. // Next returns an upstream target using round-robin technique.
  152. func (b *roundRobinBalancer) Next() *ProxyTarget {
  153. b.i = b.i % uint32(len(b.targets))
  154. t := b.targets[b.i]
  155. atomic.AddUint32(&b.i, 1)
  156. return t
  157. }
  158. // Proxy returns a Proxy middleware.
  159. //
  160. // Proxy middleware forwards the request to upstream server using a configured load balancing technique.
  161. func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
  162. c := DefaultProxyConfig
  163. c.Balancer = balancer
  164. return ProxyWithConfig(c)
  165. }
  166. // ProxyWithConfig returns a Proxy middleware with config.
  167. // See: `Proxy()`
  168. func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
  169. // Defaults
  170. if config.Skipper == nil {
  171. config.Skipper = DefaultLoggerConfig.Skipper
  172. }
  173. if config.Balancer == nil {
  174. panic("echo: proxy middleware requires balancer")
  175. }
  176. config.rewriteRegex = map[*regexp.Regexp]string{}
  177. // Initialize
  178. for k, v := range config.Rewrite {
  179. k = strings.Replace(k, "*", "(\\S*)", -1)
  180. config.rewriteRegex[regexp.MustCompile(k)] = v
  181. }
  182. return func(next echo.HandlerFunc) echo.HandlerFunc {
  183. return func(c echo.Context) (err error) {
  184. if config.Skipper(c) {
  185. return next(c)
  186. }
  187. req := c.Request()
  188. res := c.Response()
  189. tgt := config.Balancer.Next()
  190. // Rewrite
  191. for k, v := range config.rewriteRegex {
  192. replacer := captureTokens(k, req.URL.Path)
  193. if replacer != nil {
  194. req.URL.Path = replacer.Replace(v)
  195. }
  196. }
  197. // Fix header
  198. if req.Header.Get(echo.HeaderXRealIP) == "" {
  199. req.Header.Set(echo.HeaderXRealIP, c.RealIP())
  200. }
  201. if req.Header.Get(echo.HeaderXForwardedProto) == "" {
  202. req.Header.Set(echo.HeaderXForwardedProto, c.Scheme())
  203. }
  204. if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" { // For HTTP, it is automatically set by Go HTTP reverse proxy.
  205. req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
  206. }
  207. // Proxy
  208. switch {
  209. case c.IsWebSocket():
  210. proxyRaw(tgt, c).ServeHTTP(res, req)
  211. case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
  212. default:
  213. proxyHTTP(tgt).ServeHTTP(res, req)
  214. }
  215. return
  216. }
  217. }
  218. }