update: 2023-03-15

1. 移除URL重写器
2. 移除插件系统
This commit is contained in:
2025-03-14 00:14:55 +08:00
parent 7efc72b362
commit 4ac5aab7a0
15 changed files with 9 additions and 504 deletions

View File

@@ -40,12 +40,6 @@ GoProxy是一个功能强大的Go语言HTTP代理库支持HTTP、HTTPS和WebS
- 后端响应时间 - 后端响应时间
- 支持自定义处理逻辑(委托模式) - 支持自定义处理逻辑(委托模式)
- 支持DNS缓存 - 支持DNS缓存
- 支持URL重写反向代理模式
- 支持插件系统
- 动态加载插件
- 插件生命周期管理
- 插件间通信
- 插件配置管理
- 支持认证授权 - 支持认证授权
- JWT认证 - JWT认证
- 基于角色的访问控制 - 基于角色的访问控制
@@ -485,9 +479,8 @@ resolver.Clear()
GoProxy采用模块化设计主要包含以下模块 GoProxy采用模块化设计主要包含以下模块
- **代理核心Proxy**处理HTTP请求和响应实现代理功能 - **代理核心Proxy**处理HTTP请求和响应实现代理功能
- **反向代理ReverseProxy**:处理反向代理请求,支持URL重写和请求修改 - **反向代理ReverseProxy**:处理反向代理请求,支持请求修改
- **路由Router**:基于主机名、路径、正则表达式等规则路由请求到不同的后端 - **路由Router**:基于主机名、路径、正则表达式等规则路由请求到不同的后端
- **URL重写Rewriter**重写请求URL和响应中的URL
- **代理上下文Context**:保存请求上下文信息,用于在处理过程中传递数据 - **代理上下文Context**:保存请求上下文信息,用于在处理过程中传递数据
- **代理委托Delegate**:定义代理处理请求的各个阶段的回调方法,用于自定义处理逻辑 - **代理委托Delegate**:定义代理处理请求的各个阶段的回调方法,用于自定义处理逻辑
- **连接缓冲区ConnBuffer**:封装网络连接,提供缓冲读写功能 - **连接缓冲区ConnBuffer**:封装网络连接,提供缓冲读写功能
@@ -577,7 +570,6 @@ GoProxy 提供了以下 With 方法用于配置代理的各个方面:
- `WithReverseProxy(enable bool)`: 启用反向代理模式 - `WithReverseProxy(enable bool)`: 启用反向代理模式
- `WithEnableRetry(maxRetries int, baseBackoff, maxBackoff time.Duration)`: 启用请求重试 - `WithEnableRetry(maxRetries int, baseBackoff, maxBackoff time.Duration)`: 启用请求重试
- `WithRateLimit(rps float64)`: 设置请求限流 - `WithRateLimit(rps float64)`: 设置请求限流
- `WithURLRewrite(enable bool)`: 启用URL重写
- `WithEnableCORS(enable bool)`: 启用CORS支持 - `WithEnableCORS(enable bool)`: 启用CORS支持
#### 证书相关选项 #### 证书相关选项
@@ -644,7 +636,6 @@ GoProxy提供了多个扩展点可以通过实现相应的接口进行扩展
GoProxy的反向代理模式提供以下特性 GoProxy的反向代理模式提供以下特性
- **URL重写**支持基于前缀和正则表达式的URL重写
- **路由规则**:支持基于主机名、路径、正则表达式等的路由规则 - **路由规则**:支持基于主机名、路径、正则表达式等的路由规则
- **请求修改**:支持修改发往后端服务器的请求 - **请求修改**:支持修改发往后端服务器的请求
- **响应修改**:支持修改来自后端服务器的响应 - **响应修改**:支持修改来自后端服务器的响应
@@ -844,82 +835,6 @@ func main() {
- 自动处理压缩请求体 - 自动处理压缩请求体
- 自动添加压缩相关响应头 - 自动添加压缩相关响应头
## 使用插件系统
GoProxy提供了插件系统,支持动态加载和管理插件:
```go
package main
import (
"log"
"net/http"
"github.com/darkit/goproxy/internal/plugin"
"github.com/darkit/goproxy/internal/proxy"
)
func main() {
// 创建插件管理器
pluginManager := plugin.NewPluginManager("./plugins")
// 加载插件
if err := pluginManager.LoadPlugins(); err != nil {
log.Printf("加载插件失败: %v", err)
}
// 创建代理
p := proxy.NewProxy(
proxy.WithPluginManager(pluginManager),
)
// 启动HTTP服务器
log.Println("代理服务器启动在 :8080")
if err := http.ListenAndServe(":8080", p); err != nil {
log.Fatalf("代理服务器启动失败: %v", err)
}
}
```
插件示例:
```go
// plugin.go
package main
import (
"context"
"log"
)
type MyPlugin struct{}
func (p *MyPlugin) Name() string {
return "my-plugin"
}
func (p *MyPlugin) Version() string {
return "1.0.0"
}
func (p *MyPlugin) Init(ctx context.Context) error {
log.Println("初始化插件")
return nil
}
func (p *MyPlugin) Start(ctx context.Context) error {
log.Println("启动插件")
return nil
}
func (p *MyPlugin) Stop(ctx context.Context) error {
log.Println("停止插件")
return nil
}
var Plugin = &MyPlugin{}
```
### 使用认证授权系统 ### 使用认证授权系统
GoProxy提供了完整的认证授权系统,支持JWT认证和基于角色的访问控制: GoProxy提供了完整的认证授权系统,支持JWT认证和基于角色的访问控制:

View File

@@ -137,43 +137,9 @@ GET /api/v1/metrics
} }
``` ```
### 4. 插件管理 ### 4. 缓存管理
#### 4.1 获取插件列表 #### 4.1 获取缓存统计
```
GET /api/v1/plugins
```
响应:
```json
{
"plugins": [
{
"name": "compression",
"version": "1.0.0",
"enabled": true
}
]
}
```
#### 4.2 启用/禁用插件
```
PUT /api/v1/plugins/{plugin_name}/status
```
请求体:
```json
{
"enabled": true
}
```
### 5. 缓存管理
#### 5.1 获取缓存统计
``` ```
GET /api/v1/cache/stats GET /api/v1/cache/stats
@@ -189,15 +155,15 @@ GET /api/v1/cache/stats
} }
``` ```
#### 5.2 清除缓存 #### 4.2 清除缓存
``` ```
DELETE /api/v1/cache DELETE /api/v1/cache
``` ```
### 6. 健康检查 ### 5. 健康检查
#### 6.1 获取健康状态 #### 5.1 获取健康状态
``` ```
GET /api/v1/health GET /api/v1/health
@@ -247,5 +213,4 @@ API版本通过URL路径指定当前版本为v1。
- 初始版本 - 初始版本
- 基本代理功能 - 基本代理功能
- 用户认证 - 用户认证
- 监控指标 - 监控指标
- 插件系统

View File

@@ -1,12 +0,0 @@
#!/bin/bash
# 编译插件
go build -buildmode=plugin -o example.so example.go
# 检查编译结果
if [ $? -eq 0 ]; then
echo "插件编译成功: example.so"
else
echo "插件编译失败"
exit 1
fi

View File

@@ -1,78 +0,0 @@
package main
import (
"context"
"log"
"time"
)
// ExamplePlugin 示例插件
type ExamplePlugin struct {
// 插件配置
config map[string]interface{}
// 插件状态
running bool
}
// Name 插件名称
func (p *ExamplePlugin) Name() string {
return "example"
}
// Version 插件版本
func (p *ExamplePlugin) Version() string {
return "1.0.0"
}
// Init 初始化插件
func (p *ExamplePlugin) Init(ctx context.Context) error {
log.Printf("初始化插件 %s v%s\n", p.Name(), p.Version())
// 初始化配置
p.config = make(map[string]interface{})
p.config["startTime"] = time.Now()
return nil
}
// Start 启动插件
func (p *ExamplePlugin) Start(ctx context.Context) error {
log.Printf("启动插件 %s\n", p.Name())
// 启动后台任务
go func() {
p.running = true
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for p.running {
select {
case <-ctx.Done():
return
case <-ticker.C:
log.Printf("插件 %s 正在运行...\n", p.Name())
}
}
}()
return nil
}
// Stop 停止插件
func (p *ExamplePlugin) Stop(ctx context.Context) error {
log.Printf("停止插件 %s\n", p.Name())
// 停止后台任务
p.running = false
// 清理资源
if startTime, ok := p.config["startTime"].(time.Time); ok {
duration := time.Since(startTime)
log.Printf("插件 %s 运行时长: %v\n", p.Name(), duration)
}
return nil
}
// Plugin 导出插件实例
var Plugin = &ExamplePlugin{}

View File

@@ -196,7 +196,6 @@ func (a *Auth) ValidateToken(tokenString string) (*jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return a.secretKey, nil return a.secretKey, nil
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -69,8 +69,6 @@ type Config struct {
ReverseProxy bool ReverseProxy bool
// 反向代理规则文件路径 // 反向代理规则文件路径
ReverseProxyRulesFile string ReverseProxyRulesFile string
// 是否启用URL重写
EnableURLRewrite bool
// 是否保留客户端IP // 是否保留客户端IP
PreserveClientIP bool PreserveClientIP bool
// 是否启用压缩 // 是否启用压缩
@@ -127,7 +125,6 @@ func DefaultConfig() *Config {
DNSCacheTTL: 5 * time.Minute, DNSCacheTTL: 5 * time.Minute,
ReverseProxy: false, ReverseProxy: false,
ReverseProxyRulesFile: "", ReverseProxyRulesFile: "",
EnableURLRewrite: false,
PreserveClientIP: true, PreserveClientIP: true,
EnableCompression: false, EnableCompression: false,
EnableCORS: false, EnableCORS: false,

View File

@@ -1,162 +0,0 @@
package plugin
import (
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"plugin"
"sync"
)
// Plugin 插件接口
type Plugin interface {
// Name 插件名称
Name() string
// Version 插件版本
Version() string
// Init 初始化插件
Init(ctx context.Context) error
// Start 启动插件
Start(ctx context.Context) error
// Stop 停止插件
Stop(ctx context.Context) error
}
// PluginManager 插件管理器
type PluginManager struct {
pluginsDir string
plugins map[string]Plugin
mu sync.RWMutex
}
// NewPluginManager 创建插件管理器
func NewPluginManager(pluginsDir string) *PluginManager {
return &PluginManager{
pluginsDir: pluginsDir,
plugins: make(map[string]Plugin),
}
}
// LoadPlugins 加载插件
func (pm *PluginManager) LoadPlugins() error {
// 确保插件目录存在
if err := os.MkdirAll(pm.pluginsDir, 0755); err != nil {
return fmt.Errorf("创建插件目录失败: %v", err)
}
// 遍历插件目录
err := filepath.Walk(pm.pluginsDir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
// 跳过目录和非.so文件
if info.IsDir() || filepath.Ext(path) != ".so" {
return nil
}
// 加载插件
if err := pm.loadPlugin(path); err != nil {
fmt.Printf("加载插件 %s 失败: %v\n", path, err)
}
return nil
})
return err
}
// loadPlugin 加载单个插件
func (pm *PluginManager) loadPlugin(path string) error {
// 打开插件文件
p, err := plugin.Open(path)
if err != nil {
return fmt.Errorf("打开插件失败: %v", err)
}
// 查找Plugin变量
symPlugin, err := p.Lookup("Plugin")
if err != nil {
return fmt.Errorf("查找Plugin变量失败: %v", err)
}
// 类型断言
plugin, ok := symPlugin.(Plugin)
if !ok {
return errors.New("插件类型错误")
}
// 检查插件名称是否已存在
pm.mu.Lock()
if _, exists := pm.plugins[plugin.Name()]; exists {
pm.mu.Unlock()
return fmt.Errorf("插件 %s 已存在", plugin.Name())
}
pm.plugins[plugin.Name()] = plugin
pm.mu.Unlock()
return nil
}
// GetPlugin 获取插件
func (pm *PluginManager) GetPlugin(name string) (Plugin, bool) {
pm.mu.RLock()
defer pm.mu.RUnlock()
plugin, exists := pm.plugins[name]
return plugin, exists
}
// GetAllPlugins 获取所有插件
func (pm *PluginManager) GetAllPlugins() []Plugin {
pm.mu.RLock()
defer pm.mu.RUnlock()
plugins := make([]Plugin, 0, len(pm.plugins))
for _, plugin := range pm.plugins {
plugins = append(plugins, plugin)
}
return plugins
}
// InitPlugins 初始化所有插件
func (pm *PluginManager) InitPlugins(ctx context.Context) error {
pm.mu.RLock()
defer pm.mu.RUnlock()
for name, plugin := range pm.plugins {
if err := plugin.Init(ctx); err != nil {
return fmt.Errorf("初始化插件 %s 失败: %v", name, err)
}
}
return nil
}
// StartPlugins 启动所有插件
func (pm *PluginManager) StartPlugins(ctx context.Context) error {
pm.mu.RLock()
defer pm.mu.RUnlock()
for name, plugin := range pm.plugins {
if err := plugin.Start(ctx); err != nil {
return fmt.Errorf("启动插件 %s 失败: %v", name, err)
}
}
return nil
}
// StopPlugins 停止所有插件
func (pm *PluginManager) StopPlugins(ctx context.Context) error {
pm.mu.RLock()
defer pm.mu.RUnlock()
for name, plugin := range pm.plugins {
if err := plugin.Stop(ctx); err != nil {
return fmt.Errorf("停止插件 %s 失败: %v", name, err)
}
}
return nil
}

View File

@@ -472,7 +472,7 @@ func SaveCertificateToFile(cert *x509.Certificate, key *ecdsa.PrivateKey, certFi
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: cert.Raw, Bytes: cert.Raw,
}) })
if err := os.WriteFile(certFile, certPEM, 0644); err != nil { if err := os.WriteFile(certFile, certPEM, 0o644); err != nil {
return fmt.Errorf("保存证书到文件失败: %s", err) return fmt.Errorf("保存证书到文件失败: %s", err)
} }
@@ -485,7 +485,7 @@ func SaveCertificateToFile(cert *x509.Certificate, key *ecdsa.PrivateKey, certFi
Type: "EC PRIVATE KEY", Type: "EC PRIVATE KEY",
Bytes: keyBytes, Bytes: keyBytes,
}) })
if err := os.WriteFile(keyFile, keyPEM, 0600); err != nil { if err := os.WriteFile(keyFile, keyPEM, 0o600); err != nil {
return fmt.Errorf("保存私钥到文件失败: %s", err) return fmt.Errorf("保存私钥到文件失败: %s", err)
} }

View File

@@ -209,16 +209,6 @@ func WithDNSCacheTTL(ttl time.Duration) Option {
} }
} }
// WithURLRewrite 启用URL重写
func WithURLRewrite(enable bool) Option {
return func(opt *Options) {
if opt.Config == nil {
opt.Config = config.DefaultConfig()
}
opt.Config.EnableURLRewrite = enable
}
}
// WithEnableCORS 启用CORS支持 // WithEnableCORS 启用CORS支持
func WithEnableCORS(enable bool) Option { func WithEnableCORS(enable bool) Option {
return func(opt *Options) { return func(opt *Options) {

View File

@@ -25,7 +25,6 @@ import (
"github.com/darkit/goproxy/internal/loadbalance" "github.com/darkit/goproxy/internal/loadbalance"
"github.com/darkit/goproxy/internal/metrics" "github.com/darkit/goproxy/internal/metrics"
"github.com/darkit/goproxy/internal/middleware" "github.com/darkit/goproxy/internal/middleware"
"github.com/darkit/goproxy/internal/plugin"
"github.com/darkit/goproxy/internal/reverse" "github.com/darkit/goproxy/internal/reverse"
"github.com/ouqiang/websocket" "github.com/ouqiang/websocket"
"github.com/viki-org/dnscache" "github.com/viki-org/dnscache"
@@ -204,8 +203,6 @@ type Options struct {
ClientTrace *httptrace.ClientTrace ClientTrace *httptrace.ClientTrace
// 认证系统 // 认证系统
Auth *auth.Auth Auth *auth.Auth
// 插件管理器
PluginManager *plugin.PluginManager
// 证书管理器 // 证书管理器
CertManager *CertManager CertManager *CertManager
} }
@@ -217,13 +214,6 @@ func WithAuth(auth *auth.Auth) Option {
} }
} }
// WithPluginManager 设置插件管理器
func WithPluginManager(pluginManager *plugin.PluginManager) Option {
return func(o *Options) {
o.PluginManager = pluginManager
}
}
// Proxy HTTP代理 // Proxy HTTP代理
type Proxy struct { type Proxy struct {
// 配置 // 配置
@@ -499,7 +489,6 @@ func (p *Proxy) handleHTTP(ctx *Context, rw http.ResponseWriter) {
// 发送请求 // 发送请求
var err error var err error
resp, err = p.httpTransport.RoundTrip(req) resp, err = p.httpTransport.RoundTrip(req)
// 处理错误 // 处理错误
if err != nil { if err != nil {
p.delegate.BeforeResponse(ctx, nil, err) p.delegate.BeforeResponse(ctx, nil, err)

View File

@@ -1,98 +0,0 @@
package rewriter
import (
"net/http"
"regexp"
"strings"
)
// Rewriter URL重写器
// 用于在反向代理中重写请求URL
type Rewriter struct {
// 重写规则列表
rules []*RewriteRule
}
// RewriteRule 重写规则
type RewriteRule struct {
// 匹配模式
Pattern string
// 替换模式
Replacement string
// 是否使用正则表达式
UseRegex bool
// 编译后的正则表达式
regex *regexp.Regexp
}
// NewRewriter 创建URL重写器
func NewRewriter() *Rewriter {
return &Rewriter{
rules: make([]*RewriteRule, 0),
}
}
// AddRule 添加重写规则
func (r *Rewriter) AddRule(pattern, replacement string, useRegex bool) error {
rule := &RewriteRule{
Pattern: pattern,
Replacement: replacement,
UseRegex: useRegex,
}
if useRegex {
regex, err := regexp.Compile(pattern)
if err != nil {
return err
}
rule.regex = regex
}
r.rules = append(r.rules, rule)
return nil
}
// Rewrite 重写URL
func (r *Rewriter) Rewrite(req *http.Request) {
path := req.URL.Path
for _, rule := range r.rules {
if rule.UseRegex {
if rule.regex.MatchString(path) {
req.URL.Path = rule.regex.ReplaceAllString(path, rule.Replacement)
break
}
} else {
if strings.HasPrefix(path, rule.Pattern) {
req.URL.Path = strings.Replace(path, rule.Pattern, rule.Replacement, 1)
break
}
}
}
}
// RewriteResponse 重写响应
// 主要用于处理响应中的Location头和内容中的URL
func (r *Rewriter) RewriteResponse(resp *http.Response, originHost string) {
// 处理重定向头
location := resp.Header.Get("Location")
if location != "" {
// 将后端服务器的域名替换成代理服务器的域名
for _, rule := range r.rules {
if rule.UseRegex && rule.regex != nil {
if rule.regex.MatchString(location) {
newLocation := rule.regex.ReplaceAllString(location, rule.Replacement)
resp.Header.Set("Location", newLocation)
break
}
}
}
}
}
// LoadRulesFromFile 从文件加载重写规则
func (r *Rewriter) LoadRulesFromFile(filename string) error {
// 实现从配置文件加载规则的逻辑
// 这里省略实现细节
return nil
}