go语言原生支持反向代理。就是通过httputil.ReverseProxy来实现的。
对一个目标主机创建反向代理:
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
return httputil.NewSingleHostReverseProxy(url), nil
}创建一个Handler或者HandleFunc,调用反向代理来处理请求
// 返回一个处理函数,使用proxy来处理请求
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}为指定的url使用处理函数即可
func main() {
proxy, err := NewProxy("http://www.baidu.com")
if err != nil {
panic(err)
}
http.HandleFunc("/", ProxyRequestHandler(proxy))
log.Fatal(http.ListenAndServe(":9999", nil))
}作为反向代理,可以在请求发送之前和之后对请求或响应进行一些处理,例如额外添加一些信息。
修改响应
通过修改反向代理对象的ModifyResponse来对响应进行修改,这个字段是一个函数,函数签名为
func(*http.Response) error例如,在响应头添加数据
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.ModifyResponse = modifyResponse()
return proxy, nil
}
func modifyResponse() func(*http.Response) error {
return func(resp *http.Response) error {
resp.Header.Set("X-Proxy", "Magical")
return nil
}
}如果在处理响应的时候出错,ModifyResponse调用返回一个错误,那么就会调用反向代理的ErrorHandler对应的错误处理函数,所以也可以通过设置此函数来进行错误处理。
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.ModifyResponse = modifyResponse()
proxy.ErrorHandler = errorHandler()
return proxy, nil
}
func errorHandler() func(http.ResponseWriter, *http.Request, error) {
return func(w http.ResponseWriter, req *http.Request, err error) {
fmt.Printf("Got error while modifying response: %v \n", err)
return
}
}
func modifyResponse() func(*http.Response) error {
return func(resp *http.Response) error {
return errors.New("response body is invalid")
}
}修改请求
使用反向代理,对于实际的目标主机来说,与它通信的不是真正的客户端,而是代理对象,这样对于目标主机来说真正的客户端时不可知的,这对于一些信息的记录是不利的。 所以在使用反向代理对象的时候,通过在反向代理发送请求的时候,会先在请求头上添加信息,携带真实的客户端ip,这样目标主机就能够知道真正的客户机了。
这一点通过反向代理的Director对应的函数来实现。
// NewProxy takes target host and creates a reverse proxy
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
modifyRequest(req)
}
proxy.ModifyResponse = modifyResponse()
proxy.ErrorHandler = errorHandler()
return proxy, nil
}
func modifyRequest(req *http.Request) {
req.Header.Set("X-Real-IP", "IP")
}通过go语言提供的ReverseProxy类型,可以很容易地实现自己的反向代理对象,同时对请求和响应对象进行修改设置。实现一个代理层。