ChatGPT代理(国内访问不通)
使用golang 实现请求转发代理
- 加密请求,满足条件才能继续请求,避免流量被滥用
- token配置到后端,避免token暴露风险
package main
import (
"bytes"
"context"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/url"
"strings"
"time"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var (
target = "https://api.openai.com" // 目标域名
)
func main() {
// fc.StartHttp(handleRequest)
http.HandleFunc("/", handleRequest)
http.ListenAndServe(":9000", nil)
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
// 过滤无效URL
_, err := url.Parse(r.URL.String())
ctx := r.Context()
if err != nil {
g.Log().Infof(ctx, "Error parsing URL: ", err.Error())
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
ts := gconv.Int64(r.Header.Get("proxy-ts"))
nowTs := time.Now().Unix()
mistake := g.Config().MustGet(context.Background(), "mistake").Int64()
if (nowTs-ts < 0 && ts-nowTs > mistake) || nowTs-ts > mistake {
g.Log().Infof(ctx, "timestamp err")
http.Error(w, "Error creating proxy request", http.StatusForbidden)
return
}
sign := r.Header.Get("proxy-sign")
data, err := io.ReadAll(r.Body)
if err != nil {
g.Log().Infof(ctx, "read body err")
http.Error(w, "Error creating proxy request", http.StatusForbidden)
return
}
r.Body.Close() // NOTE 原始的 Body 无需手动关闭,会在 response.reqBody中自动关闭的.
br := bytes.NewReader(data)
r.Body = io.NopCloser(br)
// 计算服务端sign 比较是否匹配
signC := sign(r)
if sign != signC {
g.Log().Infof(ctx, "sign err")
http.Error(w, "Error creating proxy request", http.StatusForbidden)
return
}
r.Header.Del("proxy-ts")
r.Header.Del("proxy-sign")
tokens := g.Config().MustGet(context.Background(), "tokens").Strings()
token := tokens[rand.Intn(len(tokens))]
r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
// 去掉环境前缀(针对腾讯云,如果包含的话,目前我只用到了test和release)
newPath := strings.Replace(r.URL.Path, "/release", "", 1)
newPath = strings.Replace(newPath, "/test", "", 1)
newPath = strings.Replace(newPath, "/openai-proxy", "", 1)
// 拼接目标URL
targetURL := target + newPath
// 创建代理HTTP请求
proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body)
if err != nil {
g.Log().Infof(ctx, "sign err: %v", err)
http.Error(w, "Error creating proxy request", http.StatusInternalServerError)
return
}
// 将原始请求头复制到新请求中
for headerKey, headerValues := range r.Header {
for _, headerValue := range headerValues {
proxyReq.Header.Add(headerKey, headerValue)
}
}
// 默认超时时间设置为60s
client := &http.Client{
Timeout: 60 * time.Second,
}
// 向 OpenAI 发起代理请求
resp, err := client.Do(proxyReq)
if err != nil {
log.Println("Error sending proxy request: ", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 将响应头复制到代理响应头中
for key, values := range resp.Header {
for _, value := range values {
w.Header().Add(key, value)
}
}
// 将响应状态码设置为原始响应状态码
w.WriteHeader(resp.StatusCode)
// 将响应实体写入到响应流中(支持流式响应)
buf := make([]byte, 1024)
for {
if n, err := resp.Body.Read(buf); err == io.EOF || n == 0 {
return
} else if err != nil {
log.Println("error while reading respbody: ", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else {
if _, err = w.Write(buf[:n]); err != nil {
log.Println("error while writing resp: ", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.(http.Flusher).Flush()
}
}
}
解决国内访问不通
- 使用香港机器(有概率被封,推荐搭配一个国内服务器作为中转,屏蔽其他ip)
- 使用云函数,一般云函数提供的域名不会被封,选择阿里云或者腾讯云搭建,推荐在香港地区创建云函数 云函数
ChatGPT账号
- 建议自行获取,就不做广告了
- 可以尝试azure提供的open-ai,不限制访问