func fail(sc *httputil.ServerConn, req *http.Request, code int, msg string) { resp := &http.Response{ StatusCode: code, ProtoMajor: 1, ProtoMinor: 0, Request: req, Body: ioutil.NopCloser(bytes.NewBufferString(msg)), ContentLength: int64(len(msg)), } sc.Write(req, resp) }
func (s *httpService) handle(req *http.Request, sc *httputil.ServerConn, tls, sticky bool) { for { req.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)) var backend *httputil.ClientConn var stickyCookie *http.Cookie if sticky { backend, stickyCookie = s.getBackendSticky(req) } else { backend = s.getBackend() } if backend == nil { log.Println("no backend found") fail(sc, req, 503, "Service Unavailable") return } if req.Method != "GET" && req.Method != "POST" && req.Method != "HEAD" && req.Method != "OPTIONS" && req.Method != "PUT" && req.Method != "DELETE" && req.Method != "TRACE" { fail(sc, req, 405, "Method not allowed") return } req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 delete(req.Header, "Te") delete(req.Header, "Transfer-Encoding") 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 := req.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } req.Header.Set("X-Forwarded-For", clientIP) } if tls { req.Header.Set("X-Forwarded-Proto", "https") } else { req.Header.Set("X-Forwarded-Proto", "http") } // TODO: Set X-Forwarded-Port if err := backend.Write(req); err != nil { log.Println("server write err:", err) return } res, err := backend.Read(req) if res != nil { if stickyCookie != nil { res.Header.Add("Set-Cookie", stickyCookie.String()) } if res.StatusCode == http.StatusSwitchingProtocols { res.Body = nil } if err := sc.Write(req, res); err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("client write err:", err) // TODO: log error } return } } if err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("server read err:", err) // TODO: log error fail(sc, req, 502, "Bad Gateway") } return } // TODO: Proxy HTTP CONNECT? (example: Go RPC over HTTP) if res.StatusCode == http.StatusSwitchingProtocols { serverW, serverR := backend.Hijack() clientW, clientR := sc.Hijack() defer serverW.Close() done := make(chan struct{}) go func() { serverR.WriteTo(clientW) if cw, ok := clientW.(writeCloser); ok { cw.CloseWrite() } close(done) }() clientR.WriteTo(serverW) serverW.(writeCloser).CloseWrite() <-done return } // close the backend connection, so we don't accidently send to // a closed socket on the backend backend.Close() // TODO: http pipelining req, err = sc.Read() if err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("client read err:", err) } return } } }
func (s *httpService) handle(req *http.Request, sc *httputil.ServerConn, tls, sticky bool) (done bool) { req.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)) req.Header.Set("X-Request-Id", random.UUID()) var backend *httputil.ClientConn var stickyCookie *http.Cookie if sticky { backend, stickyCookie = s.getBackendSticky(req) } else { backend = s.getBackend() } if backend == nil { log.Println("no backend found") fail(sc, req, 503, "Service Unavailable") return } defer backend.Close() req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 delete(req.Header, "Te") delete(req.Header, "Transfer-Encoding") 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 := req.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } req.Header.Set("X-Forwarded-For", clientIP) } if tls { req.Header.Set("X-Forwarded-Proto", "https") } else { req.Header.Set("X-Forwarded-Proto", "http") } // TODO: Set X-Forwarded-Port // Pass the Request-URI verbatim without any modifications req.URL.Opaque = strings.Split(strings.TrimPrefix(req.RequestURI, req.URL.Scheme+":"), "?")[0] if err := backend.Write(req); err != nil { log.Println("server write err:", err) // TODO: return error to client here return true } res, err := backend.Read(req) if res != nil { if stickyCookie != nil { res.Header.Add("Set-Cookie", stickyCookie.String()) } if res.StatusCode == http.StatusSwitchingProtocols { res.Body = nil } if err := sc.Write(req, res); err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("client write err:", err) // TODO: log error } return true } } if err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("server read err:", err) // TODO: log error fail(sc, req, 502, "Bad Gateway") } return } // TODO: Proxy HTTP CONNECT? (example: Go RPC over HTTP) if res.StatusCode == http.StatusSwitchingProtocols { serverW, serverR := backend.Hijack() clientW, clientR := sc.Hijack() defer serverW.Close() done := make(chan struct{}) go func() { serverR.WriteTo(clientW) if cw, ok := clientW.(writeCloser); ok { cw.CloseWrite() } close(done) }() clientR.WriteTo(serverW) serverW.(writeCloser).CloseWrite() <-done return true } return }
func (s *httpServer) handle(req *http.Request, sc *httputil.ServerConn, tls bool) { req.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)) backend := s.getBackend() if backend == nil { // TODO: Return 503 log.Println("no backend found") return } defer backend.Close() for { if req.Method != "GET" && req.Method != "POST" && req.Method != "HEAD" && req.Method != "OPTIONS" && req.Method != "PUT" && req.Method != "DELETE" && req.Method != "TRACE" { // TODO: return 405 } req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 delete(req.Header, "Te") delete(req.Header, "Transfer-Encoding") 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 := req.Header["X-Forwarded-For"]; ok { clientIP = strings.Join(prior, ", ") + ", " + clientIP } req.Header.Set("X-Forwarded-For", clientIP) } if tls { req.Header.Set("X-Forwarded-Proto", "https") } else { req.Header.Set("X-Forwarded-Proto", "http") } // TODO: Set X-Forwarded-Port if err := backend.Write(req); err != nil { log.Println("server write err:", err) return } res, err := backend.Read(req) if res != nil { if err := sc.Write(req, res); err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("client write err:", err) // TODO: log error } return } } if err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("server read err:", err) // TODO: log error // TODO: Return 502 } return } // TODO: Proxy HTTP CONNECT? (example: Go RPC over HTTP) if res.StatusCode == http.StatusSwitchingProtocols { serverW, serverR := backend.Hijack() clientW, clientR := sc.Hijack() defer serverW.Close() done := make(chan struct{}) go func() { serverR.WriteTo(clientW) close(done) }() clientR.WriteTo(serverW) <-done return } // TODO: http pipelining req, err = sc.Read() if err != nil { if err != io.EOF && err != httputil.ErrPersistEOF { log.Println("client read err:", err) } return } req.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)) } }