gddo-server: fix redirect loop with canonical imports
Only redirect to canonical github project when the requested is a case
insensitive match, but not an exact match to the requested project.
Otherwise projects which have been moved to a new name, but use a
canonical import to the old name will result in a redirect loop.
Change-Id: I350359d802c3c42e47f2247ffee2469d6449eabc
Reviewed-on: https://go-review.googlesource.com/93176
Reviewed-by: Tuo Shan <shantuo@google.com>
diff --git a/gosrc/github.go b/gosrc/github.go
index e1d9528..c7497df 100644
--- a/gosrc/github.go
+++ b/gosrc/github.go
@@ -36,7 +36,7 @@
var (
gitHubRawHeader = http.Header{"Accept": {"application/vnd.github-blob.raw"}}
gitHubPreviewHeader = http.Header{"Accept": {"application/vnd.github.preview"}}
- ownerRepoPat = regexp.MustCompile(`^https://api.github.com/repos/([^/]+)/([^/]+)/`)
+ ownerRepoPat = regexp.MustCompile(`^https://api.github.com/repos/([^/]+/[^/]+)/`)
)
type githubCommit struct {
@@ -126,15 +126,8 @@
return nil, NotFoundError{Message: "No files in directory."}
}
- // GitHub owner and repo names are case-insensitive. Redirect if requested
- // names do not match the canonical names in API response.
- if m := ownerRepoPat.FindStringSubmatch(contents[0].GitURL); m != nil && (m[1] != match["owner"] || m[2] != match["repo"]) {
- match["owner"] = m[1]
- match["repo"] = m[2]
- return nil, NotFoundError{
- Message: "Github import path has incorrect case.",
- Redirect: expand("github.com/{owner}/{repo}{dir}", match),
- }
+ if err := validateGitHubProjectName(contents[0].GitURL, match); err != nil {
+ return nil, err
}
var files []*File
@@ -180,6 +173,28 @@
}, nil
}
+// validateGitHubProjectName checks if the requested owner and repo names match
+// the case of the canonical names returned by Github. GitHub owner and repo names
+// are case-insensitive so they must be normalized to the canonical casing.
+//
+// Returns a not found error if the names are the same, but have different case.
+// Returns nil if the names match exactly, or if the names are different,
+// which will happen if the url is a github redirect to a new project name.
+func validateGitHubProjectName(canonicalName string, requestMatch map[string]string) error {
+ m := ownerRepoPat.FindStringSubmatch(canonicalName)
+ if m == nil {
+ return nil
+ }
+ requestedName := requestMatch["owner"] + "/" + requestMatch["repo"]
+ if m[1] == requestedName || !strings.EqualFold(m[1], requestedName) {
+ return nil
+ }
+ return NotFoundError{
+ Message: "Github import path has incorrect case.",
+ Redirect: fmt.Sprintf("github.com/%s%s", m[1], requestMatch["dir"]),
+ }
+}
+
// isQuickFork reports whether the repository is a "quick fork":
// it has fewer than 3 commits, all within a week of the repo creation, createdAt.
// Commits must be in reverse chronological order by Commit.Committer.Date.