Browse Source

Merge pull request #786 from fatedier/header

support setting headers in http request
fatedier 6 years ago
parent
commit
cf4136fe99
9 changed files with 94 additions and 20 deletions
  1. 20 2
      README.md
  2. 17 0
      README_zh.md
  3. 6 4
      conf/frpc_full.ini
  4. 26 5
      models/config/proxy.go
  5. 7 6
      models/msg/msg.go
  6. 1 0
      server/proxy.go
  7. 1 1
      utils/version/version.go
  8. 15 2
      utils/vhost/newhttp.go
  9. 1 0
      utils/vhost/vhost.go

+ 20 - 2
README.md

@@ -36,6 +36,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
     * [Support KCP Protocol](#support-kcp-protocol)
     * [Connection Pool](#connection-pool)
     * [Rewriting the Host Header](#rewriting-the-host-header)
+    * [Set Headers In HTTP Request](#set-headers-in-http-request)
     * [Get Real IP](#get-real-ip)
     * [Password protecting your web service](#password-protecting-your-web-service)
     * [Custom subdomain names](#custom-subdomain-names)
@@ -485,7 +486,7 @@ This feature is fit for a large number of short connections.
 
 ### Rewriting the Host Header
 
-When forwarding to a local port, frp does not modify the tunneled HTTP requests at all, they are copied to your server byte-for-byte as they are received. Some application servers use the Host header for determining which development site to display. For this reason, frp can rewrite your requests with a modified Host header. Use the `host_header_rewrite` switch to rewrite incoming HTTP requests.
+When forwarding to a local port, frp does not modify the tunneled HTTP requests at all, they are copied to your server byte-for-byte as they are received. Some application servers use the Host header for determining which development site to display. For this reason, frp can rewrite your requests with a modified host header. Use the `host_header_rewrite` switch to rewrite incoming HTTP requests.
 
 ```ini
 # frpc.ini
@@ -496,7 +497,24 @@ custom_domains = test.yourdomain.com
 host_header_rewrite = dev.yourdomain.com
 ```
 
-If `host_header_rewrite` is specified, the Host header will be rewritten to match the hostname portion of the forwarding address.
+If `host_header_rewrite` is specified, the host header will be rewritten to match the hostname portion of the forwarding address.
+
+### Set Headers In HTTP Request
+
+You can set headers for proxy which type is `http`.
+
+```ini
+# frpc.ini
+[web]
+type = http
+local_port = 80
+custom_domains = test.yourdomain.com
+host_header_rewrite = dev.yourdomain.com
+header_X-From-Where = frp
+```
+
+Note that params which have prefix `header_` will be added to http request headers.
+In this example, it will set header `X-From-Where: frp` to http request.
 
 ### Get Real IP
 

+ 17 - 0
README_zh.md

@@ -34,6 +34,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
     * [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
     * [连接池](#连接池)
     * [修改 Host Header](#修改-host-header)
+    * [设置 http 请求的 header](#设置-http-请求的-header)
     * [获取用户真实 IP](#获取用户真实-ip)
     * [通过密码保护你的 web 服务](#通过密码保护你的-web-服务)
     * [自定义二级域名](#自定义二级域名)
@@ -525,6 +526,22 @@ host_header_rewrite = dev.yourdomain.com
 
 原来 http 请求中的 host 字段 `test.yourdomain.com` 转发到后端服务时会被替换为 `dev.yourdomain.com`。
 
+### 设置 http 请求的 header
+
+对于 `type = http` 的代理,可以设置在转发中动态添加的 header 参数。
+
+```ini
+# frpc.ini
+[web]
+type = http
+local_port = 80
+custom_domains = test.yourdomain.com
+host_header_rewrite = dev.yourdomain.com
+header_X-From-Where = frp
+```
+
+对于参数配置中所有以 `header_` 开头的参数(支持同时配置多个),都会被添加到 http 请求的 header 中,根据如上的配置,会在请求的 header 中加上 `X-From-Where: frp`。
+
 ### 获取用户真实 IP
 
 目前只有 **http** 类型的代理支持这一功能,可以通过用户请求的 header 中的 `X-Forwarded-For` 和 `X-Real-IP` 来获取用户真实 IP。

+ 6 - 4
conf/frpc_full.ini

@@ -56,10 +56,10 @@ dns_server = 8.8.8.8
 # heartbeat_interval = 30
 # heartbeat_timeout = 90
 
-# ssh is the proxy name same as server's configuration
-# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as your_name.ssh
+# 'ssh' is the unique proxy name
+# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
 [ssh]
-# tcp | udp | http | https, default is tcp
+# tcp | udp | http | https | stcp | xtcp, default is tcp
 type = tcp
 local_ip = 127.0.0.1
 local_port = 22
@@ -120,6 +120,8 @@ custom_domains = web02.yourdomain.com
 # locations is only available for http type
 locations = /,/pic
 host_header_rewrite = example.com
+# params with prefix "header_" will be used to update http request headers
+header_X-From-Where = frp
 
 [web02]
 type = https
@@ -136,7 +138,7 @@ remote_port = 6003
 # if plugin is defined, local_ip and local_port is useless
 # plugin will handle connections got from frps
 plugin = unix_domain_socket
-# params set with prefix "plugin_" that plugin needed
+# params with prefix "plugin_" that plugin needed
 plugin_unix_path = /var/run/docker.sock
 
 [plugin_http_proxy]

+ 26 - 5
models/config/proxy.go

@@ -429,10 +429,11 @@ type HttpProxyConf struct {
 
 	LocalSvrConf
 
-	Locations         []string `json:"locations"`
-	HostHeaderRewrite string   `json:"host_header_rewrite"`
-	HttpUser          string   `json:"http_user"`
-	HttpPwd           string   `json:"http_pwd"`
+	Locations         []string          `json:"locations"`
+	HttpUser          string            `json:"http_user"`
+	HttpPwd           string            `json:"http_pwd"`
+	HostHeaderRewrite string            `json:"host_header_rewrite"`
+	Headers           map[string]string `json:"headers"`
 }
 
 func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
@@ -447,9 +448,20 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
 		strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
 		cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
 		cfg.HttpUser != cmpConf.HttpUser ||
-		cfg.HttpPwd != cmpConf.HttpPwd {
+		cfg.HttpPwd != cmpConf.HttpPwd ||
+		len(cfg.Headers) != len(cmpConf.Headers) {
 		return false
 	}
+
+	for k, v := range cfg.Headers {
+		if v2, ok := cmpConf.Headers[k]; !ok {
+			return false
+		} else {
+			if v != v2 {
+				return false
+			}
+		}
+	}
 	return true
 }
 
@@ -461,6 +473,7 @@ func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
 	cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
 	cfg.HttpUser = pMsg.HttpUser
 	cfg.HttpPwd = pMsg.HttpPwd
+	cfg.Headers = pMsg.Headers
 }
 
 func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
@@ -487,6 +500,13 @@ func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section i
 	cfg.HostHeaderRewrite = section["host_header_rewrite"]
 	cfg.HttpUser = section["http_user"]
 	cfg.HttpPwd = section["http_pwd"]
+	cfg.Headers = make(map[string]string)
+
+	for k, v := range section {
+		if strings.HasPrefix(k, "header_") {
+			cfg.Headers[strings.TrimPrefix(k, "header_")] = v
+		}
+	}
 	return
 }
 
@@ -498,6 +518,7 @@ func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
 	pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
 	pMsg.HttpUser = cfg.HttpUser
 	pMsg.HttpPwd = cfg.HttpPwd
+	pMsg.Headers = cfg.Headers
 }
 
 func (cfg *HttpProxyConf) CheckForCli() (err error) {

+ 7 - 6
models/msg/msg.go

@@ -91,12 +91,13 @@ type NewProxy struct {
 	RemotePort int `json:"remote_port"`
 
 	// http and https only
-	CustomDomains     []string `json:"custom_domains"`
-	SubDomain         string   `json:"subdomain"`
-	Locations         []string `json:"locations"`
-	HostHeaderRewrite string   `json:"host_header_rewrite"`
-	HttpUser          string   `json:"http_user"`
-	HttpPwd           string   `json:"http_pwd"`
+	CustomDomains     []string          `json:"custom_domains"`
+	SubDomain         string            `json:"subdomain"`
+	Locations         []string          `json:"locations"`
+	HttpUser          string            `json:"http_user"`
+	HttpPwd           string            `json:"http_pwd"`
+	HostHeaderRewrite string            `json:"host_header_rewrite"`
+	Headers           map[string]string `json:"headers"`
 
 	// stcp
 	Sk string `json:"sk"`

+ 1 - 0
server/proxy.go

@@ -225,6 +225,7 @@ type HttpProxy struct {
 func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 	routeConfig := vhost.VhostRouteConfig{
 		RewriteHost:  pxy.cfg.HostHeaderRewrite,
+		Headers:      pxy.cfg.Headers,
 		Username:     pxy.cfg.HttpUser,
 		Password:     pxy.cfg.HttpPwd,
 		CreateConnFn: pxy.GetRealConn,

+ 1 - 1
utils/version/version.go

@@ -19,7 +19,7 @@ import (
 	"strings"
 )
 
-var version string = "0.19.1"
+var version string = "0.20.0"
 
 func Full() string {
 	return version

+ 15 - 2
utils/vhost/newhttp.go

@@ -63,12 +63,17 @@ func NewHttpReverseProxy() *HttpReverseProxy {
 		Director: func(req *http.Request) {
 			req.URL.Scheme = "http"
 			url := req.Context().Value("url").(string)
-			host := getHostFromAddr(req.Context().Value("host").(string))
-			host = rp.GetRealHost(host, url)
+			oldHost := getHostFromAddr(req.Context().Value("host").(string))
+			host := rp.GetRealHost(oldHost, url)
 			if host != "" {
 				req.Host = host
 			}
 			req.URL.Host = req.Host
+
+			headers := rp.GetHeaders(oldHost, url)
+			for k, v := range headers {
+				req.Header.Set(k, v)
+			}
 		},
 		Transport: &http.Transport{
 			ResponseHeaderTimeout: responseHeaderTimeout,
@@ -117,6 +122,14 @@ func (rp *HttpReverseProxy) GetRealHost(domain string, location string) (host st
 	return
 }
 
+func (rp *HttpReverseProxy) GetHeaders(domain string, location string) (headers map[string]string) {
+	vr, ok := rp.getVhost(domain, location)
+	if ok {
+		headers = vr.payload.(*VhostRouteConfig).Headers
+	}
+	return
+}
+
 func (rp *HttpReverseProxy) CreateConnection(domain string, location string) (net.Conn, error) {
 	vr, ok := rp.getVhost(domain, location)
 	if ok {

+ 1 - 0
utils/vhost/vhost.go

@@ -59,6 +59,7 @@ type VhostRouteConfig struct {
 	RewriteHost string
 	Username    string
 	Password    string
+	Headers     map[string]string
 
 	CreateConnFn CreateConnFunc
 }