func (s *CountersCollectingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if s.RateLimiter != nil { if !s.RateLimiter.Accepted() { w.WriteHeader(http.StatusServiceUnavailable) return } } if s.Limiter != nil { ctx := context.GetRequestContext(req) s.Limiter.Acquire(ctx.Tr) defer s.Limiter.Release(ctx.Tr) } s.stats.in() s.Handler.ServeHTTP(w, req) s.stats.out() if wr, ok := w.(*StatsCollectingResponseWriter); ok { respBucket := wr.ResponseCode / 100 if respBucket == 0 { respBucket = 2 } if respBucket > 0 && respBucket <= 5 { atomic.AddInt64(&s.stats.CountersByResponseCode[respBucket], 1) } } }
func (b *Backend) ServeHTTP(w http.ResponseWriter, r *http.Request) { tr := trace.New("backend."+b.Cf.Name, r.RequestURI) tr.LazyPrintf("Request: %#v", r) defer tr.Finish() glog.V(3).Infof("Backend %s serving %s %s", b.Cf.Name, r.Host) ctx := context.GetRequestContext(r) ctx.Log.BackendName = b.Cf.Name ctx.Tr.LazyPrintf("using backend %s", b.Cf.Name) defer ctx.Tr.LazyPrintf("backend done") b.proxy.ServeHTTP(w, r) if wr, ok := w.(*stats.StatsCollectingResponseWriter); ok { tr.LazyPrintf("Response %d", wr.ResponseCode) tr.LazyPrintf("Response headers %v", wr.Header()) if wr.IsErrorResponse() { tr.SetError() } } }
func (b *Balancer) RoundTrip(r *http.Request) (*http.Response, error) { ctx := context.GetRequestContext(r) tr := ctx.Tr starttime := time.Now().UnixNano() tr.LazyPrintf("balancer") defer tr.LazyPrintf("balancer done") idx := atomic.AddInt64(&b.idx, 1) glog.V(3).Infof("Balancer serving %v using %d", r.URL, idx%int64(len(b.handlers))) glog.V(3).Infof("Request %v", r) b.mux.Lock() if len(b.activeHandlers) == 0 { b.mux.Unlock() tr.LazyPrintf("No healthy backend server available") tr.SetError() return nil, NoHealthyBackendAvailable } h := b.activeHandlers[idx%int64(len(b.activeHandlers))] b.mux.Unlock() //TODO: handle error and redispatch to another server resp, err := h.RoundTrip(r) glog.V(3).Infof("Response %v", resp) ctx.Log.ServerLatencyNs = time.Now().UnixNano() - starttime return resp, err }
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 context.LinkContext(req, outreq) defer context.Clear(outreq) if closeNotifier, ok := rw.(http.CloseNotifier); ok { if requestCanceler, ok := transport.(requestCanceler); ok { reqDone := make(chan struct{}) defer close(reqDone) clientGone := closeNotifier.CloseNotify() outreq.Body = struct { io.Reader io.Closer }{ Reader: &runOnFirstRead{ Reader: outreq.Body, fn: func() { go func() { select { case <-clientGone: requestCanceler.CancelRequest(outreq) case <-reqDone: } }() }, }, Closer: outreq.Body, } } } 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 { glog.Infof("http: proxy error: %v", err) ctx := context.GetRequestContext(req) if ctx != nil && ctx.Tr != nil { ctx.Tr.LazyPrintf("http: proxy error: %v", err) } rw.WriteHeader(http.StatusInternalServerError) return } for _, h := range hopHeaders { res.Header.Del(h) } copyHeader(rw.Header(), res.Header) // The "Trailer" header isn't included in the Transport's response, // at least for *http.Transport. Build it up from Trailer. if len(res.Trailer) > 0 { var trailerKeys []string for k := range res.Trailer { trailerKeys = append(trailerKeys, k) } rw.Header().Add("Trailer", strings.Join(trailerKeys, ", ")) } rw.WriteHeader(res.StatusCode) if len(res.Trailer) > 0 { // Force chunking if we saw a response trailer. // This prevents net/http from calculating the length for short // bodies and adding a Content-Length. if fl, ok := rw.(http.Flusher); ok { fl.Flush() } } p.copyResponse(rw, res.Body) res.Body.Close() // close now, instead of defer, to populate res.Trailer copyHeader(rw.Header(), res.Trailer) }