一個利用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是目標主機
浙公網安備 33010602011771號