proxy.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package goproxy
  2. import (
  3. "bufio"
  4. "io"
  5. "log"
  6. "net"
  7. "net/http"
  8. "os"
  9. "regexp"
  10. "sync/atomic"
  11. )
  12. // The basic proxy type. Implements http.Handler.
  13. type ProxyHttpServer struct {
  14. // session variable must be aligned in i386
  15. // see http://golang.org/src/pkg/sync/atomic/doc.go#L41
  16. sess int64
  17. // KeepDestinationHeaders indicates the proxy should retain any headers present in the http.Response before proxying
  18. KeepDestinationHeaders bool
  19. // setting Verbose to true will log information on each request sent to the proxy
  20. Verbose bool
  21. Logger Logger
  22. NonproxyHandler http.Handler
  23. reqHandlers []ReqHandler
  24. respHandlers []RespHandler
  25. httpsHandlers []HttpsHandler
  26. Tr *http.Transport
  27. // ConnectDial will be used to create TCP connections for CONNECT requests
  28. // if nil Tr.Dial will be used
  29. ConnectDial func(network string, addr string) (net.Conn, error)
  30. CertStore CertStorage
  31. KeepHeader bool
  32. }
  33. var hasPort = regexp.MustCompile(`:\d+$`)
  34. func copyHeaders(dst, src http.Header, keepDestHeaders bool) {
  35. if !keepDestHeaders {
  36. for k := range dst {
  37. dst.Del(k)
  38. }
  39. }
  40. for k, vs := range src {
  41. for _, v := range vs {
  42. dst.Add(k, v)
  43. }
  44. }
  45. }
  46. func isEof(r *bufio.Reader) bool {
  47. _, err := r.Peek(1)
  48. if err == io.EOF {
  49. return true
  50. }
  51. return false
  52. }
  53. func (proxy *ProxyHttpServer) filterRequest(r *http.Request, ctx *ProxyCtx) (req *http.Request, resp *http.Response) {
  54. req = r
  55. for _, h := range proxy.reqHandlers {
  56. req, resp = h.Handle(r, ctx)
  57. // non-nil resp means the handler decided to skip sending the request
  58. // and return canned response instead.
  59. if resp != nil {
  60. break
  61. }
  62. }
  63. return
  64. }
  65. func (proxy *ProxyHttpServer) filterResponse(respOrig *http.Response, ctx *ProxyCtx) (resp *http.Response) {
  66. resp = respOrig
  67. for _, h := range proxy.respHandlers {
  68. ctx.Resp = resp
  69. resp = h.Handle(resp, ctx)
  70. }
  71. return
  72. }
  73. func removeProxyHeaders(ctx *ProxyCtx, r *http.Request) {
  74. r.RequestURI = "" // this must be reset when serving a request with the client
  75. ctx.Logf("Sending request %v %v", r.Method, r.URL.String())
  76. // If no Accept-Encoding header exists, Transport will add the headers it can accept
  77. // and would wrap the response body with the relevant reader.
  78. r.Header.Del("Accept-Encoding")
  79. // curl can add that, see
  80. // https://jdebp.eu./FGA/web-proxy-connection-header.html
  81. r.Header.Del("Proxy-Connection")
  82. r.Header.Del("Proxy-Authenticate")
  83. r.Header.Del("Proxy-Authorization")
  84. // Connection, Authenticate and Authorization are single hop Header:
  85. // http://www.w3.org/Protocols/rfc2616/rfc2616.txt
  86. // 14.10 Connection
  87. // The Connection general-header field allows the sender to specify
  88. // options that are desired for that particular connection and MUST NOT
  89. // be communicated by proxies over further connections.
  90. // When server reads http request it sets req.Close to true if
  91. // "Connection" header contains "close".
  92. // https://github.com/golang/go/blob/master/src/net/http/request.go#L1080
  93. // Later, transfer.go adds "Connection: close" back when req.Close is true
  94. // https://github.com/golang/go/blob/master/src/net/http/transfer.go#L275
  95. // That's why tests that checks "Connection: close" removal fail
  96. if r.Header.Get("Connection") == "close" {
  97. r.Close = false
  98. }
  99. r.Header.Del("Connection")
  100. }
  101. // Standard net/http function. Shouldn't be used directly, http.Serve will use it.
  102. func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  103. //r.Header["X-Forwarded-For"] = w.RemoteAddr()
  104. if r.Method == "CONNECT" {
  105. proxy.handleHttps(w, r)
  106. } else {
  107. ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy}
  108. var err error
  109. ctx.Logf("Got request %v %v %v %v", r.URL.Path, r.Host, r.Method, r.URL.String())
  110. if !r.URL.IsAbs() {
  111. proxy.NonproxyHandler.ServeHTTP(w, r)
  112. return
  113. }
  114. r, resp := proxy.filterRequest(r, ctx)
  115. if resp == nil {
  116. if isWebSocketRequest(r) {
  117. ctx.Logf("Request looks like websocket upgrade.")
  118. proxy.serveWebsocket(ctx, w, r)
  119. }
  120. if !proxy.KeepHeader {
  121. removeProxyHeaders(ctx, r)
  122. }
  123. resp, err = ctx.RoundTrip(r)
  124. if err != nil {
  125. ctx.Error = err
  126. resp = proxy.filterResponse(nil, ctx)
  127. }
  128. if resp != nil {
  129. ctx.Logf("Received response %v", resp.Status)
  130. }
  131. }
  132. var origBody io.ReadCloser
  133. if resp != nil {
  134. origBody = resp.Body
  135. defer origBody.Close()
  136. }
  137. resp = proxy.filterResponse(resp, ctx)
  138. if resp == nil {
  139. var errorString string
  140. if ctx.Error != nil {
  141. errorString = "error read response " + r.URL.Host + " : " + ctx.Error.Error()
  142. ctx.Logf(errorString)
  143. http.Error(w, ctx.Error.Error(), 500)
  144. } else {
  145. errorString = "error read response " + r.URL.Host
  146. ctx.Logf(errorString)
  147. http.Error(w, errorString, 500)
  148. }
  149. return
  150. }
  151. ctx.Logf("Copying response to client %v [%d]", resp.Status, resp.StatusCode)
  152. // http.ResponseWriter will take care of filling the correct response length
  153. // Setting it now, might impose wrong value, contradicting the actual new
  154. // body the user returned.
  155. // We keep the original body to remove the header only if things changed.
  156. // This will prevent problems with HEAD requests where there's no body, yet,
  157. // the Content-Length header should be set.
  158. if origBody != resp.Body {
  159. resp.Header.Del("Content-Length")
  160. }
  161. copyHeaders(w.Header(), resp.Header, proxy.KeepDestinationHeaders)
  162. w.WriteHeader(resp.StatusCode)
  163. nr, err := io.Copy(w, resp.Body)
  164. if err := resp.Body.Close(); err != nil {
  165. ctx.Warnf("Can't close response body %v", err)
  166. }
  167. ctx.Logf("Copied %v bytes to client error=%v", nr, err)
  168. }
  169. }
  170. // NewProxyHttpServer creates and returns a proxy server, logging to stderr by default
  171. func NewProxyHttpServer() *ProxyHttpServer {
  172. proxy := ProxyHttpServer{
  173. Logger: log.New(os.Stderr, "", log.LstdFlags),
  174. reqHandlers: []ReqHandler{},
  175. respHandlers: []RespHandler{},
  176. httpsHandlers: []HttpsHandler{},
  177. NonproxyHandler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  178. http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", 500)
  179. }),
  180. Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify, Proxy: http.ProxyFromEnvironment},
  181. }
  182. proxy.ConnectDial = dialerFromEnv(&proxy)
  183. return &proxy
  184. }