proxy.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. // setting Verbose to true will log information on each request sent to the proxy
  18. Verbose bool
  19. Logger *log.Logger
  20. NonproxyHandler http.Handler
  21. reqHandlers []ReqHandler
  22. respHandlers []RespHandler
  23. httpsHandlers []HttpsHandler
  24. Tr *http.Transport
  25. // ConnectDial will be used to create TCP connections for CONNECT requests
  26. // if nil Tr.Dial will be used
  27. ConnectDial func(network string, addr string) (net.Conn, error)
  28. }
  29. var hasPort = regexp.MustCompile(`:\d+$`)
  30. func copyHeaders(dst, src http.Header) {
  31. for k, _ := range dst {
  32. dst.Del(k)
  33. }
  34. for k, vs := range src {
  35. for _, v := range vs {
  36. dst.Add(k, v)
  37. }
  38. }
  39. }
  40. func isEof(r *bufio.Reader) bool {
  41. _, err := r.Peek(1)
  42. if err == io.EOF {
  43. return true
  44. }
  45. return false
  46. }
  47. func (proxy *ProxyHttpServer) filterRequest(r *http.Request, ctx *ProxyCtx) (req *http.Request, resp *http.Response) {
  48. req = r
  49. for _, h := range proxy.reqHandlers {
  50. req, resp = h.Handle(r, ctx)
  51. // non-nil resp means the handler decided to skip sending the request
  52. // and return canned response instead.
  53. if resp != nil {
  54. break
  55. }
  56. }
  57. return
  58. }
  59. func (proxy *ProxyHttpServer) filterResponse(respOrig *http.Response, ctx *ProxyCtx) (resp *http.Response) {
  60. resp = respOrig
  61. for _, h := range proxy.respHandlers {
  62. ctx.Resp = resp
  63. resp = h.Handle(resp, ctx)
  64. }
  65. return
  66. }
  67. func removeProxyHeaders(ctx *ProxyCtx, r *http.Request) {
  68. r.RequestURI = "" // this must be reset when serving a request with the client
  69. ctx.Logf("Sending request %v %v", r.Method, r.URL.String())
  70. // If no Accept-Encoding header exists, Transport will add the headers it can accept
  71. // and would wrap the response body with the relevant reader.
  72. r.Header.Del("Accept-Encoding")
  73. // curl can add that, see
  74. // https://jdebp.eu./FGA/web-proxy-connection-header.html
  75. r.Header.Del("Proxy-Connection")
  76. r.Header.Del("Proxy-Authenticate")
  77. r.Header.Del("Proxy-Authorization")
  78. // Connection, Authenticate and Authorization are single hop Header:
  79. // http://www.w3.org/Protocols/rfc2616/rfc2616.txt
  80. // 14.10 Connection
  81. // The Connection general-header field allows the sender to specify
  82. // options that are desired for that particular connection and MUST NOT
  83. // be communicated by proxies over further connections.
  84. r.Header.Del("Connection")
  85. }
  86. // Standard net/http function. Shouldn't be used directly, http.Serve will use it.
  87. func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  88. //r.Header["X-Forwarded-For"] = w.RemoteAddr()
  89. if r.Method == "CONNECT" {
  90. proxy.handleHttps(w, r)
  91. } else {
  92. ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}
  93. var err error
  94. ctx.Logf("Got request %v %v %v %v", r.URL.Path, r.Host, r.Method, r.URL.String())
  95. if !r.URL.IsAbs() {
  96. proxy.NonproxyHandler.ServeHTTP(w, r)
  97. return
  98. }
  99. r, resp := proxy.filterRequest(r, ctx)
  100. if resp == nil {
  101. removeProxyHeaders(ctx, r)
  102. resp, err = ctx.RoundTrip(r)
  103. if err != nil {
  104. ctx.Error = err
  105. resp = proxy.filterResponse(nil, ctx)
  106. if resp == nil {
  107. ctx.Logf("error read response %v %v:", r.URL.Host, err.Error())
  108. http.Error(w, err.Error(), 500)
  109. return
  110. }
  111. }
  112. ctx.Logf("Received response %v", resp.Status)
  113. }
  114. origBody := resp.Body
  115. resp = proxy.filterResponse(resp, ctx)
  116. defer origBody.Close()
  117. ctx.Logf("Copying response to client %v [%d]", resp.Status, resp.StatusCode)
  118. // http.ResponseWriter will take care of filling the correct response length
  119. // Setting it now, might impose wrong value, contradicting the actual new
  120. // body the user returned.
  121. // We keep the original body to remove the header only if things changed.
  122. // This will prevent problems with HEAD requests where there's no body, yet,
  123. // the Content-Length header should be set.
  124. if origBody != resp.Body {
  125. resp.Header.Del("Content-Length")
  126. }
  127. copyHeaders(w.Header(), resp.Header)
  128. w.WriteHeader(resp.StatusCode)
  129. nr, err := io.Copy(w, resp.Body)
  130. if err := resp.Body.Close(); err != nil {
  131. ctx.Warnf("Can't close response body %v", err)
  132. }
  133. ctx.Logf("Copied %v bytes to client error=%v", nr, err)
  134. }
  135. }
  136. // New proxy server, logs to StdErr by default
  137. func NewProxyHttpServer() *ProxyHttpServer {
  138. proxy := ProxyHttpServer{
  139. Logger: log.New(os.Stderr, "", log.LstdFlags),
  140. reqHandlers: []ReqHandler{},
  141. respHandlers: []RespHandler{},
  142. httpsHandlers: []HttpsHandler{},
  143. NonproxyHandler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  144. http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", 500)
  145. }),
  146. Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify,
  147. Proxy: http.ProxyFromEnvironment},
  148. }
  149. proxy.ConnectDial = dialerFromEnv(&proxy)
  150. return &proxy
  151. }