feat: 基本功能
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
builds
|
||||||
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# AnyProxy 简介
|
||||||
|
|
||||||
|
AnyProxy 是一个简单的 HTTP/HTTPS 代理服务器。它可以帮助你转发和代理请求。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. **直接协议路径**
|
||||||
|
- 目标URL: `https://example.com/path`
|
||||||
|
代理URL: `http://AnyproxyIP/https/example.com/path`
|
||||||
|
- 目标URL: `http://example.com/path`
|
||||||
|
代理URL: `http://AnyproxyIP/http/example.com/path`
|
||||||
|
|
||||||
|
2. **完整URL路径**
|
||||||
|
- 目标URL: `https://example.com`
|
||||||
|
代理URL: `http://AnyproxyIP/proxy/https://example.com`
|
||||||
|
|
||||||
|
> 目标URL 必须以 `https://` 或 `http://` 开头。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
1. 下载对应平台的二进制Relase文件
|
||||||
|
2. 运行二进制文件
|
||||||
|
3. (可选) 配置为系统服务
|
||||||
|
|
||||||
|
系统服务参考(Systemd)
|
||||||
|
~~~ ini
|
||||||
|
# /etc/systemd/system/anyproxy.service
|
||||||
|
[Unit]
|
||||||
|
Description=AnyProxy Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/opt/anyproxy/anyproxy
|
||||||
|
WorkingDirectory=/opt/anyproxy
|
||||||
|
Restart=always
|
||||||
|
User=root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
~~~
|
||||||
72
autobuild.ps1
Normal file
72
autobuild.ps1
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 设置程序名称
|
||||||
|
$appName = "anyproxy"
|
||||||
|
|
||||||
|
# 创建构建目录
|
||||||
|
New-Item -ItemType Directory -Force -Path "builds" | Out-Null
|
||||||
|
|
||||||
|
# 清理旧构建文件
|
||||||
|
Remove-Item -Path "builds\*" -Force -Recurse -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
# 定义目标平台
|
||||||
|
$platforms = @(
|
||||||
|
@{ OS = "linux"; Arch = "amd64" },
|
||||||
|
@{ OS = "linux"; Arch = "arm64" },
|
||||||
|
@{ OS = "windows"; Arch = "amd64" },
|
||||||
|
@{ OS = "windows"; Arch = "arm64" },
|
||||||
|
@{ OS = "darwin"; Arch = "amd64" },
|
||||||
|
@{ OS = "darwin"; Arch = "arm64" }
|
||||||
|
)
|
||||||
|
|
||||||
|
# 交叉编译函数
|
||||||
|
function Build-Target {
|
||||||
|
param (
|
||||||
|
[string]$os,
|
||||||
|
[string]$arch
|
||||||
|
)
|
||||||
|
|
||||||
|
$output = "builds\$appName-$os-$arch"
|
||||||
|
|
||||||
|
# Windows 平台添加 .exe 后缀
|
||||||
|
if ($os -eq "windows") {
|
||||||
|
$output += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Building for $os/$arch..." -ForegroundColor Yellow
|
||||||
|
|
||||||
|
# 设置环境变量并编译
|
||||||
|
$env:GOOS = $os
|
||||||
|
$env:GOARCH = $arch
|
||||||
|
go build -ldflags="-s -w" -o $output .
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "✓ Success: $output" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "✗ Failed: $os/$arch" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 遍历所有平台进行编译
|
||||||
|
foreach ($platform in $platforms) {
|
||||||
|
Build-Target -os $platform.OS -arch $platform.Arch
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成校验和文件
|
||||||
|
Write-Host "Generating checksums..." -ForegroundColor Yellow
|
||||||
|
Set-Location -Path "builds"
|
||||||
|
$checksumFile = "checksums.txt"
|
||||||
|
$files = Get-ChildItem -Exclude $checksumFile
|
||||||
|
|
||||||
|
# 清空旧的校验和文件
|
||||||
|
if (Test-Path $checksumFile) {
|
||||||
|
Clear-Content $checksumFile
|
||||||
|
}
|
||||||
|
|
||||||
|
# 为每个文件生成SHA256校验和
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$hash = Get-FileHash -Path $file.FullName -Algorithm SHA256
|
||||||
|
"$($hash.Hash.ToLower()) $($file.Name)" | Out-File -FilePath $checksumFile -Append -Encoding UTF8
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "✓ Checksums generated: builds\$checksumFile" -ForegroundColor Green
|
||||||
|
Set-Location -Path ".."
|
||||||
|
Write-Host "Build complete! Files are in the 'builds' directory." -ForegroundColor Cyan
|
||||||
33
go.mod
Normal file
33
go.mod
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
module anyproxy
|
||||||
|
|
||||||
|
go 1.24.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.10.1 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
|
golang.org/x/arch v0.19.0 // indirect
|
||||||
|
golang.org/x/crypto v0.40.0 // indirect
|
||||||
|
golang.org/x/net v0.42.0 // indirect
|
||||||
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
|
golang.org/x/text v0.27.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
74
go.sum
Normal file
74
go.sum
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||||
|
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
|
||||||
|
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
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