在
http.HandlerFunc
中检查 r.Method 为 Connect 后及将请求交给 handleTunneling,其中关键的是将 http.ResponseWriter 转换为 http.Hijacker 接管连接,并通过两个 goroutinetransfer
复制请求。
func handleTunneling(w http.ResponseWriter, r *http.Request) {
dst, err := net.DialTimeout("tcp", r.Host, 10*time.Second)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
go transfer(dst, conn)
go transfer(conn, dst)
}
func transfer(dst io.WriteCloser, src io.ReadCloser) {
defer dst.Close()
defer src.Close()
_, _ = io.Copy(dst, src)
}