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。 // 此功能借鉴了https://github.com/WJQSERVER-STUDIO/ghproxy,版权归`WJQSERVER-STUDIO`所有。 // 参数: // - 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() var reader io.Reader = input if isCompressed { debugPrintf("检测到压缩文件,进行解压处理\n") gzipReader, gzipErr := gzip.NewReader(input) if gzipErr != nil { err = gzipErr return } defer gzipReader.Close() reader = gzipReader } // 使用更大的缓冲区 bufReader := bufio.NewReaderSize(reader, 32*1024) // 32KB buffer var writer io.Writer = pipeWriter if isCompressed { gzipWriter := gzip.NewWriter(writer) defer gzipWriter.Close() writer = gzipWriter } bufWriter := bufio.NewWriterSize(writer, 32*1024) // 32KB buffer defer bufWriter.Flush() written, err = processContent(bufReader, bufWriter, host) if err != nil { debugPrintf("处理内容时发生错误: %v\n", err) return } 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, fmt.Errorf("读取行时发生错误: %w", err) } if line != "" { // 在处理前先检查是否包含GitHub URL if strings.Contains(line, "github.com") || strings.Contains(line, "raw.githubusercontent.com") { 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) if writeErr != nil { return written, fmt.Errorf("写入修改后的行时发生错误: %w", writeErr) } written += int64(n) } else { // 如果行中没有GitHub URL,直接写入 n, writeErr := writer.WriteString(line) if writeErr != nil { return written, fmt.Errorf("写入原始行时发生错误: %w", writeErr) } written += int64(n) } } if err == io.EOF { break } } // 确保所有数据都被写入 if err := writer.Flush(); err != nil { return written, fmt.Errorf("刷新缓冲区时发生错误: %w", err) } 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") }