golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用
Golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用
GoProxy简介
GoProxy是一个用于创建自定义HTTP/HTTPS代理服务器的Go语言库,具有多种可配置选项。该项目旨在提供一个优化的代理服务器,能够处理合理数量的流量,同时保持可定制性和可编程性。
主要特性
- 仅在特定主机上执行某些操作,支持简单比较或正则表达式评估
- 在发送请求和响应到浏览器前进行修改
- 使用自定义的http.Transport向目标服务器发送请求
- 指定MITM证书缓存以重用相同主机的证书
- 将普通HTTP流量重定向到自定义处理器
- 可选择使用的日志记录器
- 禁用HTTP请求头规范化
代理模式
- 常规HTTP代理
- 通过CONNECT的HTTPS代理
- HTTPS MITM(中间人)代理服务器
- "劫持"代理连接
基本示例
以下是一个基本的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))
}
这个示例展示了如何:
- 为所有请求添加自定义头
- 在特定时间段阻止访问特定网站
- 阻止特定类型的文件请求
您可以根据需要扩展此示例,添加更多自定义逻辑。
更多关于golang创建自定义HTTP/HTTPS代理服务器插件库GoProxy的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于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编码
}
性能优化建议
- 对于高并发场景,考虑使用连接池:
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,
}
- 限制并发连接数:
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提供了丰富的功能来构建各种类型的代理服务器,从简单的转发代理到复杂的中间人拦截代理。根据你的需求选择合适的配置和拦截点,可以构建出功能强大的代理解决方案。