golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用

Golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用

GoProxy简介

GoProxy是一个用于创建自定义HTTP/HTTPS代理服务器的Go语言库,具有多种可配置选项。该项目旨在提供一个优化的代理服务器,能够处理合理数量的流量,同时保持可定制性和可编程性。

主要特性

  • 仅在特定主机上执行某些操作,支持简单比较或正则表达式评估
  • 在发送请求和响应到浏览器前进行修改
  • 使用自定义的http.Transport向目标服务器发送请求
  • 指定MITM证书缓存以重用相同主机的证书
  • 将普通HTTP流量重定向到自定义处理器
  • 可选择使用的日志记录器
  • 禁用HTTP请求头规范化

代理模式

  1. 常规HTTP代理
  2. 通过CONNECT的HTTPS代理
  3. HTTPS MITM(中间人)代理服务器
  4. "劫持"代理连接

基本示例

以下是一个基本的HTTP/HTTPS代理示例,它只是将数据转发到目的地:

package main

import (
    "log"
    "net/http"

    "github.com/elazarl/goproxy"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = true
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

请求处理器

这行代码会为所有通过代理发送的请求添加X-GoProxy: yxorPoG-X头:

proxy.OnRequest().DoFunc(
    func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
        r.Header.Set("X-GoProxy","yxorPoG-X")
        return r,nil
    })

条件请求处理器

在服务器本地时间8点到17点之间拒绝连接到www.reddit.com

proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
    func(req *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
        if h,_,_ := time.Now().Clock(); h >= 8 && h <= 17 {
            resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusForbidden, "Don't waste your time!")
            return req, resp
        }
        return req, nil
})

请求和响应操作

有三种不同类型的处理器来操作代理的行为:

// 在从客户端接收HTTP CONNECT后,代理与目标主机建立连接前调用的处理器
httpsHandlers   []HttpsHandler

// 在代理向目标主机发送HTTP请求前调用的处理器
reqHandlers     []ReqHandler 

// 在代理从目标主机接收HTTP响应后,代理将响应转发给客户端前调用的处理器
respHandlers    []RespHandler

添加处理器的方法:

// 添加处理器到httpsHandlers 
proxy.OnRequest(some ReqConditions).HandleConnect(YourHandlerFunc())

// 添加处理器到reqHandlers
proxy.OnRequest(some ReqConditions).Do(YourReqHandlerFunc())

// 添加处理器到respHandlers
proxy.OnResponse(some RespConditions).Do(YourRespHandlerFunc())

错误处理

通用错误

如果在通过代理处理请求时发生错误,默认情况下代理会返回HTTP错误500(内部服务器错误),并将错误消息作为正文内容。

连接错误

如果在向目标远程服务器(或代理客户端)发送数据时发生错误,会调用proxy.ConnectionErrHandler来处理错误(如果存在),否则使用默认处理器。

项目状态

该项目已经创建10年,达到了成熟阶段。它可以安全地用于生产环境,许多项目已经在这样做。

完整示例

以下是一个完整的自定义代理服务器示例,展示了如何拦截和修改请求:

package main

import (
	"log"
	"net/http"
	"regexp"
	"time"

	"github.com/elazarl/goproxy"
)

func main() {
	// 创建代理服务器
	proxy := goproxy.NewProxyHttpServer()
	proxy.Verbose = true // 启用详细日志

	// 为所有请求添加自定义头
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			r.Header.Set("X-GoProxy", "yxorPoG-X")
			return r, nil
		})

	// 在8:00-17:59之间阻止访问reddit
	proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
		func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			if h, _, _ := time.Now().Clock(); h >= 8 && h <= 17 {
				resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusForbidden, "工作时间禁止访问Reddit!")
				return req, resp
			}
			return req, nil
		})

	// 阻止访问.gif文件
	proxy.OnRequest(goproxy.UrlMatches(regexp.MustCompile(`.*\.gif$`))).DoFunc(
		func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusForbidden, "GIF文件被阻止")
			return req, resp
		})

	// 启动代理服务器
	log.Println("代理服务器正在监听 :8080")
	log.Fatal(http.ListenAndServe(":8080", proxy))
}

这个示例展示了如何:

  1. 为所有请求添加自定义头
  2. 在特定时间段阻止访问特定网站
  3. 阻止特定类型的文件请求

您可以根据需要扩展此示例,添加更多自定义逻辑。


更多关于golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用GoProxy创建自定义HTTP/HTTPS代理服务器

GoProxy是一个功能强大的Golang代理服务器库,支持HTTP/HTTPS/SOCKS5等多种代理协议。下面我将介绍如何使用GoProxy创建自定义代理服务器。

基本安装

首先安装GoProxy库:

go get github.com/elazarl/goproxy

基本HTTP代理服务器示例

package main

import (
    "log"
    "net/http"
    
    "github.com/elazarl/goproxy"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = true // 启用详细日志
    
    log.Println("代理服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

高级功能示例

1. 请求/响应拦截和修改

package main

import (
    "log"
    "net/http"
    "strings"
    
    "github.com/elazarl/goproxy"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    
    // 拦截所有请求
    proxy.OnRequest().DoFunc(
        func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
            log.Printf("拦截请求: %s %s", r.Method, r.URL)
            
            // 修改请求头
            r.Header.Set("X-Proxy", "GoProxy")
            
            // 阻止访问特定网站
            if strings.Contains(r.URL.Host, "blocked.com") {
                return r, goproxy.NewResponse(r,
                    goproxy.ContentTypeText, http.StatusForbidden,
                    "访问被阻止")
            }
            
            return r, nil
        })
    
    // 拦截所有响应
    proxy.OnResponse().DoFunc(
        func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
            if resp != nil {
                log.Printf("收到响应: %s", resp.Status)
                // 修改响应头
                resp.Header.Add("X-Proxy-Processed", "true")
            }
            return resp
        })
    
    log.Println("高级代理服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

2. HTTPS中间人代理

package main

import (
    "crypto/tls"
    "log"
    "net/http"
    
    "github.com/elazarl/goproxy"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = true
    
    // 设置HTTPS中间人
    proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
    
    // 自定义CA证书 (可选)
    goproxy.GoproxyCa = loadCA() // 需要实现loadCA函数
    
    // HTTPS特定处理
    proxy.OnRequest(goproxy.ReqHostIs("secure.example.com:443")).DoFunc(
        func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
            log.Println("处理HTTPS请求到 secure.example.com")
            return r, nil
        })
    
    log.Println("HTTPS中间人代理启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

// 加载CA证书示例函数
func loadCA() tls.Certificate {
    cert, err := tls.X509KeyPair(certPEM, keyPEM)
    if err != nil {
        log.Fatal("加载CA证书失败:", err)
    }
    return cert
}

// 示例证书数据 (实际使用时应替换为你的证书)
var certPEM = []byte(`-----BEGIN CERTIFICATE-----...`)
var keyPEM = []byte(`-----BEGIN RSA PRIVATE KEY-----...`)

3. 带认证的代理服务器

package main

import (
    "log"
    "net/http"
    "strings"
    
    "github.com/elazarl/goproxy"
)

func main() {
    proxy := goproxy.NewProxyHttpServer()
    
    // 添加认证中间件
    proxy.OnRequest().DoFunc(
        func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
            auth := r.Header.Get("Proxy-Authorization")
            if auth == "" {
                return r, goproxy.NewResponse(r,
                    goproxy.ContentTypeText, http.StatusProxyAuthRequired,
                    "需要代理认证")
            }
            
            // 简单认证检查 (实际应用中应该更安全)
            if !strings.HasPrefix(auth, "Basic ") || 
               !checkCredentials(strings.TrimPrefix(auth, "Basic ")) {
                return r, goproxy.NewResponse(r,
                    goproxy.ContentTypeText, http.StatusForbidden,
                    "无效的认证信息")
            }
            
            return r, nil
        })
    
    log.Println("带认证的代理服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", proxy))
}

func checkCredentials(cred string) bool {
    // 实现你的认证逻辑
    return cred == "dXNlcjpwYXNzd29yZA==" // "user:password" base64编码
}

性能优化建议

  1. 对于高并发场景,考虑使用连接池:
proxy.Tr = &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    DialContext: (&net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }).DialContext,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}
  1. 限制并发连接数:
proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    if len(proxy.Sessions) >= 1000 {
        http.Error(w, "代理服务器繁忙", http.StatusServiceUnavailable)
        return
    }
    proxy.ServeHTTP(w, req)
})

GoProxy提供了丰富的功能来构建各种类型的代理服务器,从简单的转发代理到复杂的中间人拦截代理。根据你的需求选择合适的配置和拦截点,可以构建出功能强大的代理解决方案。

回到顶部