這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
先看反向 Proxy庫
type ReverseProxy struct {// Director must be a function which modifies// the request into a new request to be sent// using Transport. Its response is then copied// back to the original client unmodified.Director func(*http.Request)// The transport used to perform proxy requests.// If nil, http.DefaultTransport is used.Transport http.RoundTripper// FlushInterval specifies the flush interval// to flush to the client while copying the// response body.// If zero, no periodic flushing is done.FlushInterval time.Duration// ErrorLog specifies an optional logger for errors// that occur when attempting to proxy the request.// If nil, logging goes to os.Stderr via the log package's// standard logger.ErrorLog *log.Logger// BufferPool optionally specifies a buffer pool to// get byte slices for use by io.CopyBuffer when// copying HTTP response bodies.BufferPool BufferPool}接著我們看一個例子(網上拷的)
package mainimport ("log""net/http""net/http/httputil""net/url")type handle struct {host stringport string}func (this *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {remote, err := url.Parse("http://" + this.host + ":" + this.port)if err != nil {panic(err)}proxy := httputil.NewSingleHostReverseProxy(remote)proxy.ServeHTTP(w, r)}func startServer() {//被代理的伺服器host和porth := &handle{host: "127.0.0.1", port: "80"}err := http.ListenAndServe(":8888", h)if err != nil {log.Fatalln("ListenAndServe: ", err)}}func main() {startServer()}
使用起來很簡單。但是我有時有一種特殊需求。如內網代理(想訪問的目標伺服器在內網),那該如何辦呢?一般方向代理是Proxy 伺服器可以主動訪問目標伺服器的,現在目標伺服器在內網,Proxy 伺服器不能主動去訪問目標伺服器。通常我們的Proxy 伺服器在公網(對於客戶和目標伺服器),我們讓內網的目標伺服器先主動與Proxy 伺服器建立一個串連,當客戶需要經過Proxy 伺服器訪問目標內網伺服器時,再將請求經過內網伺服器與Proxy 伺服器預先建立的串連發送到內網伺服器。
我們有兩個服務程式,一個運行在Proxy 伺服器,一個運行在內網伺服器。
將上面代碼稍微改造就成為運行在Proxy 伺服器的程式:
package mainimport ("log""net/http""net/http/httputil""net/url")var conntype handle struct {}func (this *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {remote, err := url.Parse("http://127.0.0.1:443")if err != nil {panic(err)}proxy := httputil.NewSingleHostReverseProxy(remote)var pTransport http.RoundTripper = &http.Transport{Proxy: http.ProxyFromEnvironment,Dial: Dial,TLSHandshakeTimeout: 10 * time.Second,ExpectContinueTimeout: 1 * time.Second,}proxy.Transport = pTransportproxy.ServeHTTP(w,r)}func startServer() {//接受內網伺服器主動串連8088連接埠l, err := net.Listen("tcp", "0.0.0.0:8088")if err != nil {return }go func() {for {conn, err = l.Accept() //迴圈接受用戶端和裝置的串連請求if err != nil {beego.Error("Can't Accept: ", err)return}}}()//接受用戶端的串連h := &handle{}err := http.ListenAndServe(":8888", h)if err != nil {log.Fatalln("ListenAndServe: ", err)}}func Dial(network, address string) (net.Conn, error) {return conn, nil}func main() {startServer()}
具體架構就是在這樣,文法不一定對。
用戶端代碼(大概):
func proxy(local, remote string) {conf := &tls.Config{InsecureSkipVerify: true,}rp, err := tls.Dial("tcp", remote, conf)if err != nil {beego.Error("Can't' connect:", remote, " err:", err)return}defer util.CloseConn(rp)config := &tls.Config{InsecureSkipVerify: true} //不做認證校正lp, err := tls.Dial("tcp", local, config)if err != nil {beego.Error("Can't' connect:", other, " err:", err)rp.Close()return}defer util.CloseConn(lp)//buf = make([]byte, 1024*1024)rp.SetReadDeadline(time.Time{})lp.SetReadDeadline(time.Time{})flag := make(chan error)go Transfer(rp, lp, flag)}func Transfer(a, b net.Conn, flag chan error) {cp := func(r, w net.Conn) {n, err := io.Copy(r, w)r.Close()w.Close()beego.Debug("Transfer", n, " bytes between ", a.RemoteAddr(), " and ", b.RemoteAddr())flag <- err}go cp(a, b)go cp(b, a)}main(){//本地服務地址和Proxy 伺服器接受內網伺服器串連的地址 proxy("127.0.0.1:443","192.168.16.110:8088")}
以上僅是大概架構,以供參考