名词解释
正向代理
为了简单、形象方便理解,可以简单粗暴的理解,将代理方式放在客户端(pc/phone)上的代理为正向代理。访问数据的链路有端上通过隧道(tunnel)与Target进行通信交互。
典型应用:
chrome via socks5
某火箭
反向代理
与正向代理相反,将代理节点后置,放在服务端上实现。典型应用:Nginx
其中正向代理使用不当就是不合规的问题。反向代理+限定target站点的访问实现来说合规问题风险会稍微小一点。这里重点讨论反向代理,对正向代理的内容不做过多赘述。
Proxy的选型
服务端环境的选择
vps 不推荐,原因:繁琐
云函数:
腾讯云 倾向选择
阿里云 备用选择
实现语言的选择
Nodejs
Golang
代码参考
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
func setupCORS(r *gin.Engine) {
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
c.Next()
})
}
func filterHeaders(r *http.Request) {
for key := range r.Header {
if strings.Contains(key, "X-Scf") {
r.Header.Del(key)
}
}
}
func main() {
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
setupCORS(r)
// 目标服务器地址
target := "https://api.anthropic.com"
targetURL, err := url.Parse(target)
if err != nil {
panic(err) // 如果 URL 解析有误,则终止程序
}
//proxy := goproxy.NewProxyHttpServer()
r.Any("/*proxyPath", func(c *gin.Context) {
proxyURL := targetURL.ResolveReference(c.Request.URL)
proxyReq, err := http.NewRequest(c.Request.Method, proxyURL.String(), c.Request.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
filterHeaders(c.Request)
originAuth := c.Request.Header.Get("x-api-key")
theIp := c.ClientIP()
if strings.Contains(originAuth, "sk-xx") || strings.Contains(originAuth, "sk-xx2") ||
theIp == "ip01" || theIp == "127.0.0.1" || theIp == "ip02" {
c.Request.Header.Set("x-api-key", "claude or openai key here")
c.Request.Header.Del("origin")
}
proxyReq.Header = c.Request.Header
// 打印请求内容
dump, err := httputil.DumpRequest(proxyReq, true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
fmt.Println("---请求body--- \\n")
fmt.Println(string(dump))
proxyRes, err := http.DefaultClient.Do(proxyReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 复制所有响应头
for k, values := range proxyRes.Header {
for _, v := range values {
c.Writer.Header().Add(k, v)
}
}
c.Writer.WriteHeader(proxyRes.StatusCode)
// 将响应体复制到客户端响应中
_, err = io.Copy(c.Writer, proxyRes.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy proxy response"})
return
}
})
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
func setupCORS(r *gin.Engine) {
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusOK)
return
}
c.Next()
})
}
func filterHeaders(r *http.Request) {
for key := range r.Header {
if strings.Contains(key, "X-Scf") {
r.Header.Del(key)
}
}
}
func main() {
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
setupCORS(r)
// 目标服务器地址
target := "https://api.anthropic.com"
targetURL, err := url.Parse(target)
if err != nil {
panic(err) // 如果 URL 解析有误,则终止程序
}
//proxy := goproxy.NewProxyHttpServer()
r.Any("/*proxyPath", func(c *gin.Context) {
proxyURL := targetURL.ResolveReference(c.Request.URL)
proxyReq, err := http.NewRequest(c.Request.Method, proxyURL.String(), c.Request.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
filterHeaders(c.Request)
originAuth := c.Request.Header.Get("x-api-key")
theIp := c.ClientIP()
if strings.Contains(originAuth, "sk-xx") || strings.Contains(originAuth, "sk-xx01") ||
theIp == "ip01" || theIp == "127.0.0.1" || theIp == "ip02" {
c.Request.Header.Set("x-api-key", "claude or openai key here")
c.Request.Header.Del("origin")
}
proxyReq.Header = c.Request.Header
// 打印请求内容
dump, err := httputil.DumpRequest(proxyReq, true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
fmt.Println("---请求body--- \\n")
fmt.Println(string(dump))
proxyRes, err := http.DefaultClient.Do(proxyReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 复制所有响应头
for k, values := range proxyRes.Header {
for _, v := range values {
c.Writer.Header().Add(k, v)
}
}
c.Writer.WriteHeader(proxyRes.StatusCode)
// 将响应体复制到客户端响应中
_, err = io.Copy(c.Writer, proxyRes.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy proxy response"})
return
}
})
r.Run(":9000") // 启动服务器在 9000 端口
}
r.Run(":9000") // 启动服务器在 9000 端口
}
# build.sh
GOOS=linux GOARCH=amd64 go build -o main main.go
zip main.zip main scf_bootstrap
#!/bin/bash
#scf_bootstrap
./main
腾讯面板
选择go模版,上传代码选择zip包上传即可
注意事项
初始化、timeout的时间需要勾选
域名配置
触发器里面升级为api网关,再到网关里面配置域名