goproxy

Go HTTP(S)代理库, 支持中间人代理解密HTTPS

安装

go get github.com/ouqiang/goproxy

使用

package mainimport (    "net/http"    "time"    "github.com/ouqiang/goproxy")func main() {    proxy := goproxy.New()    server := &http.Server{        Addr:         ":8080",        Handler:      proxy,        ReadTimeout:  1 * time.Minute,        WriteTimeout: 1 * time.Minute,    }    err := server.ListenAndServe()    if err != nil {        panic(err)    }}

代理测试

curl -x localhost:8080 https://www.baidu.com

中间人代理, 解密HTTPS

系统需导入根证书 mitm-proxy.crt

package mainimport (    "crypto/tls"    "net/http"    "sync"    "time"    "github.com/ouqiang/goproxy")// 实现证书缓存接口type Cache struct {    m sync.Map}func (c *Cache) Set(host string, cert *tls.Certificate) {    c.m.Store(host, cert)}func (c *Cache) Get(host string) *tls.Certificate {    v, ok := c.m.Load(host)    if !ok {        return nil    }    return v.(*tls.Certificate)}func main() {    proxy := goproxy.New(goproxy.WithDecryptHTTPS(&Cache{}))    server := &http.Server{        Addr:         ":8080",        Handler:      proxy,        ReadTimeout:  1 * time.Minute,        WriteTimeout: 1 * time.Minute,    }    err := server.ListenAndServe()    if err != nil {        panic(err)    }}

事件处理

实现Delegate接口

type Delegate interface {    // Connect 收到客户端连接    Connect(ctx *Context, rw http.ResponseWriter)    // Auth 代理身份认证    Auth(ctx *Context, rw http.ResponseWriter)    // BeforeRequest HTTP请求前 设置X-Forwarded-For, 修改Header、Body    BeforeRequest(ctx *Context)    // BeforeResponse 响应发送到客户端前, 修改Header、Body、Status Code    BeforeResponse(ctx *Context, resp *http.Response, err error)    // ParentProxy 上级代理    ParentProxy(*http.Request) (*url.URL, error)    // Finish 本次请求结束    Finish(ctx *Context)    // 记录错误信息    ErrorLog(err error)}
type EventHandler struct{}func (e *EventHandler) Connect(ctx *goproxy.Context, rw http.ResponseWriter) {    // 保存的数据可以在后面的回调方法中获取    ctx.Data["req_id"] = "uuid"    // 禁止访问某个域名    if strings.Contains(ctx.Req.URL.Host, "example.com") {        rw.WriteHeader(http.StatusForbidden)        ctx.Abort()        return    }}func (e *EventHandler) Auth(ctx *goproxy.Context, rw http.ResponseWriter)  {    // 身份验证}func (e *EventHandler) BeforeRequest(ctx *goproxy.Context) {    // 修改header    ctx.Req.Header.Add("X-Request-Id", ctx.Data["req_id"].(string))    // 设置X-Forwarded-For    if clientIP, _, err := net.SplitHostPort(ctx.Req.RemoteAddr); err == nil {        if prior, ok := ctx.Req.Header["X-Forwarded-For"]; ok {            clientIP = strings.Join(prior, ", ") + ", " + clientIP        }        ctx.Req.Header.Set("X-Forwarded-For", clientIP)    }    // 读取Body    body, err := ioutil.ReadAll(ctx.Req.Body)    if err != nil {        // 错误处理        return    }    // Request.Body只能读取一次, 读取后必须再放回去    // Response.Body同理    ctx.Req.Body = ioutil.NopCloser(bytes.NewReader(body))}func (e *EventHandler) BeforeResponse(ctx *goproxy.Context, resp *http.Response, err error) {    if err != nil {        return    }    // 修改response}// 设置上级代理func (e *EventHandler) ParentProxy(req *http.Request) (*url.URL, error) {    return url.Parse("http://localhost:1087")}func (e *EventHandler) Finish(ctx *goproxy.Context) {    fmt.Printf("请求结束 URL:%s\n", ctx.Req.URL)}// 记录错误日志func (e *EventHandler) ErrorLog(err error) {    log.Println(err)}func main() {    proxy := goproxy.New(goproxy.WithDelegate(&EventHandler{}))    server := &http.Server{        Addr:         ":8080",        Handler:      proxy,        ReadTimeout:  1 * time.Minute,        WriteTimeout: 1 * time.Minute,    }    err := server.ListenAndServe()    if err != nil {        panic(err)    }}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/order_c/article/details/81839273