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类型,可以很容易地实现自己的反向代理对象,同时对请求和响应对象进行修改设置。实现一个代理层。