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