Copyright 2011 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
HTTP reverse proxy handler

package httputil

import (
	
	
	
	
	
	
	
	
	
	
	

	
)
ReverseProxy is an HTTP Handler that takes an incoming request and sends it to another server, proxying the response back to the client. ReverseProxy by default sets the client IP as the value of the X-Forwarded-For header. If an X-Forwarded-For header already exists, the client IP is appended to the existing values. As a special case, if the header exists in the Request.Header map but has a nil value (such as when set by the Director func), the X-Forwarded-For header is not modified. To prevent IP spoofing, be sure to delete any pre-existing X-Forwarded-For header coming from the client or an untrusted proxy.
Director must be a function which modifies the request into a new request to be sent using Transport. Its response is then copied back to the original client unmodified. Director must not access the provided Request after returning.
The transport used to perform proxy requests. If nil, http.DefaultTransport is used.
FlushInterval specifies the flush interval to flush to the client while copying the response body. If zero, no periodic flushing is done. A negative value means to flush immediately after each write to the client. The FlushInterval is ignored when ReverseProxy recognizes a response as a streaming response, or if its ContentLength is -1; for such responses, writes are flushed to the client immediately.
ErrorLog specifies an optional logger for errors that occur when attempting to proxy the request. If nil, logging is done via the log package's standard logger.
BufferPool optionally specifies a buffer pool to get byte slices for use by io.CopyBuffer when copying HTTP response bodies.
ModifyResponse is an optional function that modifies the Response from the backend. It is called if the backend returns a response at all, with any HTTP status code. If the backend is unreachable, the optional ErrorHandler is called without any call to ModifyResponse. If ModifyResponse returns an error, ErrorHandler is called with its error value. If ErrorHandler is nil, its default implementation is used.
ErrorHandler is an optional function that handles errors reaching the backend or errors from ModifyResponse. If nil, the default is to log the provided error and return a 502 Status Bad Gateway response.
A BufferPool is an interface for getting and returning temporary byte slices for use by io.CopyBuffer.
type BufferPool interface {
	Get() []byte
	Put([]byte)
}

func (,  string) string {
	 := strings.HasSuffix(, "/")
	 := strings.HasPrefix(, "/")
	switch {
	case  && :
		return  + [1:]
	case ! && !:
		return  + "/" + 
	}
	return  + 
}

func (,  *url.URL) (,  string) {
	if .RawPath == "" && .RawPath == "" {
		return singleJoiningSlash(.Path, .Path), ""
Same as singleJoiningSlash, but uses EscapedPath to determine whether a slash should be added
	 := .EscapedPath()
	 := .EscapedPath()

	 := strings.HasSuffix(, "/")
	 := strings.HasPrefix(, "/")

	switch {
	case  && :
		return .Path + .Path[1:],  + [1:]
	case ! && !:
		return .Path + "/" + .Path,  + "/" + 
	}
	return .Path + .Path,  + 
}
NewSingleHostReverseProxy returns a new ReverseProxy that routes URLs to the scheme, host, and base path provided in target. If the target's path is "/base" and the incoming request was for "/dir", the target request will be for /base/dir. NewSingleHostReverseProxy does not rewrite the Host header. To rewrite Host headers, use ReverseProxy directly with a custom Director policy.
func ( *url.URL) *ReverseProxy {
	 := .RawQuery
	 := func( *http.Request) {
		.URL.Scheme = .Scheme
		.URL.Host = .Host
		.URL.Path, .URL.RawPath = joinURLPath(, .URL)
		if  == "" || .URL.RawQuery == "" {
			.URL.RawQuery =  + .URL.RawQuery
		} else {
			.URL.RawQuery =  + "&" + .URL.RawQuery
		}
explicitly disable User-Agent so it's not set to default value
			.Header.Set("User-Agent", "")
		}
	}
	return &ReverseProxy{Director: }
}

func (,  http.Header) {
	for ,  := range  {
		for ,  := range  {
			.Add(, )
		}
	}
}
Hop-by-hop headers. These are removed when sent to the backend. As of RFC 7230, hop-by-hop headers are required to appear in the Connection header field. These are the headers defined by the obsoleted RFC 2616 (section 13.5.1) and are used for backward compatibility.
var hopHeaders = []string{
	"Connection",
	"Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te",      // canonicalized version of "TE"
	"Trailer", // not Trailers per URL above; https://www.rfc-editor.org/errata_search.php?eid=4522
	"Transfer-Encoding",
	"Upgrade",
}

func ( *ReverseProxy) ( http.ResponseWriter,  *http.Request,  error) {
	.logf("http: proxy error: %v", )
	.WriteHeader(http.StatusBadGateway)
}

func ( *ReverseProxy) () func(http.ResponseWriter, *http.Request, error) {
	if .ErrorHandler != nil {
		return .ErrorHandler
	}
	return .defaultErrorHandler
}
modifyResponse conditionally runs the optional ModifyResponse hook and reports whether the request should proceed.
func ( *ReverseProxy) ( http.ResponseWriter,  *http.Response,  *http.Request) bool {
	if .ModifyResponse == nil {
		return true
	}
	if  := .ModifyResponse();  != nil {
		.Body.Close()
		.getErrorHandler()(, , )
		return false
	}
	return true
}

func ( *ReverseProxy) ( http.ResponseWriter,  *http.Request) {
	 := .Transport
	if  == nil {
		 = http.DefaultTransport
	}

	 := .Context()
	if ,  := .(http.CloseNotifier);  {
		var  context.CancelFunc
		,  = context.WithCancel()
		defer ()
		 := .CloseNotify()
		go func() {
			select {
			case <-:
				()
			case <-.Done():
			}
		}()
	}

	 := .Clone()
	if .ContentLength == 0 {
		.Body = nil // Issue 16036: nil Body for http.Transport retries
	}
	if .Header == nil {
		.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate
	}

	.Director()
	.Close = false

	 := upgradeType(.Header)
	removeConnectionHeaders(.Header)
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.
	for ,  := range hopHeaders {
		 := .Header.Get()
		if  == "" {
			continue
		}
Issue 21096: tell backend applications that care about trailer support that we support trailers. (We do, but we don't go out of our way to advertise that unless the incoming client request thought it was worth mentioning)
			continue
		}
		.Header.Del()
	}
After stripping all the hop-by-hop connection headers above, add back any necessary for protocol upgrades, such as for websockets.
	if  != "" {
		.Header.Set("Connection", "Upgrade")
		.Header.Set("Upgrade", )
	}

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.
		,  := .Header["X-Forwarded-For"]
		 :=  &&  == nil // Issue 38079: nil now means don't populate the header
		if len() > 0 {
			 = strings.Join(, ", ") + ", " + 
		}
		if ! {
			.Header.Set("X-Forwarded-For", )
		}
	}

	,  := .RoundTrip()
	if  != nil {
		.getErrorHandler()(, , )
		return
	}
Deal with 101 Switching Protocols responses: (WebSocket, h2c, etc)
	if .StatusCode == http.StatusSwitchingProtocols {
		if !.modifyResponse(, , ) {
			return
		}
		.handleUpgradeResponse(, , )
		return
	}

	removeConnectionHeaders(.Header)

	for ,  := range hopHeaders {
		.Header.Del()
	}

	if !.modifyResponse(, , ) {
		return
	}

	copyHeader(.Header(), .Header)
The "Trailer" header isn't included in the Transport's response, at least for *http.Transport. Build it up from Trailer.
	 := len(.Trailer)
	if  > 0 {
		 := make([]string, 0, len(.Trailer))
		for  := range .Trailer {
			 = append(, )
		}
		.Header().Add("Trailer", strings.Join(, ", "))
	}

	.WriteHeader(.StatusCode)

	 = .copyResponse(, .Body, .flushInterval())
	if  != nil {
Since we're streaming the response, if we run into an error all we can do is abort the request. Issue 23643: ReverseProxy should use ErrAbortHandler on read error while copying body.
		if !shouldPanicOnCopyError() {
			.logf("suppressing panic for copyResponse error in test; copy error: %v", )
			return
		}
		panic(http.ErrAbortHandler)
	}
	.Body.Close() // close now, instead of defer, to populate res.Trailer

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 ,  := .(http.Flusher);  {
			.Flush()
		}
	}

	if len(.Trailer) ==  {
		copyHeader(.Header(), .Trailer)
		return
	}

	for ,  := range .Trailer {
		 = http.TrailerPrefix + 
		for ,  := range  {
			.Header().Add(, )
		}
	}
}

var inOurTests bool // whether we're in our own tests
shouldPanicOnCopyError reports whether the reverse proxy should panic with http.ErrAbortHandler. This is the right thing to do by default, but Go 1.10 and earlier did not, so existing unit tests weren't expecting panics. Only panic in our own tests, or when running under the HTTP server.
Our tests know to handle this panic.
		return true
	}
We seem to be running under an HTTP server, so it'll recover the panic.
		return true
Otherwise act like Go 1.10 and earlier to not break existing tests.
	return false
}
removeConnectionHeaders removes hop-by-hop headers listed in the "Connection" header of h. See RFC 7230, section 6.1
func ( http.Header) {
	for ,  := range ["Connection"] {
		for ,  := range strings.Split(, ",") {
			if  = textproto.TrimString();  != "" {
				.Del()
			}
		}
	}
}
flushInterval returns the p.FlushInterval value, conditionally overriding its value for a specific request/response.
func ( *ReverseProxy) ( *http.Response) time.Duration {
	 := .Header.Get("Content-Type")
For Server-Sent Events responses, flush immediately. The MIME type is defined in https://www.w3.org/TR/eventsource/#text-event-stream
	if  == "text/event-stream" {
		return -1 // negative means immediately
	}
We might have the case of streaming for which Content-Length might be unset.
	if .ContentLength == -1 {
		return -1
	}

	return .FlushInterval
}

func ( *ReverseProxy) ( io.Writer,  io.Reader,  time.Duration) error {
	if  != 0 {
		if ,  := .(writeFlusher);  {
			 := &maxLatencyWriter{
				dst:     ,
				latency: ,
			}
			defer .stop()
set up initial timer so headers get flushed even if body writes are delayed
			.flushPending = true
			.t = time.AfterFunc(, .delayedFlush)

			 = 
		}
	}

	var  []byte
	if .BufferPool != nil {
		 = .BufferPool.Get()
		defer .BufferPool.Put()
	}
	,  := .copyBuffer(, , )
	return 
}
copyBuffer returns any write errors or non-EOF read errors, and the amount of bytes written.
func ( *ReverseProxy) ( io.Writer,  io.Reader,  []byte) (int64, error) {
	if len() == 0 {
		 = make([]byte, 32*1024)
	}
	var  int64
	for {
		,  := .Read()
		if  != nil &&  != io.EOF &&  != context.Canceled {
			.logf("httputil: ReverseProxy read error during body copy: %v", )
		}
		if  > 0 {
			,  := .Write([:])
			if  > 0 {
				 += int64()
			}
			if  != nil {
				return , 
			}
			if  !=  {
				return , io.ErrShortWrite
			}
		}
		if  != nil {
			if  == io.EOF {
				 = nil
			}
			return , 
		}
	}
}

func ( *ReverseProxy) ( string,  ...interface{}) {
	if .ErrorLog != nil {
		.ErrorLog.Printf(, ...)
	} else {
		log.Printf(, ...)
	}
}

type writeFlusher interface {
	io.Writer
	http.Flusher
}

type maxLatencyWriter struct {
	dst     writeFlusher
	latency time.Duration // non-zero; negative means to flush immediately

	mu           sync.Mutex // protects t, flushPending, and dst.Flush
	t            *time.Timer
	flushPending bool
}

func ( *maxLatencyWriter) ( []byte) ( int,  error) {
	.mu.Lock()
	defer .mu.Unlock()
	,  = .dst.Write()
	if .latency < 0 {
		.dst.Flush()
		return
	}
	if .flushPending {
		return
	}
	if .t == nil {
		.t = time.AfterFunc(.latency, .delayedFlush)
	} else {
		.t.Reset(.latency)
	}
	.flushPending = true
	return
}

func ( *maxLatencyWriter) () {
	.mu.Lock()
	defer .mu.Unlock()
	if !.flushPending { // if stop was called but AfterFunc already started this goroutine
		return
	}
	.dst.Flush()
	.flushPending = false
}

func ( *maxLatencyWriter) () {
	.mu.Lock()
	defer .mu.Unlock()
	.flushPending = false
	if .t != nil {
		.t.Stop()
	}
}

func ( http.Header) string {
	if !httpguts.HeaderValuesContainsToken(["Connection"], "Upgrade") {
		return ""
	}
	return strings.ToLower(.Get("Upgrade"))
}

func ( *ReverseProxy) ( http.ResponseWriter,  *http.Request,  *http.Response) {
	 := upgradeType(.Header)
	 := upgradeType(.Header)
	if  !=  {
		.getErrorHandler()(, , fmt.Errorf("backend tried to switch protocol %q when %q was requested", , ))
		return
	}

	,  := .(http.Hijacker)
	if ! {
		.getErrorHandler()(, , fmt.Errorf("can't switch protocols using non-Hijacker ResponseWriter type %T", ))
		return
	}
	,  := .Body.(io.ReadWriteCloser)
	if ! {
		.getErrorHandler()(, , fmt.Errorf("internal error: 101 switching protocols response with non-writable body"))
		return
	}

	 := make(chan bool)
Ensure that the cancelation of a request closes the backend. See issue https://golang.org/issue/35559.
		select {
		case <-.Context().Done():
		case <-:
		}
		.Close()
	}()

	defer close()

	, ,  := .Hijack()
	if  != nil {
		.getErrorHandler()(, , fmt.Errorf("Hijack failed on protocol switch: %v", ))
		return
	}
	defer .Close()

	copyHeader(.Header(), .Header)

	.Header = .Header()
	.Body = nil // so res.Write only writes the headers; we have res.Body in backConn above
	if  := .Write();  != nil {
		.getErrorHandler()(, , fmt.Errorf("response write: %v", ))
		return
	}
	if  := .Flush();  != nil {
		.getErrorHandler()(, , fmt.Errorf("response flush: %v", ))
		return
	}
	 := make(chan error, 1)
	 := switchProtocolCopier{user: , backend: }
	go .copyToBackend()
	go .copyFromBackend()
	<-
	return
}
switchProtocolCopier exists so goroutines proxying data back and forth have nice names in stacks.
type switchProtocolCopier struct {
	user, backend io.ReadWriter
}

func ( switchProtocolCopier) ( chan<- error) {
	,  := io.Copy(.user, .backend)
	 <- 
}

func ( switchProtocolCopier) ( chan<- error) {
	,  := io.Copy(.backend, .user)
	 <-