• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Go 实现HTTP中间人代理的操作

    goproxy

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

    项目地址

    安装

    go get github.com/ouqiang/goproxy

    使用

    package main
    import (
        "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 main
    import (
        "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)
        }
    }
    

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

    您可能感兴趣的文章:
    • go 原生http web 服务跨域restful api的写法介绍
    • golang http使用踩过的坑与填坑指南
    • Go http client 连接池不复用的问题
    • Golang实现http server提供压缩文件下载功能
    • golang语言http协议get拼接参数操作
    • 在go文件服务器加入http.StripPrefix的用途介绍
    • Golang 实现分片读取http超大文件流和并发控制
    上一篇:golang 实现一个负载均衡案例(随机,轮训)
    下一篇:解决Go gorm踩过的坑
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Go 实现HTTP中间人代理的操作 实现,HTTP,中间人,代理,的,