我基本上试图编写一个反向代理服务器,以便当我curl localhost:8080/get
它代理请求https://nghttp2.org/httpbin/get
。
注意:上面列出的https://nghttp2.org/httpbin/get服务是http / 2。但是这种行为也发生在http / 1,例如https://httpbin.org/get。
我正在使用httputil.ReverseProxy,我正在重写URL,同时自定义Host
标头,以免泄漏localhost:8080
到实际的后端。
但是,无论我在标题上设置了多少次,请求仍会使用Host: localhost:8080
命中后端。类似地,我使用mitmproxy
来窥探请求,它看起来像net / http.Client将:authority
伪标头设置为localhost:8080
这是我的源代码:
package main
import (
"log"
"net/http"
"net/http/httputil"
)
func main() {
proxy := &httputil.ReverseProxy{
Transport: roundTripper(rt),
Director: func(req *http.Request) {
req.URL.Scheme = "https"
req.URL.Host = "nghttp2.org"
req.URL.Path = "/httpbin" + req.URL.Path
req.Header.Set("Host", "nghttp2.org") // <--- I set it here first
},
}
log.Fatal(http.ListenAndServe(":8080", proxy))
}
func rt(req *http.Request) (*http.Response, error) {
log.Printf("request received. url=%s", req.URL)
req.Header.Set("Host", "nghttp2.org") // <--- I set it here as well
defer log.Printf("request complete. url=%s", req.URL)
return http.DefaultTransport.RoundTrip(req)
}
// roundTripper makes func signature a http.RoundTripper
type roundTripper func(*http.Request) (*http.Response, error)
func (f roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
当我查询curl localhost:8080/get
时,请求被代理到https://nghttp2.org/httpbin/get。回显的响应表明,显然我的指令设置Host
标头没有做任何事情:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip",
"Host": "localhost:8080",
"User-Agent": "curl/7.54.0"
},
"origin": "2601:602:9c02:16c2:fca3:aaab:3914:4a71",
"url": "https://localhost:8080/httpbin/get"
}
mitmproxy snooping还清楚地表明该请求是使用:authority
伪标头设置为localhost:8080
:
来自http.Request
docs:
// For server requests, Host specifies the host on which the URL // is sought. Per RFC 7230, section 5.4, this is either the value // of the "Host" header or the host name given in the URL itself. // It may be of the form "host:port". For international domain // names, Host may be in Punycode or Unicode form. Use // golang.org/x/net/idna to convert it to either format if // needed. // To prevent DNS rebinding attacks, server Handlers should // validate that the Host header has a value for which the // Handler considers itself authoritative. The included // ServeMux supports patterns registered to particular host // names and thus protects its registered Handlers. // // For client requests, Host optionally overrides the Host // header to send. If empty, the Request.Write method uses // the value of URL.Host. Host may contain an international // domain name. Host string
所以URL.Host
的值仅用于request.Host
为空的情况,但事实并非如此。设置request.Host
应解决此问题:
req.Host = "nghttp2.org"
相关问题讨论了here。