reverse.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package proxy
  2. import (
  3. "io"
  4. "log"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. )
  10. // Hop-by-hop headers. These are removed when sent to the backend.
  11. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
  12. // This list of headers borrowed from stdlib httputil.ReverseProxy
  13. var singleHopHeaders = []string{
  14. "Connection",
  15. "Keep-Alive",
  16. "Proxy-Authenticate",
  17. "Proxy-Authorization",
  18. "Te", // canonicalized version of "TE"
  19. "Trailers",
  20. "Transfer-Encoding",
  21. "Upgrade",
  22. }
  23. func removeSingleHopHeaders(hdrs *http.Header) {
  24. for _, h := range singleHopHeaders {
  25. hdrs.Del(h)
  26. }
  27. }
  28. type reverseProxy struct {
  29. director *director
  30. transport http.RoundTripper
  31. }
  32. func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request) {
  33. proxyreq := new(http.Request)
  34. *proxyreq = *clientreq
  35. // deep-copy the headers, as these will be modified below
  36. proxyreq.Header = make(http.Header)
  37. copyHeader(proxyreq.Header, clientreq.Header)
  38. normalizeRequest(proxyreq)
  39. removeSingleHopHeaders(&proxyreq.Header)
  40. maybeSetForwardedFor(proxyreq)
  41. endpoints := p.director.endpoints()
  42. if len(endpoints) == 0 {
  43. log.Printf("proxy: zero endpoints currently available")
  44. rw.WriteHeader(http.StatusServiceUnavailable)
  45. return
  46. }
  47. var res *http.Response
  48. var err error
  49. for _, ep := range endpoints {
  50. redirectRequest(proxyreq, ep.URL)
  51. res, err = p.transport.RoundTrip(proxyreq)
  52. if err != nil {
  53. log.Printf("proxy: failed to direct request to %s: %v", ep.URL.String(), err)
  54. ep.Failed()
  55. continue
  56. }
  57. break
  58. }
  59. if res == nil {
  60. log.Printf("proxy: unable to get response from %d endpoint(s)", len(endpoints))
  61. rw.WriteHeader(http.StatusBadGateway)
  62. return
  63. }
  64. defer res.Body.Close()
  65. removeSingleHopHeaders(&res.Header)
  66. copyHeader(rw.Header(), res.Header)
  67. rw.WriteHeader(res.StatusCode)
  68. io.Copy(rw, res.Body)
  69. }
  70. func copyHeader(dst, src http.Header) {
  71. for k, vv := range src {
  72. for _, v := range vv {
  73. dst.Add(k, v)
  74. }
  75. }
  76. }
  77. func redirectRequest(req *http.Request, loc url.URL) {
  78. req.URL.Scheme = loc.Scheme
  79. req.URL.Host = loc.Host
  80. }
  81. func normalizeRequest(req *http.Request) {
  82. req.Proto = "HTTP/1.1"
  83. req.ProtoMajor = 1
  84. req.ProtoMinor = 1
  85. req.Close = false
  86. }
  87. func maybeSetForwardedFor(req *http.Request) {
  88. clientIP, _, err := net.SplitHostPort(req.RemoteAddr)
  89. if err != nil {
  90. return
  91. }
  92. // If we aren't the first proxy retain prior
  93. // X-Forwarded-For information as a comma+space
  94. // separated list and fold multiple headers into one.
  95. if prior, ok := req.Header["X-Forwarded-For"]; ok {
  96. clientIP = strings.Join(prior, ", ") + ", " + clientIP
  97. }
  98. req.Header.Set("X-Forwarded-For", clientIP)
  99. }