1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
// Matcher 从原始URL路径中高效地解析并匹配代理规则.
func Matcher(rawPath string, cfg *config.Config) (string, string, string, *GHProxyErrors) {
if len(rawPath) < 18 {
return "", "", "", NewErrorWithStatusLookup(404, "path too short")
}
// 匹配 "https://github.com/"
if strings.HasPrefix(rawPath, githubPrefix) {
remaining := rawPath[githubPrefixLen:]
i := strings.IndexByte(remaining, '/')
if i <= 0 {
return "", "", "", NewErrorWithStatusLookup(400, "malformed github path: missing user")
}
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")
}
i = strings.IndexByte(remaining, '/')
action := remaining
if i != -1 {
action = remaining[:i]
}
var matcher string
switch action {
case "releases", "archive":
matcher = "releases"
case "blob":
matcher = "blob"
case "raw":
matcher = "raw"
case "info", "git-upload-pack":
matcher = "clone"
default:
return "", "", "", NewErrorWithStatusLookup(400, fmt.Sprintf("unsupported github action: %s", action))
}
return user, repo, matcher, nil
}
// 匹配 "https://raw.githubusercontent.com/"
if strings.HasPrefix(rawPath, rawPrefix) {
remaining := rawPath[rawPrefixLen:]
// 这里的逻辑与 github.com 的类似, 需要提取 user, repo, branch, file...
// 我们只需要 user 和 repo
i := strings.IndexByte(remaining, '/')
if i <= 0 {
return "", "", "", NewErrorWithStatusLookup(400, "malformed raw url: missing user")
}
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/"
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")
}
// case: https://gist.github.com/user/gist_id...
user := remaining[:i]
return user, "", "gist", nil
}
// 匹配 "https://api.github.com/"
if strings.HasPrefix(rawPath, apiPrefix) {
if !cfg.Auth.ForceAllowApi && (cfg.Auth.Method != "header" || !cfg.Auth.Enabled) {
return "", "", "", NewErrorWithStatusLookup(403, "API proxy requires header authentication")
}
remaining := rawPath[apiPrefixLen:]
var user, repo string
if strings.HasPrefix(remaining, "repos/") {
parts := strings.SplitN(remaining[6:], "/", 3)
if len(parts) >= 2 {
user = parts[0]
repo = parts[1]
}
} else if strings.HasPrefix(remaining, "users/") {
parts := strings.SplitN(remaining[6:], "/", 2)
if len(parts) >= 1 {
user = parts[0]
}
}
return user, repo, "api", nil
}
return "", "", "", NewErrorWithStatusLookup(404, "no matcher found for the given path")
}
|