支持shell脚本嵌套代理

This commit is contained in:
NewName
2025-05-18 01:01:42 +00:00
parent 590095eaa4
commit 61cf7ceddd
2 changed files with 206 additions and 2 deletions

View File

@@ -195,8 +195,36 @@ func proxy(c *gin.Context, u string) {
}
c.Status(resp.StatusCode)
if _, err := io.Copy(c.Writer, resp.Body); err != nil {
return
// 检查是否为.sh文件
isShellFile := strings.HasSuffix(strings.ToLower(u), ".sh")
isCompressed := resp.Header.Get("Content-Encoding") == "gzip"
if isShellFile {
// 获取真实域名
realHost := c.Request.Header.Get("X-Forwarded-Host")
if realHost == "" {
realHost = c.Request.Host
}
// 如果域名中没有协议前缀添加https://
if !strings.HasPrefix(realHost, "http://") && !strings.HasPrefix(realHost, "https://") {
realHost = "https://" + realHost
}
// 使用ProcessGitHubURLs处理.sh文件
processedBody, _, err := ProcessGitHubURLs(resp.Body, isCompressed, realHost, true)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("处理shell文件时发生错误: %v", err))
return
}
if _, err := io.Copy(c.Writer, processedBody); err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("写入响应时发生错误: %v", err))
return
}
} else {
// 对于非.sh文件直接复制响应体
if _, err := io.Copy(c.Writer, resp.Body); err != nil {
return
}
}
}

176
ghproxy/proxysh.go Normal file
View File

@@ -0,0 +1,176 @@
package main
import (
"bufio"
"compress/gzip"
"fmt"
"io"
"regexp"
"strings"
)
var (
// gitHubDomains 定义所有支持的GitHub相关域名
gitHubDomains = []string{
"github.com",
"raw.githubusercontent.com",
"raw.github.com",
"gist.githubusercontent.com",
"gist.github.com",
"api.github.com",
}
// urlPattern 使用gitHubDomains构建正则表达式
urlPattern = regexp.MustCompile(`https?://(?:` + strings.Join(gitHubDomains, "|") + `)[^\s'"]+`)
// 是否启用脚本嵌套代理的调试日志
DebugLog = true
)
// 打印调试日志的辅助函数
func debugPrintf(format string, args ...interface{}) {
if DebugLog {
fmt.Printf(format, args...)
}
}
// ProcessGitHubURLs 处理数据流中的GitHub URL将其替换为代理URL
// 参数:
// - input: 输入数据流
// - isCompressed: 是否为gzip压缩数据
// - host: 代理服务器域名
// - isShellFile: 是否为.sh文件 (如果为true则会处理其中的GitHub URL)
//
// 返回:
// - io.Reader: 处理后的数据流
// - int64: 写入的字节数
// - error: 错误信息
func ProcessGitHubURLs(input io.ReadCloser, isCompressed bool, host string, isShellFile bool) (io.Reader, int64, error) {
debugPrintf("开始处理文件: isCompressed=%v, host=%s, isShellFile=%v\n", isCompressed, host, isShellFile)
if !isShellFile {
debugPrintf("非shell文件跳过处理\n")
return input, 0, nil
}
pipeReader, pipeWriter := io.Pipe()
var written int64
go func() {
var err error
defer func() {
if err != nil {
debugPrintf("处理过程中发生错误: %v\n", err)
_ = pipeWriter.CloseWithError(err)
} else {
_ = pipeWriter.Close()
}
}()
defer input.Close()
reader := input
if isCompressed {
debugPrintf("检测到压缩文件,进行解压处理\n")
gzipReader, gzipErr := gzip.NewReader(input)
if gzipErr != nil {
err = gzipErr
return
}
defer gzipReader.Close()
reader = gzipReader
}
bufReader := bufio.NewReader(reader)
var bufWriter *bufio.Writer
if isCompressed {
gzipWriter := gzip.NewWriter(pipeWriter)
defer gzipWriter.Close()
bufWriter = bufio.NewWriterSize(gzipWriter, 4096)
} else {
bufWriter = bufio.NewWriterSize(pipeWriter, 4096)
}
defer bufWriter.Flush()
written, err = processContent(bufReader, bufWriter, host)
debugPrintf("文件处理完成,共处理 %d 字节\n", written)
}()
return pipeReader, written, nil
}
// processContent 处理文件内容,返回处理的字节数
func processContent(reader *bufio.Reader, writer *bufio.Writer, host string) (int64, error) {
var written int64
lineNum := 0
for {
lineNum++
line, err := reader.ReadString('\n')
if err != nil && err != io.EOF {
return written, err
}
if line != "" {
// 在处理前先检查是否包含GitHub URL
matches := urlPattern.FindAllString(line, -1)
if len(matches) > 0 {
debugPrintf("\n在第 %d 行发现 %d 个GitHub URL:\n", lineNum, len(matches))
for _, match := range matches {
debugPrintf("原始URL: %s\n", match)
}
}
modifiedLine := processLine(line, host, lineNum)
n, writeErr := writer.WriteString(modifiedLine)
written += int64(n)
if writeErr != nil {
return written, writeErr
}
}
if err == io.EOF {
break
}
}
return written, nil
}
// processLine 处理单行文本替换所有匹配的GitHub URL
func processLine(line string, host string, lineNum int) string {
return urlPattern.ReplaceAllStringFunc(line, func(url string) string {
newURL := modifyGitHubURL(url, host)
if newURL != url {
debugPrintf("第 %d 行URL替换:\n 原始: %s\n 替换后: %s\n", lineNum, url, newURL)
}
return newURL
})
}
// modifyGitHubURL 修改GitHub URL添加代理域名前缀
func modifyGitHubURL(url string, host string) string {
for _, domain := range gitHubDomains {
hasHttps := strings.HasPrefix(url, "https://"+domain)
hasHttp := strings.HasPrefix(url, "http://"+domain)
if hasHttps || hasHttp || strings.HasPrefix(url, domain) {
if !hasHttps && !hasHttp {
url = "https://" + url
}
if hasHttp {
url = "https://" + strings.TrimPrefix(url, "http://")
}
// 移除host开头的协议头如果有
host = strings.TrimPrefix(host, "https://")
host = strings.TrimPrefix(host, "http://")
// 返回组合后的URL
return host + "/" + url
}
}
return url
}
// IsShellFile 检查文件是否为shell文件基于文件名
func IsShellFile(filename string) bool {
return strings.HasSuffix(filename, ".sh")
}