http代理一般用于突破网络限制和加速网络用,其原理就是代转发请求和回应。
http流量,未加密,可以直接从请求头中获得要访问目标的地址,代理服创建一条访问目标的tcp连接,将http流量相互转发即可
https流量,由于加密,无法读取内容,所以多出一个CONNECT请求(我们最经常接触的是GET,POST),客户端会先向代理服发送CONNECT请求,CONNECT请求会带上要访问目标的地址,代理服创建一条访问目标的tcp连接,相互转发流量即可
golang实现具体代码如下
package main
import (
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
)
type Pxy struct {
// The transport used to send proxy requests to actual server.
// If nil, http.DefaultTransport is used.
Transport http.RoundTripper
Credential string
}
// NewProxy returns a new Pxy object
func NewProxy() *Pxy {
return &Pxy{}
}
func (p *Pxy) handleTunnel(rw http.ResponseWriter, req *http.Request, localPort int) {
host := req.URL.Host
hij, ok := rw.(http.Hijacker)
if !ok {
panic("HTTP Server does not support hijacking")
}
client, _, err := hij.Hijack()
if err != nil {
return
}
// Connect to the proxy server
proxyConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort))
if err != nil {
return
}
defer proxyConn.Close()
// Send a CONNECT request to the proxy server
connectRequest := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", host, host)
_, err = proxyConn.Write([]byte(connectRequest))
if err != nil {
return
}
go io.Copy(proxyConn, client)
io.Copy(client, proxyConn)
}
// ServeHTTP is the main handler for all requests.
func (p *Pxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
localPort := 2080
if req.Method == "CONNECT" {
p.handleTunnel(rw, req, localPort)
return
}
transport := &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse("http://127.0.0.1:2080")
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
outReq := new(http.Request)
*outReq = *req
res, err := transport.RoundTrip(outReq)
if err != nil {
rw.WriteHeader(http.StatusBadGateway)
return
}
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()
}
func main() {
pxy := NewProxy()
err := http.ListenAndServe(fmt.Sprintf(":%d", 8080), pxy)
if err != nil {
panic(err)
}
}
以上只是一个简单的案例,可以在其中添加自定义逻辑,如流量统计,动态路由等