go doc http.Transport
在這個接口下進行http協議的讀寫. 因此只要是實現了這個接口也就實現http協議.
代理的思路如下:
1. 本地開啓一個http服務接受請求.
2. 收到請求之後正確的向下一個節點(下一個代理或者真正的服務器)發送請求
3. 從下一個節點接收相應並正確的傳遞給用戶.
也就是充當一個中間人.
在處理請求和相應過程中的step 1/2/3分別是:
1.復制一個request. 並修改/插入header信息.
如果頭部有X-Forwarded-For字段, 則添加上一個節點的IP地址
2. 採用http.Transport實力的RoundTrip投遞請求, 拿到相應的http.Response.
需要做一些 error判定.
3. 把拿到的Reponse寫入http.ResponseWriter(在go裏的操作)
package main
import (
"fmt"
"io"
"net"
"net/http"
"strconv"
"strings"
"time"
)
func main() {
port := 8081
fmt.Println("Proxy serve on :" + strconv.Itoa(port))
svr := http.NewServeMux()
svr.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
fmt.Printf("Received request %s %s %s\n",
req.Method,
req.Host,
req.RemoteAddr,
)
transport := http.DefaultTransport
// step 1
outReq := new(http.Request)
*outReq = *req // this only does shallow copies of maps
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
clientIP = strings.Join(prior, ", ") + ", " + clientIP
}
outReq.Header.Set("X-Forwarded-For", clientIP)
}
// step 2
res, err := transport.RoundTrip(outReq)
if err != nil {
rw.WriteHeader(http.StatusBadGateway)
return
}
// step 3
for key, value := range res.Header {
for _, v := range value {
rw.Header().Add(key, v)
}
}
rw.WriteHeader(res.StatusCode)
io.Copy(rw, res.Body)
res.Body.Close()
})
serverInstance := http.Server{
Addr: fmt.Sprintf(":%d", port),
ReadTimeout: 10 * time.Second,
Handler: svr,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
rv := serverInstance.ListenAndServe()
fmt.Printf("\n%v", rv)
// http.HandleFunc("/", SomeServeHTTP)
// http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
原文鏈接:
http://cizixs.com/2017/03/21/http-proxy-and-golang-implementation