// 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.
type ReverseProxy struct {
	// 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.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// 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.
	FlushInterval time.Duration

	// 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.
	ErrorLog *log.Logger

	// BufferPool optionally specifies a buffer pool to
	// get byte slices for use by io.CopyBuffer when
	// copying HTTP response bodies.
	BufferPool BufferPool

	// 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.
	ModifyResponse func(*http.Response) error

	// 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.
	ErrorHandler func(http.ResponseWriter, *http.Request, error)
}

// 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 singleJoiningSlash(,  string) string {
	 := strings.HasSuffix(, "/")
	 := strings.HasPrefix(, "/")
	switch {
	case  && :
		return  + [1:]
	case ! && !:
		return  + "/" + 
	}
	return  + 
}

func joinURLPath(,  *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
		}
		if ,  := .Header["User-Agent"]; ! {
			// explicitly disable User-Agent so it's not set to default value
			.Header.Set("User-Agent", "")
		}
	}
	return &ReverseProxy{Director: }
}

func copyHeader(,  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
		}
		if  == "Te" &&  == "trailers" {
			// 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 , ,  := net.SplitHostPort(.RemoteAddr);  == 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.
		,  := .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 {
		defer .Body.Close()
		// 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

	if len(.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 ,  := .(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.
func shouldPanicOnCopyError( *http.Request) bool {
	if inOurTests {
		// Our tests know to handle this panic.
		return true
	}
	if .Context().Value(http.ServerContextKey) != nil {
		// 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 removeConnectionHeaders( 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 upgradeType( 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)
	go func() {
		// 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)
	 <- 
}