mirror of
https://github.com/WJQSERVER-STUDIO/ghproxy.git
synced 2026-02-03 16:21:11 +08:00
Compare commits
No commits in common. "b0388e6abc1e11029bea246757242f9476357d7b" and "4b3f8e101866b4f66d08a7b06c1756b58d9ebc17" have entirely different histories.
b0388e6abc
...
4b3f8e1018
5 changed files with 71 additions and 79 deletions
|
|
@ -1,9 +1,5 @@
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
4.2.5 - 2025-07-31
|
|
||||||
---
|
|
||||||
- CHANGE: 进一步完善匹配器, 兼容更多情况
|
|
||||||
|
|
||||||
4.2.4 - 2025-07-29
|
4.2.4 - 2025-07-29
|
||||||
---
|
---
|
||||||
- CHANGE: 改进匹配器, 防止匹配不应匹配的内容
|
- CHANGE: 改进匹配器, 防止匹配不应匹配的内容
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
4.2.5
|
4.2.4
|
||||||
|
|
|
||||||
5
main.go
5
main.go
|
|
@ -399,11 +399,6 @@ func main() {
|
||||||
proxy.RoutingHandler(cfg)(c)
|
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) {
|
r.GET("/github.com/:user/:repo/archive/*filepath", func(c *touka.Context) {
|
||||||
c.Set("matcher", "releases")
|
c.Set("matcher", "releases")
|
||||||
proxy.RoutingHandler(cfg)(c)
|
proxy.RoutingHandler(cfg)(c)
|
||||||
|
|
|
||||||
131
proxy/match.go
131
proxy/match.go
|
|
@ -42,62 +42,37 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHPro
|
||||||
|
|
||||||
// 匹配 "https://github.com/"
|
// 匹配 "https://github.com/"
|
||||||
if strings.HasPrefix(rawPath, githubPrefix) {
|
if strings.HasPrefix(rawPath, githubPrefix) {
|
||||||
pathAfterDomain := rawPath[githubPrefixLen:]
|
remaining := rawPath[githubPrefixLen:]
|
||||||
|
i := strings.IndexByte(remaining, '/')
|
||||||
// 解析 user
|
|
||||||
i := strings.IndexByte(pathAfterDomain, '/')
|
|
||||||
if i <= 0 {
|
if i <= 0 {
|
||||||
return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing user")
|
return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing user")
|
||||||
}
|
}
|
||||||
user := pathAfterDomain[:i]
|
user := remaining[:i]
|
||||||
pathAfterUser := pathAfterDomain[i+1:]
|
remaining = remaining[i+1:]
|
||||||
|
i = strings.IndexByte(remaining, '/')
|
||||||
// 解析 repo
|
|
||||||
i = strings.IndexByte(pathAfterUser, '/')
|
|
||||||
if i <= 0 {
|
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")
|
return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing action")
|
||||||
}
|
}
|
||||||
repo := pathAfterUser[:i]
|
i = strings.IndexByte(remaining, '/')
|
||||||
pathAfterRepo := pathAfterUser[i+1:]
|
action := remaining
|
||||||
|
|
||||||
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
|
|
||||||
if i != -1 {
|
if i != -1 {
|
||||||
action = pathAfterRepo[:i]
|
action = remaining[:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
var matcher string
|
var matcher string
|
||||||
switch action {
|
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":
|
case "blob":
|
||||||
matcher = "blob"
|
matcher = "blob"
|
||||||
case "raw":
|
case "raw":
|
||||||
|
|
@ -113,27 +88,59 @@ func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHPro
|
||||||
// 匹配 "https://raw.githubusercontent.com/"
|
// 匹配 "https://raw.githubusercontent.com/"
|
||||||
if strings.HasPrefix(rawPath, rawPrefix) {
|
if strings.HasPrefix(rawPath, rawPrefix) {
|
||||||
remaining := rawPath[rawPrefixLen:]
|
remaining := rawPath[rawPrefixLen:]
|
||||||
parts := strings.SplitN(remaining, "/", 3)
|
// 这里的逻辑与 github.com 的类似, 需要提取 user, repo, branch, file...
|
||||||
if len(parts) < 3 {
|
// 我们只需要 user 和 repo
|
||||||
return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: path too short")
|
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/"
|
// 匹配 "https://gist.github.com/"
|
||||||
isGist := strings.HasPrefix(rawPath, gistPrefix)
|
if strings.HasPrefix(rawPath, gistPrefix) {
|
||||||
if isGist || strings.HasPrefix(rawPath, gistContentPrefix) {
|
remaining := rawPath[gistPrefixLen:]
|
||||||
var remaining string
|
i := strings.IndexByte(remaining, '/')
|
||||||
if isGist {
|
if i <= 0 {
|
||||||
remaining = rawPath[gistPrefixLen:]
|
// case: https://gist.github.com/user
|
||||||
} else {
|
// 这种情况下, gist_id 缺失, 但我们仍然可以认为 user 是有效的
|
||||||
remaining = rawPath[gistContentPrefixLen:]
|
if len(remaining) > 0 {
|
||||||
}
|
return remaining, "", "gist", nil
|
||||||
parts := strings.SplitN(remaining, "/", 2)
|
}
|
||||||
if len(parts) == 0 || parts[0] == "" {
|
|
||||||
return "", "", "", NewErrorWithStatusLookup(400, "malformed gist url: missing user")
|
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/"
|
// 匹配 "https://api.github.com/"
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,11 @@ func TestMatcher_Compatibility(t *testing.T) {
|
||||||
expectedErrCode int
|
expectedErrCode int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "GH Releases Path 1",
|
name: "GH Releases Path",
|
||||||
rawPath: "https://github.com/owner/repo/releases/download/v1.0/asset.zip",
|
rawPath: "https://github.com/owner/repo/releases/download/v1.0/asset.zip",
|
||||||
config: cfgWithAuth,
|
config: cfgWithAuth,
|
||||||
expectedUser: "owner", expectedRepo: "repo", expectedMatcher: "releases",
|
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",
|
name: "GH Releases Path Page",
|
||||||
rawPath: "https://github.com/owner/repo/releases",
|
rawPath: "https://github.com/owner/repo/releases",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue