mirror of https://github.com/fatedier/frp
				
				
				
			support responseHeaders.set for proxy type http (#4192)
							parent
							
								
									d0d396becb
								
							
						
					
					
						commit
						e81b36c5ba
					
				| 
						 | 
				
			
			@ -983,7 +983,7 @@ The HTTP request will have the `Host` header rewritten to `Host: dev.example.com
 | 
			
		|||
 | 
			
		||||
### Setting other HTTP Headers
 | 
			
		||||
 | 
			
		||||
Similar to `Host`, You can override other HTTP request headers with proxy type `http`.
 | 
			
		||||
Similar to `Host`, You can override other HTTP request and response headers with proxy type `http`.
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
# frpc.toml
 | 
			
		||||
| 
						 | 
				
			
			@ -995,9 +995,10 @@ localPort = 80
 | 
			
		|||
customDomains = ["test.example.com"]
 | 
			
		||||
hostHeaderRewrite = "dev.example.com"
 | 
			
		||||
requestHeaders.set.x-from-where = "frp"
 | 
			
		||||
responseHeaders.set.foo = "bar"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In this example, it will set header `x-from-where: frp` in the HTTP request.
 | 
			
		||||
In this example, it will set header `x-from-where: frp` in the HTTP request and `foo: bar` in the HTTP response.
 | 
			
		||||
 | 
			
		||||
### Get Real IP
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ When connecting to frps versions older than v0.39.0 might encounter compatibilit
 | 
			
		|||
### Features
 | 
			
		||||
 | 
			
		||||
* Show tcpmux proxies on the frps dashboard.
 | 
			
		||||
* `http` proxy can modify the response header. For example, `responseHeaders.set.foo = "bar"` will add a new header `foo: bar` to the response.
 | 
			
		||||
 | 
			
		||||
### Fixes
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -209,6 +209,7 @@ locations = ["/", "/pic"]
 | 
			
		|||
# routeByHTTPUser = abc
 | 
			
		||||
hostHeaderRewrite = "example.com"
 | 
			
		||||
requestHeaders.set.x-from-where = "frp"
 | 
			
		||||
responseHeaders.set.foo = "bar"
 | 
			
		||||
healthCheck.type = "http"
 | 
			
		||||
# frpc will send a GET http request '/status' to local http service
 | 
			
		||||
# http service is alive when it return 2xx http response code
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -291,6 +291,7 @@ type HTTPProxyConfig struct {
 | 
			
		|||
	HTTPPassword      string           `json:"httpPassword,omitempty"`
 | 
			
		||||
	HostHeaderRewrite string           `json:"hostHeaderRewrite,omitempty"`
 | 
			
		||||
	RequestHeaders    HeaderOperations `json:"requestHeaders,omitempty"`
 | 
			
		||||
	ResponseHeaders   HeaderOperations `json:"responseHeaders,omitempty"`
 | 
			
		||||
	RouteByHTTPUser   string           `json:"routeByHTTPUser,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -304,6 +305,7 @@ func (c *HTTPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
 | 
			
		|||
	m.HTTPUser = c.HTTPUser
 | 
			
		||||
	m.HTTPPwd = c.HTTPPassword
 | 
			
		||||
	m.Headers = c.RequestHeaders.Set
 | 
			
		||||
	m.ResponseHeaders = c.ResponseHeaders.Set
 | 
			
		||||
	m.RouteByHTTPUser = c.RouteByHTTPUser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -317,6 +319,7 @@ func (c *HTTPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
 | 
			
		|||
	c.HTTPUser = m.HTTPUser
 | 
			
		||||
	c.HTTPPassword = m.HTTPPwd
 | 
			
		||||
	c.RequestHeaders.Set = m.Headers
 | 
			
		||||
	c.ResponseHeaders.Set = m.ResponseHeaders
 | 
			
		||||
	c.RouteByHTTPUser = m.RouteByHTTPUser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -121,6 +121,7 @@ type NewProxy struct {
 | 
			
		|||
	HTTPPwd           string            `json:"http_pwd,omitempty"`
 | 
			
		||||
	HostHeaderRewrite string            `json:"host_header_rewrite,omitempty"`
 | 
			
		||||
	Headers           map[string]string `json:"headers,omitempty"`
 | 
			
		||||
	ResponseHeaders   map[string]string `json:"response_headers,omitempty"`
 | 
			
		||||
	RouteByHTTPUser   string            `json:"route_by_http_user,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// stcp, sudp, xtcp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,9 +63,9 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
 | 
			
		|||
			req := r.Out
 | 
			
		||||
			req.URL.Scheme = "http"
 | 
			
		||||
			reqRouteInfo := req.Context().Value(RouteInfoKey).(*RequestRouteInfo)
 | 
			
		||||
			oldHost, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
 | 
			
		||||
			originalHost, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
 | 
			
		||||
 | 
			
		||||
			rc := rp.GetRouteConfig(oldHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
 | 
			
		||||
			rc := req.Context().Value(RouteConfigKey).(*RouteConfig)
 | 
			
		||||
			if rc != nil {
 | 
			
		||||
				if rc.RewriteHost != "" {
 | 
			
		||||
					req.Host = rc.RewriteHost
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
 | 
			
		|||
					endpoint, _ = rc.ChooseEndpointFn()
 | 
			
		||||
					reqRouteInfo.Endpoint = endpoint
 | 
			
		||||
					log.Tracef("choose endpoint name [%s] for http request host [%s] path [%s] httpuser [%s]",
 | 
			
		||||
						endpoint, oldHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
 | 
			
		||||
						endpoint, originalHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
 | 
			
		||||
				}
 | 
			
		||||
				// Set {domain}.{location}.{routeByHTTPUser}.{endpoint} as URL host here to let http transport reuse connections.
 | 
			
		||||
				req.URL.Host = rc.Domain + "." +
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +92,15 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
 | 
			
		|||
				req.URL.Host = req.Host
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		ModifyResponse: func(r *http.Response) error {
 | 
			
		||||
			rc := r.Request.Context().Value(RouteConfigKey).(*RouteConfig)
 | 
			
		||||
			if rc != nil {
 | 
			
		||||
				for k, v := range rc.ResponseHeaders {
 | 
			
		||||
					r.Header.Set(k, v)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
		// Create a connection to one proxy routed by route policy.
 | 
			
		||||
		Transport: &http.Transport{
 | 
			
		||||
			ResponseHeaderTimeout: rp.responseHeaderTimeout,
 | 
			
		||||
| 
						 | 
				
			
			@ -157,14 +166,6 @@ func (rp *HTTPReverseProxy) GetRouteConfig(domain, location, routeByHTTPUser str
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rp *HTTPReverseProxy) GetHeaders(domain, location, routeByHTTPUser string) (headers map[string]string) {
 | 
			
		||||
	vr, ok := rp.getVhost(domain, location, routeByHTTPUser)
 | 
			
		||||
	if ok {
 | 
			
		||||
		headers = vr.payload.(*RouteConfig).Headers
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateConnection create a new connection by route config
 | 
			
		||||
func (rp *HTTPReverseProxy) CreateConnection(reqRouteInfo *RequestRouteInfo, byEndpoint bool) (net.Conn, error) {
 | 
			
		||||
	host, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
 | 
			
		||||
| 
						 | 
				
			
			@ -305,8 +306,13 @@ func (rp *HTTPReverseProxy) injectRequestInfoToCtx(req *http.Request) *http.Requ
 | 
			
		|||
		RemoteAddr: req.RemoteAddr,
 | 
			
		||||
		URLHost:    req.URL.Host,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	originalHost, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
 | 
			
		||||
	rc := rp.GetRouteConfig(originalHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
 | 
			
		||||
 | 
			
		||||
	newctx := req.Context()
 | 
			
		||||
	newctx = context.WithValue(newctx, RouteInfoKey, reqRouteInfo)
 | 
			
		||||
	newctx = context.WithValue(newctx, RouteConfigKey, rc)
 | 
			
		||||
	return req.Clone(newctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,8 @@ import (
 | 
			
		|||
type RouteInfo string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	RouteInfoKey RouteInfo = "routeInfo"
 | 
			
		||||
	RouteInfoKey   RouteInfo = "routeInfo"
 | 
			
		||||
	RouteConfigKey RouteInfo = "routeConfig"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RequestRouteInfo struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +114,7 @@ type RouteConfig struct {
 | 
			
		|||
	Username        string
 | 
			
		||||
	Password        string
 | 
			
		||||
	Headers         map[string]string
 | 
			
		||||
	ResponseHeaders map[string]string
 | 
			
		||||
	RouteByHTTPUser string
 | 
			
		||||
 | 
			
		||||
	CreateConnFn           CreateConnFunc
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,7 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
 | 
			
		|||
		RewriteHost:     pxy.cfg.HostHeaderRewrite,
 | 
			
		||||
		RouteByHTTPUser: pxy.cfg.RouteByHTTPUser,
 | 
			
		||||
		Headers:         pxy.cfg.RequestHeaders.Set,
 | 
			
		||||
		ResponseHeaders: pxy.cfg.ResponseHeaders.Set,
 | 
			
		||||
		Username:        pxy.cfg.HTTPUser,
 | 
			
		||||
		Password:        pxy.cfg.HTTPPassword,
 | 
			
		||||
		CreateConnFn:    pxy.GetRealConn,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -267,7 +267,7 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
 | 
			
		|||
			Ensure()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ginkgo.It("Modify headers", func() {
 | 
			
		||||
	ginkgo.It("Modify request headers", func() {
 | 
			
		||||
		vhostHTTPPort := f.AllocPort()
 | 
			
		||||
		serverConf := getDefaultServerConf(vhostHTTPPort)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +292,6 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
 | 
			
		|||
 | 
			
		||||
		f.RunProcesses([]string{serverConf}, []string{clientConf})
 | 
			
		||||
 | 
			
		||||
		// not set auth header
 | 
			
		||||
		framework.NewRequestExpect(f).Port(vhostHTTPPort).
 | 
			
		||||
			RequestModify(func(r *request.Request) {
 | 
			
		||||
				r.HTTP().HTTPHost("normal.example.com")
 | 
			
		||||
| 
						 | 
				
			
			@ -301,6 +300,40 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
 | 
			
		|||
			Ensure()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ginkgo.It("Modify response headers", func() {
 | 
			
		||||
		vhostHTTPPort := f.AllocPort()
 | 
			
		||||
		serverConf := getDefaultServerConf(vhostHTTPPort)
 | 
			
		||||
 | 
			
		||||
		localPort := f.AllocPort()
 | 
			
		||||
		localServer := httpserver.New(
 | 
			
		||||
			httpserver.WithBindPort(localPort),
 | 
			
		||||
			httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
				w.WriteHeader(200)
 | 
			
		||||
			})),
 | 
			
		||||
		)
 | 
			
		||||
		f.RunServer("", localServer)
 | 
			
		||||
 | 
			
		||||
		clientConf := consts.DefaultClientConfig
 | 
			
		||||
		clientConf += fmt.Sprintf(`
 | 
			
		||||
			[[proxies]]
 | 
			
		||||
			name = "test"
 | 
			
		||||
			type = "http"
 | 
			
		||||
			localPort = %d
 | 
			
		||||
			customDomains = ["normal.example.com"]
 | 
			
		||||
			responseHeaders.set.x-from-where = "frp"
 | 
			
		||||
			`, localPort)
 | 
			
		||||
 | 
			
		||||
		f.RunProcesses([]string{serverConf}, []string{clientConf})
 | 
			
		||||
 | 
			
		||||
		framework.NewRequestExpect(f).Port(vhostHTTPPort).
 | 
			
		||||
			RequestModify(func(r *request.Request) {
 | 
			
		||||
				r.HTTP().HTTPHost("normal.example.com")
 | 
			
		||||
			}).
 | 
			
		||||
			Ensure(func(res *request.Response) bool {
 | 
			
		||||
				return res.Header.Get("X-From-Where") == "frp"
 | 
			
		||||
			})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ginkgo.It("Host Header Rewrite", func() {
 | 
			
		||||
		vhostHTTPPort := f.AllocPort()
 | 
			
		||||
		serverConf := getDefaultServerConf(vhostHTTPPort)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue