func NewHTTPReverseProxy()

in pkg/util/vhost/http.go [51:120]


func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *HTTPReverseProxy {
	if option.ResponseHeaderTimeoutS <= 0 {
		option.ResponseHeaderTimeoutS = 60
	}
	rp := &HTTPReverseProxy{
		responseHeaderTimeout: time.Duration(option.ResponseHeaderTimeoutS) * time.Second,
		vhostRouter:           vhostRouter,
	}
	proxy := &httputil.ReverseProxy{
		// Modify incoming requests by route policies.
		Director: func(req *http.Request) {
			req.URL.Scheme = "http"
			url := req.Context().Value(RouteInfoURL).(string)
			routeByHTTPUser := req.Context().Value(RouteInfoHTTPUser).(string)
			oldHost, _ := util.CanonicalHost(req.Context().Value(RouteInfoHost).(string))
			rc := rp.GetRouteConfig(oldHost, url, routeByHTTPUser)
			if rc != nil {
				if rc.RewriteHost != "" {
					req.Host = rc.RewriteHost
				}
				// Set {domain}.{location}.{routeByHTTPUser} as URL host here to let http transport reuse connections.
				// TODO(fatedier): use proxy name instead?
				req.URL.Host = rc.Domain + "." +
					base64.StdEncoding.EncodeToString([]byte(rc.Location)) + "." +
					base64.StdEncoding.EncodeToString([]byte(rc.RouteByHTTPUser))

				for k, v := range rc.Headers {
					req.Header.Set(k, v)
				}
			} else {
				req.URL.Host = req.Host
			}
		},
		// Create a connection to one proxy routed by route policy.
		Transport: &http.Transport{
			ResponseHeaderTimeout: rp.responseHeaderTimeout,
			IdleConnTimeout:       60 * time.Second,
			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
				url := ctx.Value(RouteInfoURL).(string)
				host, _ := util.CanonicalHost(ctx.Value(RouteInfoHost).(string))
				routerByHTTPUser := ctx.Value(RouteInfoHTTPUser).(string)
				remote := ctx.Value(RouteInfoRemote).(string)
				return rp.CreateConnection(host, url, routerByHTTPUser, remote)
			},
			Proxy: func(req *http.Request) (*url.URL, error) {
				// Use proxy mode if there is host in HTTP first request line.
				// GET http://example.com/ HTTP/1.1
				// Host: example.com
				//
				// Normal:
				// GET / HTTP/1.1
				// Host: example.com
				urlHost := req.Context().Value(RouteInfoURLHost).(string)
				if urlHost != "" {
					return req.URL, nil
				}
				return nil, nil
			},
		},
		BufferPool: newWrapPool(),
		ErrorLog:   log.New(newWrapLogger(), "", 0),
		ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
			frpLog.Warn("do http proxy request [host: %s] error: %v", req.Host, err)
			rw.WriteHeader(http.StatusNotFound)
			_, _ = rw.Write(getNotFoundPageContent())
		},
	}
	rp.proxy = proxy
	return rp
}