feat: 基本功能

This commit is contained in:
2025-08-05 12:18:18 +08:00
commit d8c5017594
6 changed files with 386 additions and 0 deletions

166
main.go Normal file
View File

@@ -0,0 +1,166 @@
package main
import (
"flag"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"sync/atomic"
"github.com/gin-gonic/gin"
)
// 全局请求计数器,使用原子操作确保线程安全
var requestCounter int64
func main() {
flag.Parse()
port := flag.Int("port", 8080, "代理服务器监听的端口")
debug := flag.Bool("debug", false, "是否启用调试模式")
if *debug {
gin.SetMode(gin.DebugMode) // 启用调试模式
} else {
gin.SetMode(gin.ReleaseMode) // 在调试时暂时注释掉
}
r := gin.Default()
// 处理根路径
r.GET("/", HelloPage)
// 使用 "catch-all" 路由来捕获所有代理请求
// 这里我们使用 /proxy/* 前缀来避免与根路径冲突
r.Any("/proxy/*proxyPath", proxyHandler)
// 为了保持向后兼容我们也可以处理直接的URL请求
// 检查是否以协议开头的路径
r.Any("/:protocol/*remainder", protocolHandler)
fmt.Printf("HTTP 代理服务器启动,监听端口 :%d\n", *port)
if err := r.Run(fmt.Sprintf(":%d", *port)); err != nil {
fmt.Printf("启动服务器失败: %v\n", err)
}
}
// normalizeURL 规范化URL格式处理缺少斜杠的情况
func normalizeURL(rawURL string) string {
// 处理 https:/example.com 或 http:/example.com 的情况
if strings.HasPrefix(rawURL, "https:/") && !strings.HasPrefix(rawURL, "https://") {
return strings.Replace(rawURL, "https:/", "https://", 1)
}
if strings.HasPrefix(rawURL, "http:/") && !strings.HasPrefix(rawURL, "http://") {
return strings.Replace(rawURL, "http:/", "http://", 1)
}
return rawURL
}
func proxyHandler(c *gin.Context) {
// 从路径参数中获取目标 URL
targetURLStr := c.Param("proxyPath")
// 移除前导斜杠
targetURLStr = strings.TrimPrefix(targetURLStr, "/")
// 规范化URL格式
targetURLStr = normalizeURL(targetURLStr)
// 检查 URL 合法性
if _, err := url.ParseRequestURI(targetURLStr); err != nil {
c.String(http.StatusBadRequest, "无效的目标 URL: %v", err)
return
}
// 执行代理请求
executeProxy(c, targetURLStr)
}
// protocolHandler 处理直接以协议开头的URL请求 (如 /https/example.com/path)
func protocolHandler(c *gin.Context) {
protocol := c.Param("protocol")
remainder := c.Param("remainder")
// 只处理 http 和 https 协议
if protocol != "http" && protocol != "https" {
c.String(http.StatusBadRequest, "不支持的协议: %s", protocol)
return
}
// 构建完整的URL
targetURLStr := protocol + ":/" + remainder
// 规范化URL格式
targetURLStr = normalizeURL(targetURLStr)
// 检查 URL 合法性
if _, err := url.ParseRequestURI(targetURLStr); err != nil {
c.String(http.StatusBadRequest, "无效的目标 URL: %v", err)
return
}
// 执行代理请求
executeProxy(c, targetURLStr)
}
// executeProxy 执行实际的代理请求
func executeProxy(c *gin.Context, targetURLStr string) {
// 增加请求计数器
atomic.AddInt64(&requestCounter, 1)
// 创建到目标服务器的请求
// 注意:我们直接将原始请求的 Body 传递过去
proxyReq, err := http.NewRequest(c.Request.Method, targetURLStr, c.Request.Body)
if err != nil {
c.String(http.StatusInternalServerError, "创建代理请求失败: %v", err)
return
}
// 复制原始请求的 Headers
proxyReq.Header = c.Request.Header
// 发送代理请求
client := &http.Client{}
resp, err := client.Do(proxyReq)
if err != nil {
c.String(http.StatusBadGateway, "请求目标服务器失败: %v", err)
return
}
defer resp.Body.Close()
// 复制目标服务器响应的 Headers 到原始响应
for key, values := range resp.Header {
for _, value := range values {
c.Header(key, value)
}
}
// 将目标服务器的响应状态码设置到原始响应
c.Status(resp.StatusCode)
// 将目标服务器的响应 Body 直接流式传输到客户端
// 使用 io.Copy 更高效,并能处理各种编码(如 chunked
_, err = io.Copy(c.Writer, resp.Body)
if err != nil {
// 如果在写入 body 时发生错误,记录下来
fmt.Printf("写入响应 Body 时出错: %v\n", err)
}
}
func HelloPage(c *gin.Context) {
// 获取当前的请求计数
count := atomic.LoadInt64(&requestCounter)
str := fmt.Sprintf("AnyProxy 服务器正在运行... 已转发 %d 个请求", count)
str += "\n\n使用方法:\n"
str += "方式1 - 直接协议路径: \n"
str += " 目标URL: https://example.com/path --> 代理URL: http://AnyproxyIP/https/example.com/path\n"
str += " 目标URL: http://example.com/path --> 代理URL: http://AnyproxyIP/http/example.com/path\n\n"
str += "方式2 - 完整URL路径: \n"
str += " 目标URL: https://example.com --> 代理URL: http://AnyproxyIP/proxy/https://example.com\n\n"
str += "目标URL必须以 https:// 或 http:// 开头。\n\n"
c.String(200, str)
}