feat: 基本功能
This commit is contained in:
166
main.go
Normal file
166
main.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user