func (p *WebsocketProxy) Proxy(w http.ResponseWriter, r *http.Request) { hj, ok := w.(http.Hijacker) if !ok { log.Println("hijack assertion failed", r.Host, r.URL.Path) p.handler.ServeHTTP(w, r) // last-ditch effort as plain http return } conn, rw, err := hj.Hijack() if err != nil { log.Println("hijack failed", r.Host, r.URL.Path, err) p.handler.ServeHTTP(w, r) // last-ditch effort as plain http return } defer conn.Close() rw.Flush() wrapreq := new(http.Request) wrapreq.Proto = "HTTP/1.1" wrapreq.ProtoMajor, wrapreq.ProtoMinor = 1, 1 wrapreq.Method = "WEBSOCKET" wrapreq.Host = r.Host const dummy = "/" wrapreq.URL = &url.URL{Path: dummy} var buf bytes.Buffer r.Write(&buf) wrapreq.Body = ioutil.NopCloser(io.MultiReader(&buf, conn)) resp, err := p.transport.RoundTrip(wrapreq) if err != nil || resp.StatusCode != 200 { io.WriteString(conn, "HTTP/1.0 503 Gateway Failed\r\n") io.WriteString(conn, "Connection: close\r\n\r\n") return } defer resp.Body.Close() io.Copy(conn, resp.Body) }
func (f *Forwarder) copyRequest(req *http.Request, u *url.URL) *http.Request { outReq := new(http.Request) *outReq = *req // includes shallow copies of maps, but we handle this below outReq.URL = utils.CopyURL(req.URL) outReq.URL.Scheme = u.Scheme outReq.URL.Host = u.Host // workaround for https://github.com/golang/go/issues/10433 outReq.URL.Opaque = mergeStartingSlashes(req.RequestURI) // raw query is already included in RequestURI, so ignore it to avoid dupes outReq.URL.RawQuery = "" // Do not pass client Host header unless optsetter PassHostHeader is set. if f.passHost != true { outReq.Host = u.Host } outReq.Proto = "HTTP/1.1" outReq.ProtoMajor = 1 outReq.ProtoMinor = 1 // Overwrite close flag so we can keep persistent connection for the backend servers outReq.Close = false outReq.Header = make(http.Header) utils.CopyHeaders(outReq.Header, req.Header) if f.rewriter != nil { f.rewriter.Rewrite(outReq) } return outReq }
// This function alters the original request - adds/removes headers, removes hop headers, // changes the request path. func rewriteRequest(req *http.Request, cmd *command.Forward, upstream *command.Upstream) *http.Request { outReq := new(http.Request) *outReq = *req // includes shallow copies of maps, but we handle this below outReq.URL.Scheme = upstream.Scheme outReq.URL.Host = fmt.Sprintf("%s:%d", upstream.Host, upstream.Port) if len(cmd.RewritePath) != 0 { outReq.URL.Path = cmd.RewritePath } outReq.URL.RawQuery = req.URL.RawQuery outReq.Proto = "HTTP/1.1" outReq.ProtoMajor = 1 outReq.ProtoMinor = 1 outReq.Close = false glog.Infof("Proxying request to: %v", outReq) outReq.Header = make(http.Header) netutils.CopyHeaders(outReq.Header, req.Header) if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { // TODO(pquerna): configure this? Not all backends properly parse the header.. if TRUST_FORWARD_HEADER { if prior, ok := outReq.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } } outReq.Header.Set("X-Forwarded-For", clientIP) } if req.TLS != nil { outReq.Header.Set("X-Forwarded-Proto", "https") } else { outReq.Header.Set("X-Forwarded-Proto", "http") } if req.Host != "" { outReq.Header.Set("X-Forwarded-Host", req.Host) } outReq.Header.Set("X-Forwarded-Server", vulcanHostname) if len(cmd.RemoveHeaders) != 0 { netutils.RemoveHeaders(cmd.RemoveHeaders, outReq.Header) } // Add generic instructions headers to the request if len(cmd.AddHeaders) != 0 { glog.Info("Proxying instructions headers:", cmd.AddHeaders) netutils.CopyHeaders(outReq.Header, cmd.AddHeaders) } // Remove hop-by-hop headers to the backend. Especially // important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. netutils.RemoveHeaders(hopHeaders, outReq.Header) return outReq }
// copyRequest creates a new proxy request with some modifications from an original request. func copyRequest(originalRequest *http.Request) *http.Request { pr := new(http.Request) *pr = *originalRequest pr.Proto = "HTTP/1.1" pr.ProtoMajor = 1 pr.ProtoMinor = 1 pr.Close = false pr.Header = make(http.Header) pr.URL.Scheme = "http" pr.URL.Path = originalRequest.URL.Path // Copy all header fields. for key, values := range originalRequest.Header { for _, value := range values { pr.Header.Add(key, value) } } // Remove ignored header fields. for _, header := range ignoredHeaderNames { pr.Header.Del(header) } // Append this machine's host name into X-Forwarded-For. if requestHost, _, err := net.SplitHostPort(originalRequest.RemoteAddr); err == nil { if originalValues, ok := pr.Header["X-Forwarded-For"]; ok { requestHost = strings.Join(originalValues, ", ") + ", " + requestHost } pr.Header.Set("X-Forwarded-For", requestHost) } return pr }
func (proxy *Proxy) copyRequest(r *http.Request) *http.Request { proxyRequest := new(http.Request) *proxyRequest = *r proxyRequest.Proto = "HTTP/1.1" proxyRequest.ProtoMajor = 1 proxyRequest.ProtoMinor = 1 proxyRequest.Close = false proxyRequest.Header = make(http.Header) proxyRequest.URL.Scheme = "http" proxyRequest.URL.Path = r.URL.Path for key, values := range r.Header { for _, value := range values { proxyRequest.Header.Add(key, value) } } for _, headerName := range ignoredHeaderNames { proxyRequest.Header.Del(headerName) } if requestHost, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { if values, ok := proxyRequest.Header["X-Forwarded-For"]; ok { requestHost = strings.Join(values, ", ") + ", " + requestHost } proxyRequest.Header.Set("X-Forwarded-For", requestHost) } return proxyRequest }
func (f *Forwarder) copyRequest(req *http.Request, u *url.URL) *http.Request { outReq := new(http.Request) *outReq = *req // includes shallow copies of maps, but we handle this below outReq.URL = utils.CopyURL(req.URL) outReq.URL.Scheme = u.Scheme outReq.URL.Host = u.Host outReq.URL.Opaque = req.RequestURI // raw query is already included in RequestURI, so ignore it to avoid dupes outReq.URL.RawQuery = "" // Go doesn't implicitly pass the host header unless you set Host on the request outReq.Host = u.Host outReq.Proto = "HTTP/1.1" outReq.ProtoMajor = 1 outReq.ProtoMinor = 1 // Overwrite close flag so we can keep persistent connection for the backend servers outReq.Close = false outReq.Header = make(http.Header) utils.CopyHeaders(outReq.Header, req.Header) if f.rewriter != nil { f.rewriter.Rewrite(outReq) } return outReq }
// RoundTrip implements http.RoundTripper.RoundTrip. func (binder Binder) RoundTrip(req *http.Request) (*http.Response, error) { if req.Proto == "" { req.Proto = fmt.Sprintf("HTTP/%d.%d", req.ProtoMajor, req.ProtoMinor) } if req.Body != nil { if req.ContentLength == -1 { req.TransferEncoding = []string{"chunked"} } } else { req.Body = ioutil.NopCloser(bytes.NewReader(nil)) } recorder := httptest.NewRecorder() binder.handler.ServeHTTP(recorder, req) resp := http.Response{ Request: req, StatusCode: recorder.Code, Status: http.StatusText(recorder.Code), Header: recorder.HeaderMap, } if recorder.Flushed { resp.TransferEncoding = []string{"chunked"} } if recorder.Body != nil { resp.Body = ioutil.NopCloser(recorder.Body) } return &resp, nil }
func (l *HttpLocation) copyRequest(req *http.Request, body netutils.MultiReader, endpoint endpoint.Endpoint) *http.Request { outReq := new(http.Request) *outReq = *req // includes shallow copies of maps, but we handle this below // Set the body to the enhanced body that can be re-read multiple times and buffered to disk outReq.Body = body endpointURL := endpoint.GetUrl() outReq.URL.Scheme = endpointURL.Scheme outReq.URL.Host = endpointURL.Host outReq.URL.Opaque = req.RequestURI // raw query is already included in RequestURI, so ignore it to avoid dupes outReq.URL.RawQuery = "" outReq.Proto = "HTTP/1.1" outReq.ProtoMajor = 1 outReq.ProtoMinor = 1 // Overwrite close flag so we can keep persistent connection for the backend servers outReq.Close = false outReq.Header = make(http.Header) netutils.CopyHeaders(outReq.Header, req.Header) return outReq }
// NewFastHTTPHandler wraps net/http handler to fasthttp request handler, // so it can be passed to fasthttp server. // // While this function may be used for easy switching from net/http to fasthttp, // it has the following drawbacks comparing to using manually written fasthttp // request handler: // // * A lot of useful functionality provided by fasthttp is missing // from net/http handler. // * net/http -> fasthttp handler conversion has some overhead, // so the returned handler will be always slower than manually written // fasthttp handler. // // So it is advisable using this function only for quick net/http -> fasthttp // switching. Then manually convert net/http handlers to fasthttp handlers // according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp . func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { var r http.Request body := ctx.PostBody() r.Method = string(ctx.Method()) r.Proto = "HTTP/1.1" r.ProtoMajor = 1 r.ProtoMinor = 1 r.RequestURI = string(ctx.RequestURI()) r.ContentLength = int64(len(body)) r.Host = string(ctx.Host()) r.RemoteAddr = ctx.RemoteAddr().String() hdr := make(http.Header) ctx.Request.Header.VisitAll(func(k, v []byte) { hdr.Set(string(k), string(v)) }) r.Header = hdr r.Body = &netHTTPBody{body} var w netHTTPResponseWriter h.ServeHTTP(&w, &r) ctx.SetStatusCode(w.StatusCode()) for k, vv := range w.Header() { for _, v := range vv { ctx.Response.Header.Set(k, v) } } ctx.Write(w.body) } }
func (p *ReverseProxy) ServeHTTP(req *http.Request) (*http.Response, error) { transport := p.Transport if transport == nil { transport = http.DefaultTransport } outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay p.Director(outreq) outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. Especially // important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. This // is modifying the same underlying map from req (shallow // copied above) so we only copy it if necessary. copiedHeaders := false for _, h := range hopHeaders { if outreq.Header.Get(h) != "" { if !copiedHeaders { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) copiedHeaders = true } outreq.Header.Del(h) } } if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { // If we aren't the first proxy retain prior // X-Forwarded-For information as a comma+space // separated list and fold multiple headers into one. if prior, ok := outreq.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } outreq.Header.Set("X-Forwarded-For", clientIP) } res, err := transport.RoundTrip(outreq) if err != nil { return res, err } for _, p := range *p.Plugins { p.Outbound(res) } for _, h := range hopHeaders { res.Header.Del(h) } return res, nil }
func makeRequest(params map[string]string) (*http.Request, error) { r := new(http.Request) r.Method = params["METHOD"] if r.Method == "" { return nil, errors.New("mongrel2: no METHOD") } r.Proto = params["VERSION"] var ok bool r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto) if !ok { return nil, errors.New("mongrel2: invalid protocol version") } r.Trailer = http.Header{} r.Header = http.Header{} r.Host = params["Host"] r.Header.Set("Referer", params["Referer"]) r.Header.Set("User-Agent", params["User-Agent"]) if lenstr := params["Content-Length"]; lenstr != "" { clen, err := strconv.ParseInt(lenstr, 10, 64) if err != nil { return nil, errors.New("mongrel2: bad Content-Length") } r.ContentLength = clen } for k, v := range params { if !skipHeader[k] { r.Header.Add(k, v) } } // TODO: cookies if r.Host != "" { url_, err := url.Parse("http://" + r.Host + params["URI"]) if err != nil { return nil, errors.New("mongrel2: failed to parse host and URI into a URL") } r.URL = url_ } if r.URL == nil { url_, err := url.Parse(params["URI"]) if err != nil { return nil, errors.New("mongrel2: failed to parse URI into a URL") } r.URL = url_ } // TODO: how do we know if we're using HTTPS? // TODO: fill in r.RemoteAddr return r, nil }
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error { transport := p.Transport if transport == nil { transport = http.DefaultTransport } p.Director(outreq) outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false res, err := transport.RoundTrip(outreq) if err != nil { return err } else if respUpdateFn != nil { respUpdateFn(res) } if res.StatusCode == http.StatusSwitchingProtocols && strings.ToLower(res.Header.Get("Upgrade")) == "websocket" { res.Body.Close() hj, ok := rw.(http.Hijacker) if !ok { return nil } conn, _, err := hj.Hijack() if err != nil { return err } defer conn.Close() backendConn, err := net.Dial("tcp", outreq.URL.Host) if err != nil { return err } defer backendConn.Close() outreq.Write(backendConn) go func() { io.Copy(backendConn, conn) // write tcp stream to backend. }() io.Copy(conn, backendConn) // read tcp stream from backend. } else { defer res.Body.Close() for _, h := range hopHeaders { res.Header.Del(h) } copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) p.copyResponse(rw, res.Body) } return nil }
func prepareRequest(req *http.Request) *http.Request { outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay // Pass the Request-URI verbatim without any modifications. // // NOTE: An exception must be made if the Request-URI is a path // beginning with "//" (e.g. "//foo/bar") because then // req.URL.RequestURI() would interpret req.URL.Opaque as being a URI // with the scheme stripped and so generate a URI like scheme:opaque // (e.g. "http://foo/bar") which would be incorrect, see: // https://github.com/golang/go/blob/f75aafd/src/net/url/url.go#L913-L931 // // It is ok to make this exception because the fallback to // req.URL.EscapedPath will generate the correct Request-URI. if !strings.HasPrefix(req.RequestURI, "//") { outreq.URL.Opaque = strings.Split(strings.TrimPrefix(req.RequestURI, req.URL.Scheme+":"), "?")[0] } outreq.URL.Scheme = "http" outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) for _, h := range hopHeaders { outreq.Header.Del(h) } // remove the Upgrade header and headers referenced in the Connection // header if HTTP < 1.1 or if Connection header didn't contain "upgrade": // https://tools.ietf.org/html/rfc7230#section-6.7 if !req.ProtoAtLeast(1, 1) || !isConnectionUpgrade(req.Header) { outreq.Header.Del("Upgrade") // Especially important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. outreq.Header.Del("Connection") // A proxy or gateway MUST parse a received Connection header field before a // message is forwarded and, for each connection-option in this field, remove // any header field(s) from the message with the same name as the // connection-option, and then remove the Connection header field itself (or // replace it with the intermediary's own connection options for the // forwarded message): https://tools.ietf.org/html/rfc7230#section-6.1 tokens := strings.Split(req.Header.Get("Connection"), ",") for _, hdr := range tokens { outreq.Header.Del(hdr) } } return outreq }
func reverseProxy(w http.ResponseWriter, req *http.Request) { logRequest(req) if rSensitivePath.MatchString(req.URL.Path) { w.WriteHeader(http.StatusForbidden) return } outReq := new(http.Request) outReq.Method = req.Method outReq.URL = &url.URL{ Scheme: "http", Host: host, Path: req.URL.Path, RawQuery: req.URL.RawQuery, } outReq.Proto = "HTTP/1.1" outReq.ProtoMajor = 1 outReq.ProtoMinor = 1 outReq.Header = make(http.Header) outReq.Body = req.Body outReq.ContentLength = req.ContentLength outReq.Host = host for _, h := range removeHeaders { req.Header.Del(h) } copyHeader(outReq.Header, req.Header) outReq.Header.Set("Host", host) outReq.Header.Set("Referer", baseURL) outReq.Header.Set("Origin", baseURL) resp, err := send(outReq) if err != nil { log.Printf("proxy error: %v", err) w.WriteHeader(http.StatusInternalServerError) return } defer resp.Body.Close() for _, h := range removeHeaders { resp.Header.Del(h) } if loc := resp.Header.Get("Location"); loc != "" { if u, err := url.Parse(loc); err == nil && u.Host == host { u.Scheme = "http" u.Host = req.Host resp.Header.Set("Location", u.String()) } } copyHeader(w.Header(), resp.Header) w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) }
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request, location string) { transport := p.Transport if transport == nil { transport = http.DefaultTransport } outreq := new(http.Request) p.Director(outreq) outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. Especially // important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. This // is modifying the same underlying map from req (shallow // copied above) so we only copy it if necessary. copiedHeaders := false for _, h := range hopHeaders { if outreq.Header.Get(h) != "" { if !copiedHeaders { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) copiedHeaders = true } outreq.Header.Del(h) } } if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { // If we aren't the first proxy retain prior // X-Forwarded-For information as a comma+space // separated list and fold multiple headers into one. if prior, ok := outreq.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } outreq.Header.Set("X-Forwarded-For", clientIP) } res, err := transport.RoundTrip(outreq) if err != nil { log.Printf("http: proxy error: %v", err) rw.WriteHeader(http.StatusInternalServerError) return } defer res.Body.Close() copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) p.copyResponse(rw, res.Body) }
func httpProxy(writer http.ResponseWriter, request *http.Request) { proxyRequest := new(http.Request) *proxyRequest = *request log.Printf("request = %s %s", request.Method, request.URL.Host) if strings.ToUpper(proxyRequest.Method) == "CONNECT" { hostPort := request.URL.Host pandora, err := dialer.Dial("tcp", hostPort) // tuner.pandora.com:443 if err != nil { log.Printf("pianobarproxy: error: %v", err) writer.WriteHeader(http.StatusInternalServerError) return } client, writer, err := writer.(http.Hijacker).Hijack() writer.WriteString("HTTP/1.0 200 Connection Established\r\n\r\n") writer.Flush() go pipe(client, pandora) go pipe(pandora, client) return } proxyRequest.Proto = "HTTP/1.1" proxyRequest.ProtoMajor = 1 proxyRequest.ProtoMinor = 1 proxyRequest.Close = false // Remove the connection header to the backend. We want a // persistent connection, regardless of what the client sent // to us. if proxyRequest.Header.Get("Connection") != "" { proxyRequest.Header = make(http.Header) copyHeader(proxyRequest.Header, request.Header) proxyRequest.Header.Del("Connection") } response, err := transport.RoundTrip(proxyRequest) if err != nil { log.Printf("pianobarproxy: error: %v", err) writer.WriteHeader(http.StatusInternalServerError) return } copyHeader(writer.Header(), response.Header) writer.WriteHeader(response.StatusCode) if response.Body != nil { io.Copy(io.Writer(writer), response.Body) } }
// ReadRequest reads an HTTP request. The header is taken from h, // which must include the SPDY-specific fields starting with ':'. // If r is not nil, the body will be read from r. If t is not nil, // the trailer will be taken from t after the body is finished. func ReadRequest(h, t http.Header, r io.Reader) (*http.Request, error) { req := new(http.Request) req.Header = make(http.Header) copyHeader(req.Header, h) path := h.Get(":path") if path == "" { return nil, errors.New("missing path") } if path[0] != '/' { return nil, errors.New("invalid path: " + path) } req.URL = &url.URL{ Scheme: h.Get(":scheme"), Path: path, Host: h.Get(":host"), } req.Close = true req.Method = h.Get(":method") req.Host = h.Get(":host") req.Proto = h.Get(":version") var ok bool if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(req.Proto); !ok { return nil, errors.New("bad http version: " + req.Proto) } req.Header.Del("Host") cl := strings.TrimSpace(req.Header.Get("Content-Length")) if cl != "" { n, err := parseContentLength(cl) if err != nil { return nil, err } req.ContentLength = n } else { // Assume GET request has no body by default. if req.Method != "GET" { req.ContentLength = -1 } req.Header.Del("Content-Length") } // TODO(kr): content length / limit reader? if r == nil { r = eofReader } if t != nil { req.Body = &body{r: r, hdr: req, trailer: t} } else { req.Body = &body{r: r} } return req, nil }
func CopyRequest(req *http.Request) *http.Request { outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove hop-by-hop headers to the backend. Especially // important is "Connection" because we want a persistent // connection, regardless of what the client sent to us. This // is modifying the same underlying map from req (shallow // copied above) so we only copy it if necessary. copiedHeaders := false for _, h := range hopHeaders { if outreq.Header.Get(h) != "" { if !copiedHeaders { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) copiedHeaders = true } outreq.Header.Del(h) } } if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { // If we aren't the first proxy retain prior // X-Forwarded-For information as a comma+space // separated list and fold multiple headers into one. if prior, ok := outreq.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } outreq.Header.Set("X-Forwarded-For", clientIP) } // If the request body is not chunked, tee it into the // proxy request's body & and replace it with a buffered // copy of the request body (it will be fully read by // the proxy request). if !isChunked(req) { bodyBuffer := bytes.NewBuffer(make([]byte, 0, req.ContentLength)) bodyReader := io.TeeReader(req.Body, bodyBuffer) outreq.Body = ioutil.NopCloser(bodyReader) req.Body = ioutil.NopCloser(bodyBuffer) } else { outreq.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) } return outreq }
func HttpRoundTrip(req *http.Request) (resp *http.Response, err error) { req.RequestURI = "" if req.Proto == "" { req.Proto = "HTTP/1.1" } if req.URL.Scheme == "" { req.URL.Scheme = "http" } resp, err = http.DefaultTransport.RoundTrip(req) //使用这个避免跟进 redirect if err != nil { return nil, err } return }
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { transport := p.Transport if transport == nil { transport = http.DefaultTransport } outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay p.Director(outreq) outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove the connection header to the backend. We want a // persistent connection, regardless of what the client sent // to us. This is modifying the same underlying map from req // (shallow copied above) so we only copy it if necessary. if outreq.Header.Get("Connection") != "" { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) outreq.Header.Del("Connection") } if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { outreq.Header.Set("X-Forwarded-For", clientIp) } res, err := transport.RoundTrip(outreq) if err != nil { log.Printf("http: proxy error: %v", err) rw.WriteHeader(http.StatusInternalServerError) return } copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) if res.Body != nil { var dst io.Writer = rw if p.FlushInterval != 0 { if wf, ok := rw.(writeFlusher); ok { dst = &maxLatencyWriter{dst: wf, latency: p.FlushInterval} } } io.Copy(dst, res.Body) } }
func (stream *SpdyStream) OnFinRead(quicStream goquic.QuicStream) { if !stream.header_parsed { // TODO(serialx): Send error message } quicStream.CloseReadSide() header := stream.header req := new(http.Request) req.Method = header.Get(":method") req.RequestURI = header.Get(":path") req.Proto = header.Get(":version") req.Header = header req.Host = header.Get(":host") // req.RemoteAddr = serverStream. TODO(serialx): Add remote addr rawPath := header.Get(":path") url, err := url.ParseRequestURI(rawPath) if err != nil { return // TODO(serialx): Send error message } url.Scheme = header.Get(":scheme") url.Host = header.Get(":host") req.URL = url // TODO(serialx): To buffered async read req.Body = ioutil.NopCloser(stream.buffer) go func() { w := &spdyResponseWriter{ serverStream: quicStream, spdyStream: stream, header: make(http.Header), sessionFnChan: stream.sessionFnChan, } if stream.server.Handler != nil { stream.server.Handler.ServeHTTP(w, req) } else { http.DefaultServeMux.ServeHTTP(w, req) } stream.sessionFnChan <- func() { if stream.closed { return } quicStream.WriteOrBufferData(make([]byte, 0), true) } }() }
func TestTransportConnectionCloseOnRequest(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() connSet, testDial := makeTestDial(t) for _, connectionClose := range []bool{false, true} { tr := &Transport{ Dial: testDial, } c := &http.Client{Transport: tr} fetch := func(n int) string { req := new(http.Request) var err error req.URL, err = url.Parse(ts.URL) if err != nil { t.Fatalf("URL parse error: %v", err) } req.Method = "GET" req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 req.Close = connectionClose res, err := c.Do(req) if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err) } body, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err) } return string(body) } body1 := fetch(1) body2 := fetch(2) bodiesDiffer := body1 != body2 if bodiesDiffer != connectionClose { t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", connectionClose, bodiesDiffer, body1, body2) } tr.CloseIdleConnections() } connSet.check(t) }
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { transport := p.Transport if transport == nil { transport = http.DefaultTransport } outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay p.Director(outreq) outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove the connection header to the backend. We want a // persistent connection, regardless of what the client sent // to us. This is modifying the same underlying map from req // (shallow copied above) so we only copy it if necessary. if outreq.Header.Get("Connection") != "" { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) outreq.Header.Del("Connection") } if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { // If we aren't the first proxy retain prior // X-Forwarded-For information as a comma+space // separated list and fold multiple headers into one. if prior, ok := outreq.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } outreq.Header.Set("X-Forwarded-For", clientIP) } res, err := transport.RoundTrip(outreq) if err != nil { log.Printf("http: proxy error: %v", err) rw.WriteHeader(http.StatusInternalServerError) return } defer res.Body.Close() copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) p.copyResponse(rw, res.Body) }
// Issue a request to an internal handler. This duplicates some logic found in // net.http.serve http://golang.org/src/net/http/server.go?#L1124 and // net.http.readRequest http://golang.org/src/net/http/server.go?#L func (t *WSTunnelClient) finishInternalRequest(id int16, req *http.Request) { log := t.Log.New("id", id, "verb", req.Method, "uri", req.RequestURI) log.Info("HTTP issuing internal request") // Remove hop-by-hop headers for _, h := range hopHeaders { req.Header.Del(h) } // Add fake protocol version req.Proto = "HTTP/1.0" req.ProtoMajor = 1 req.ProtoMinor = 0 // Dump the request into a buffer in case we want to log it dump, _ := httputil.DumpRequest(req, false) log.Debug("dump", "req", strings.Replace(string(dump), "\r\n", " || ", -1)) // Make sure we don't die if a panic occurs in the handler defer func() { if err := recover(); err != nil { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] log.Error("HTTP panic in handler", "err", err, "stack", string(buf)) } }() // Concoct Response rw := newResponseWriter(req) // Issue the request to the HTTP server t.InternalServer.ServeHTTP(rw, req) err := rw.finishResponse() if err != nil { //dump2, _ := httputil.DumpResponse(resp, true) //log15.Info("handleWsRequests: request error", "err", err.Error(), // "req", string(dump), "resp", string(dump2)) log.Info("HTTP request error", "err", err.Error()) writeResponseMessage(t, id, concoctResponse(req, err.Error(), 502)) return } log.Info("HTTP responded", "status", rw.resp.Status) writeResponseMessage(t, id, rw.resp) }
func relay(w http.ResponseWriter, req *http.Request) { defer func() { if err := recover(); err != nil { http.Error(w, fmt.Sprint(err), 200) } }() c, proxy, err := buildProxy() if err != nil { panic(err) } defer c.Close() _ = proxy outreq := new(http.Request) *outreq = *req // includes shallow copies of maps, but okay outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false // Remove the connection header to the backend. We want a // persistent connection, regardless of what the client sent // to us. This is modifying the same underlying map from req // (shallow copied above) so we only copy it if necessary. if outreq.Header.Get("Connection") != "" { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) outreq.Header.Del("Connection") } outreq.Write(c) br := bufio.NewReader(c) resp, err := http.ReadResponse(br, outreq) if err != nil { panic(err) } copyHeader(w.Header(), resp.Header) w.WriteHeader(resp.StatusCode) if resp.Body != nil { io.Copy(w, resp.Body) } }
func RequestFromMap(params map[string]string) (*http.Request, error) { r:= new (http.Request) r.Method =params["REQUEST_METHOD"] if r.Method ==""{ return nil ,errors.New("cgi:no REQUEST_METHOD in enviroment") } r.Proto = params["SERVER_PROTOCOL"] var ok bool r.ProtoMajor, r.ProtoMinor ,ok = http.ParseHTTPVersion(r.Proto) if !ok { return nil,errors.New("cgi:invalid SERVER_PROTOCOL version") } r.Close = true r.Trailer = http.Header{} r.Header = http.Header{} r.Host = params["HTTP_HOST"] if lenstr := params["CONTENT_LENGTH"]; lenstr != "" { clen ,err := strconv.ParseInt(lenstr, 10, 64) if err !=nil { return nil ,errors.New("cgi:bad CONTENT_LENTH in environment :"+ lenstr) } r.ContentLength = clen } if ct := params["CONTENT_TYPE"];ct != ""{ r.Header.Set("Content-Type", ct) } for k,v:=range params { if !strings.HasPrefix(k, "HTTP_"|| K == "HTTP_HOST"){ continue } r.Header.Add(strings.Replace(k[5:],"_","-", -1), v) } uriStr = params["SCRIPT_NAME"] +params["PATH_INFO"] s:=params["QUERY_STRING"] if s!=""{ uriStr +="?"+s } }
func (p *Proxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { node := p.lookup(req) if node == nil { rw.WriteHeader(http.StatusNotFound) return } p.increaseConn(node) defer p.decreaseConn(node) outreq := new(http.Request) *outreq = *req outreq.URL.Scheme = "http" outreq.URL.Host = node.Host outreq.Proto = "HTTP/1.1" outreq.ProtoMajor = 1 outreq.ProtoMinor = 1 outreq.Close = false if outreq.Header.Get("Connection") != "" { outreq.Header = make(http.Header) copyHeader(outreq.Header, req.Header) outreq.Header.Del("Connection") } if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { outreq.Header.Set("X-Forwarded-For", clientIp) } res, err := http.DefaultTransport.RoundTrip(outreq) if err != nil { log.Printf("proxy round trip error: %v", err) rw.WriteHeader(http.StatusInternalServerError) return } copyHeader(rw.Header(), res.Header) rw.WriteHeader(res.StatusCode) if res.Body != nil { var dst io.Writer = rw io.Copy(dst, res.Body) } }
// NewFastHTTPHandler wraps net/http handler to fasthttp request handler, // so it can be passed to fasthttp server. // // While this function may be used for easy switching from net/http to fasthttp, // it has the following drawbacks comparing to using manually written fasthttp // request handler: // // * A lot of useful functionality provided by fasthttp is missing // from net/http handler. // * net/http -> fasthttp handler conversion has some overhead, // so the returned handler will be always slower than manually written // fasthttp handler. // // So it is advisable using this function only for quick net/http -> fasthttp // switching. Then manually convert net/http handlers to fasthttp handlers // according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp . func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { var r http.Request body := ctx.PostBody() r.Method = string(ctx.Method()) r.Proto = "HTTP/1.1" r.ProtoMajor = 1 r.ProtoMinor = 1 r.RequestURI = string(ctx.RequestURI()) r.ContentLength = int64(len(body)) r.Host = string(ctx.Host()) r.RemoteAddr = ctx.RemoteAddr().String() hdr := make(http.Header) ctx.Request.Header.VisitAll(func(k, v []byte) { sk := string(k) sv := string(v) switch sk { case "Transfer-Encoding": r.TransferEncoding = append(r.TransferEncoding, sv) default: hdr.Set(sk, sv) } }) r.Header = hdr r.Body = &netHTTPBody{body} rURL, err := url.ParseRequestURI(r.RequestURI) if err != nil { ctx.Logger().Printf("cannot parse requestURI %q: %s", r.RequestURI, err) ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError) return } r.URL = rURL var w netHTTPResponseWriter h.ServeHTTP(&w, &r) ctx.SetStatusCode(w.StatusCode()) for k, vv := range w.Header() { for _, v := range vv { ctx.Response.Header.Set(k, v) } } ctx.Write(w.body) } }
func dialServer(c net.Conn, r *http.Request, addr string) { // "localhost" can resolve to both the IPv4 and IPv6 addresses. Since we still use IPv4, we specify the address manually. addr = "127.0.0.1" + addr fmt.Println("Dialing remote server", addr, "in response to request for", r.URL.Path) serverconn, e := net.Dial("tcp", addr) if serverconn != nil { defer serverconn.Close() } if e != nil { error503(c, r, e) return } // Change the http version of the request so that the server doesn't think we support keepalive. // It seems to be the only way i can get it to work. r.Proto = "HTTP/1.0" r.ProtoMinor = 0 r.Header.Del("Connection") fmt.Println("Writing data to server") r.Write(serverconn) serverconnbuf := bufio.NewReader(serverconn) fmt.Println("Reading response") resp, e := http.ReadResponse(serverconnbuf, r) if e != nil { error503(c, r, e) return } ct := resp.Header.Get("Content-Type") fmt.Println("Content-Type of response:", ct) if strings.Contains(ct, "text/xml") { resp.Header.Set("Content-Type", "text/html; charset=utf-8") } //resp.AddHeader("Connection", "close") //resp.ProtoMinor = 0 fmt.Println("Writing response") resp.Write(c) resp.Body.Close() fmt.Println("Done!") //c.Close() return //conn.Close() //io.Copy(conn, serverconn) //serverconn.Close() }
// NewFastHTTPVodkaAdaptor is responsible for adapting vodka requests through fasthttp interfaces to net/http requests. // // Based on valyala/fasthttp implementation. // Available here: https://github.com/valyala/fasthttp/blob/master/fasthttpadaptor/adaptor.go func NewFastHTTPVodkaAdaptor(h http.Handler) vodka.HandlerFunc { return func(c vodka.Context) error { var r http.Request ctx := c.Request().(*fasthttp.Request).RequestCtx body := ctx.PostBody() r.Method = string(ctx.Method()) r.Proto = "HTTP/1.1" r.ProtoMajor = 1 r.ProtoMinor = 1 r.RequestURI = string(ctx.RequestURI()) r.ContentLength = int64(len(body)) r.Host = string(ctx.Host()) r.RemoteAddr = ctx.RemoteAddr().String() hdr := make(http.Header) ctx.Request.Header.VisitAll(func(k, v []byte) { hdr.Set(string(k), string(v)) }) r.Header = hdr r.Body = &netHTTPBody{body} rURL, err := url.ParseRequestURI(r.RequestURI) if err != nil { ctx.Logger().Printf("cannot parse requestURI %q: %s", r.RequestURI, err) return fmt.Errorf("Internal Server Error") } r.URL = rURL var w netHTTPResponseWriter h.ServeHTTP(&w, &r) ctx.SetStatusCode(w.StatusCode()) for k, vv := range w.Header() { for _, v := range vv { c.Response().Header().Set(k, v) } } if strings.Contains(c.Response().Header().Get(vodka.HeaderContentType), textPlainContentType) { c.Response().Header().Set(vodka.HeaderContentType, http.DetectContentType(w.body)) } c.Response().Write(w.body) return nil } }