Beispiel #1
0
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)
		}
	}
}
Beispiel #2
0
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()
		}
	}
}
Beispiel #3
0
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
}
Beispiel #4
0
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)
}