diff --git a/CHANGELOG.md b/CHANGELOG.md index ff99f7b..7eb963c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,5 @@ # 更新日志 -4.2.5 - 2025-07-31 ---- -- CHANGE: 进一步完善匹配器, 兼容更多情况 - 4.2.4 - 2025-07-29 --- - CHANGE: 改进匹配器, 防止匹配不应匹配的内容 diff --git a/VERSION b/VERSION index ad35fe0..cf78d5b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.5 \ No newline at end of file +4.2.4 diff --git a/main.go b/main.go index 3313829..269727a 100644 --- a/main.go +++ b/main.go @@ -399,11 +399,6 @@ func main() { proxy.RoutingHandler(cfg)(c) }) - r.GET("/github.com/:user/:repo/releases/:tag/download/*filepath", func(c *touka.Context) { - c.Set("matcher", "releases") - proxy.RoutingHandler(cfg)(c) - }) - r.GET("/github.com/:user/:repo/archive/*filepath", func(c *touka.Context) { c.Set("matcher", "releases") proxy.RoutingHandler(cfg)(c) diff --git a/proxy/match.go b/proxy/match.go index 9a37f0a..3f5f87f 100644 --- a/proxy/match.go +++ b/proxy/match.go @@ -42,62 +42,37 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHPro // 匹配 "https://github.com/" if strings.HasPrefix(rawPath, githubPrefix) { - pathAfterDomain := rawPath[githubPrefixLen:] - - // 解析 user - i := strings.IndexByte(pathAfterDomain, '/') + remaining := rawPath[githubPrefixLen:] + i := strings.IndexByte(remaining, '/') if i <= 0 { return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing user") } - user := pathAfterDomain[:i] - pathAfterUser := pathAfterDomain[i+1:] - - // 解析 repo - i = strings.IndexByte(pathAfterUser, '/') + user := remaining[:i] + remaining = remaining[i+1:] + i = strings.IndexByte(remaining, '/') if i <= 0 { + return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing repo") + } + repo := remaining[:i] + remaining = remaining[i+1:] + if len(remaining) == 0 { return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing action") } - repo := pathAfterUser[:i] - pathAfterRepo := pathAfterUser[i+1:] - - if len(pathAfterRepo) == 0 { - return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing action") - } - - // 优先处理所有 "releases" 相关的下载路径 - if strings.HasPrefix(pathAfterRepo, "releases/") { - // 情况 A: "releases/download/..." - if strings.HasPrefix(pathAfterRepo, "releases/download/") { - return user, repo, "releases", nil - } - // 情况 B: "releases/:tag/download/..." - pathAfterReleases := pathAfterRepo[len("releases/"):] - slashIndex := strings.IndexByte(pathAfterReleases, '/') - if slashIndex > 0 { // 确保tag不为空 - pathAfterTag := pathAfterReleases[slashIndex+1:] - if strings.HasPrefix(pathAfterTag, "download/") { - return user, repo, "releases", nil - } - } - // 如果不满足上述下载链接的结构, 则为网页浏览路径, 予以拒绝 - return "", "", "", NewErrorWithStatusLookup(400, "unsupported releases page, only download links are allowed") - } - - // 检查 "archive/" 路径 - if strings.HasPrefix(pathAfterRepo, "archive/") { - // 根据测试用例, archive路径的matcher也应为releases - return user, repo, "releases", nil - } - - // 如果不是下载路径, 则解析action并进行分类 - i = strings.IndexByte(pathAfterRepo, '/') - action := pathAfterRepo + i = strings.IndexByte(remaining, '/') + action := remaining if i != -1 { - action = pathAfterRepo[:i] + action = remaining[:i] } - var matcher string switch action { + case "releases": + if strings.HasPrefix(remaining, releasesDownloadSnippet) { + matcher = "releases" + } else { + return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: not a releases download url") + } + case "archive": + matcher = "releases" case "blob": matcher = "blob" case "raw": @@ -113,27 +88,59 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHPro // 匹配 "https://raw.githubusercontent.com/" if strings.HasPrefix(rawPath, rawPrefix) { remaining := rawPath[rawPrefixLen:] - parts := strings.SplitN(remaining, "/", 3) - if len(parts) < 3 { - return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: path too short") + // 这里的逻辑与 github.com 的类似, 需要提取 user, repo, branch, file... + // 我们只需要 user 和 repo + i := strings.IndexByte(remaining, '/') + if i <= 0 { + return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: missing user") } - return parts[0], parts[1], "raw", nil + user := remaining[:i] + remaining = remaining[i+1:] + i = strings.IndexByte(remaining, '/') + if i <= 0 { + return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: missing repo") + } + repo := remaining[:i] + // raw 链接至少需要 user/repo/branch 三部分 + remaining = remaining[i+1:] + if len(remaining) == 0 { + return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: missing branch/commit") + } + return user, repo, "raw", nil } - // 匹配 "https://gist.github.com/" 或 "https://gist.githubusercontent.com/" - isGist := strings.HasPrefix(rawPath, gistPrefix) - if isGist || strings.HasPrefix(rawPath, gistContentPrefix) { - var remaining string - if isGist { - remaining = rawPath[gistPrefixLen:] - } else { - remaining = rawPath[gistContentPrefixLen:] - } - parts := strings.SplitN(remaining, "/", 2) - if len(parts) == 0 || parts[0] == "" { + // 匹配 "https://gist.github.com/" + if strings.HasPrefix(rawPath, gistPrefix) { + remaining := rawPath[gistPrefixLen:] + i := strings.IndexByte(remaining, '/') + if i <= 0 { + // case: https://gist.github.com/user + // 这种情况下, gist_id 缺失, 但我们仍然可以认为 user 是有效的 + if len(remaining) > 0 { + return remaining, "", "gist", nil + } return "", "", "", NewErrorWithStatusLookup(400, "malformed gist url: missing user") } - return parts[0], "", "gist", nil + // case: https://gist.github.com/user/gist_id... + user := remaining[:i] + return user, "", "gist", nil + } + + // 匹配 "https://gist.githubusercontent.com/" + if strings.HasPrefix(rawPath, gistContentPrefix) { + remaining := rawPath[gistContentPrefixLen:] + i := strings.IndexByte(remaining, '/') + if i <= 0 { + // case: https://gist.githubusercontent.com/user + // 这种情况下, gist_id 缺失, 但我们仍然可以认为 user 是有效的 + if len(remaining) > 0 { + return remaining, "", "gist", nil + } + return "", "", "", NewErrorWithStatusLookup(400, "malformed gist url: missing user") + } + // case: https://gist.githubusercontent.com/user/gist_id... + user := remaining[:i] + return user, "", "gist", nil } // 匹配 "https://api.github.com/" diff --git a/proxy/matcher_test.go b/proxy/matcher_test.go index 07f3e4a..4b260c2 100644 --- a/proxy/matcher_test.go +++ b/proxy/matcher_test.go @@ -33,17 +33,11 @@ func TestMatcher_Compatibility(t *testing.T) { expectedErrCode int }{ { - name: "GH Releases Path 1", + name: "GH Releases Path", rawPath: "https://github.com/owner/repo/releases/download/v1.0/asset.zip", config: cfgWithAuth, expectedUser: "owner", expectedRepo: "repo", expectedMatcher: "releases", }, - { - name: "GH Releases Path 2", - rawPath: "https://github.com/owner/repo/releases/v1.0/download/asset.zip", - config: cfgWithAuth, - expectedUser: "owner", expectedRepo: "repo", expectedMatcher: "releases", - }, { name: "GH Releases Path Page", rawPath: "https://github.com/owner/repo/releases",