一个利用go反向代理解决api转发的例子(go反向代理)
实现的效果:
如果访问的url路径是类似 /163/ 或 /163/debian 的形式,则转发到163开源镜像服务器
直接上代码:
package main import ( "fmt" "log" "net/http" "net/http/httputil" "net/url" ) var fwdHost = "mirrors.163.com" //http首部字段的HOST取值 var fwdTo = "http://" + fwdHost + "/" var fwdPrefix = "/163/" type forward struct { RProxy *httputil.ReverseProxy } func (f *forward) ServeHTTP(wr http.ResponseWriter, req *http.Request) { //fmt.Printf("http头部是:%+v\n", req.Header) //假设这是处理http头部的代码 fmt.Printf(" #### REQ:%+v\n", req) //处理完后转发到网易163镜像 req.URL.Path = req.URL.Path[len(fwdPrefix)-1:] //修改了这里,req.RequestURI会跟着变 req.Host = fwdHost fmt.Printf(" *** REQ:%+v\n", req) f.RProxy.ServeHTTP(wr, req) } func main() { var fwd forward u, _ := url.Parse(fwdTo) fwd.RProxy = httputil.NewSingleHostReverseProxy(u) http.Handle(fwdPrefix, &fwd) //所有请求将转发到网易163的debian镜像 http.HandleFunc("/", notForward) http.HandleFunc("/api/v1/", notForward) log.Fatal(http.ListenAndServe(":3000", nil)) } func notForward(wr http.ResponseWriter, req *http.Request) { wr.Write([]byte(fmt.Sprintf(`<html> <body> <em>Not forward!!</em> <br /> <i>url = %s</i> </body> </html> `,req.URL.String()))) }
类似的还有更简单的做法,关键在httputil.ReverseProxy的Director字段:
func APIReverseProxy(host string) http.HandlerFunc { var rp = httputil.ReverseProxy{ Director:func(req *http.Request) { req.URL.Scheme = "http"
req.URL.Host = host req.Host = host //对于一个ip地址托管多个域名的情况下,必须要给req.Host赋值,如果一个ip地址只有一个域名,可以不写这一句 //req.URL.Path = //如果需要改path的话 }, } return func(wr http.ResponsWriter, req *http.Request){ rp.ServeHTTP(wr,req) } //这里是返回http.HandlerFunc的例子,其实也可以直接返回rp(因为*rp就是一个http.Handler, *rp实现了ServeHTTP(wr,req)方法) }
关于req.URL.Host和req.Host:
go http包中对request中Host的注释:
// For server requests Host specifies the host on which the
// URL is sought. Per RFC 2616, 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.
//
// 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
另外:
req.URL.Host是从URL中解析出来的,
req.Host是http请求头部"Host" , 这个头部用于实现虚拟主机(一个ip托管多个域名),http1.1规范中,Host头是必须存在的
对于下面的请求:
GET /index.html HTTP/1.1
Host: www.example.org:8080
req.URL.Host是空的
对于通过代理的请求,req.URL.Host是目标主机,req.Host是代理服务器。
对于不是走代理的请求,req.URL.Host是空的,req.Host是目标主机