// Round trips the request to one of the endpoints and returns the response. func (l *HttpLocation) RoundTrip(req request.Request) (*http.Response, error) { // Get options and transport as one single read transaction. // Options and transport may change if someone calls SetOptions o, tr := l.GetOptionsAndTransport() originalRequest := req.GetHttpRequest() // Check request size first, if that exceeds the limit, we don't bother reading the request. if l.isRequestOverLimit(req) { return nil, errors.FromStatus(http.StatusRequestEntityTooLarge) } // Read the body while keeping this location's limits in mind. This reader controls the maximum bytes // to read into memory and disk. This reader returns an error if the total request size exceeds the // prefefined MaxSizeBytes. This can occur if we got chunked request, in this case ContentLength would be set to -1 // and the reader would be unbounded bufio in the http.Server body, err := netutils.NewBodyBufferWithOptions(originalRequest.Body, netutils.BodyBufferOptions{ MemBufferBytes: o.Limits.MaxMemBodyBytes, MaxSizeBytes: o.Limits.MaxBodyBytes, }) if err != nil { return nil, err } if body == nil { return nil, fmt.Errorf("Empty body") } // Set request body to buffered reader that can replay the read and execute Seek req.SetBody(body) // Note that we don't change the original request Body as it's handled by the http server defer body.Close() for { _, err := req.GetBody().Seek(0, 0) if err != nil { return nil, err } endpoint, err := l.loadBalancer.NextEndpoint(req) if err != nil { log.Errorf("Load Balancer failure: %s", err) return nil, err } // Adds headers, changes urls. Note that we rewrite request each time we proxy it to the // endpoint, so that each try gets a fresh start req.SetHttpRequest(l.copyRequest(originalRequest, req.GetBody(), endpoint)) // In case if error is not nil, we allow load balancer to choose the next endpoint // e.g. to do request failover. Nil error means that we got proxied the request successfully. response, err := l.proxyToEndpoint(tr, &o, endpoint, req) if o.FailoverPredicate(req) { continue } else { return response, err } } log.Errorf("All endpoints failed!") return nil, fmt.Errorf("All endpoints failed") }
// readResponse reads the response body into buffer, closes the original response body to release the connection // and replaces the body with the buffer. func readResponse(l *Limits, re *http.Response, err error) (*http.Response, error) { if re == nil || re.Body == nil { return nil, err } // Closing a response body releases connection to the pool in transport body := re.Body defer body.Close() newBody, err := netutils.NewBodyBufferWithOptions(body, netutils.BodyBufferOptions{ MemBufferBytes: l.MaxMemBodyBytes, MaxSizeBytes: l.MaxBodyBytes, }) if err != nil { return nil, err } re.Body = newBody return re, nil }