diff --git a/cmd/flags.go b/cmd/flags.go index e1838c2..78040be 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -10,7 +10,7 @@ var ServeFlags = []cli.Flag{ // TODO: Usage EnvVars: []string{"DEBUG"}, }, - + // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". diff --git a/server/upstream/domains.go b/server/upstream/domains.go index a80cc26..9deab7c 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -5,6 +5,7 @@ import ( "net/url" "path" "strings" + "time" "github.com/valyala/fasthttp" @@ -19,11 +20,11 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m func giteaRawContent(targetOwner, targetRepo, ref, giteaRoot, giteaAPIToken, resource string) ([]byte, error) { req := fasthttp.AcquireRequest() - req.SetRequestURI(path.Join(giteaRoot, giteaApiRepos, targetOwner, "/", targetRepo, "/raw/", resource, "?ref="+url.QueryEscape(ref))) + req.SetRequestURI(path.Join(giteaRoot, giteaApiRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref))) req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) res := fasthttp.AcquireResponse() - if err := client.Do(req, res); err != nil { + if err := getFastHTTPClient(10*time.Second).Do(req, res); err != nil { return nil, err } if res.StatusCode() != fasthttp.StatusOK { @@ -46,7 +47,6 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } } } else { - body, err := giteaRawContent(giteaRoot, targetRepo, targetBranch, giteaRoot, giteaAPIToken, canonicalDomainConfig) if err == nil { for _, domain := range strings.Split(string(body), "\n") { diff --git a/server/upstream/helper.go b/server/upstream/helper.go index c0e759e..5eee533 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -1,6 +1,8 @@ package upstream import ( + "fmt" + "path" "time" "github.com/valyala/fasthttp" @@ -16,38 +18,69 @@ type branchTimestamp struct { // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) -func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { +func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { return nil } return result.(*branchTimestamp) } - result := &branchTimestamp{} - result.Branch = branch - if branch == "" { + result := &branchTimestamp{ + Branch: branch, + } + if len(branch) == 0 { // Get default branch - var body = make([]byte, 0) - // TODO: use header for API key? - status, body, err := fasthttp.GetTimeout(body, giteaRoot+giteaApiRepos+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, defaultBranchCacheTimeout) + defaultBranch, err := giteaGetRepoDefaultBranch(giteaRoot, owner, repo, giteaAPIToken) + if err != nil { + _ = branchTimestampCache.Set(owner+"/"+repo+"/", nil, defaultBranchCacheTimeout) return nil } - result.Branch = fastjson.GetString(body, "default_branch") + result.Branch = defaultBranch } - var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, giteaRoot+giteaApiRepos+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { + timestamp, err := giteaGetRepoBranchTimestamp(giteaRoot, owner, repo, branch, giteaAPIToken) + if err != nil { return nil } - - result.Timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + result.Timestamp = timestamp _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, branchExistenceCacheTimeout) return result } +func giteaGetRepoBranchTimestamp(giteaRoot, repoOwner, repoName, branchName, giteaAPIToken string) (time.Time, error) { + client := getFastHTTPClient(5 * time.Second) + + req := fasthttp.AcquireRequest() + req.SetRequestURI(path.Join(giteaRoot, giteaApiRepos, repoOwner, repoName, "branches", branchName)) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) + res := fasthttp.AcquireResponse() + + if err := client.Do(req, res); err != nil { + return time.Time{}, err + } + if res.StatusCode() != fasthttp.StatusOK { + return time.Time{}, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return time.Parse(time.RFC3339, fastjson.GetString(res.Body(), "commit", "timestamp")) +} + +func giteaGetRepoDefaultBranch(giteaRoot, repoOwner, repoName, giteaAPIToken string) (string, error) { + client := getFastHTTPClient(5 * time.Second) + + req := fasthttp.AcquireRequest() + req.SetRequestURI(path.Join(giteaRoot, giteaApiRepos, repoOwner, repoName)) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) + res := fasthttp.AcquireResponse() + + if err := client.Do(req, res); err != nil { + return "", err + } + if res.StatusCode() != fasthttp.StatusOK { + return "", fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return fastjson.GetString(res.Body(), "default_branch"), nil +} + type fileResponse struct { exists bool mimeType string diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 133d6fe..d610665 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -38,11 +38,13 @@ type Options struct { redirectIfExists string } -var client = fasthttp.Client{ - ReadTimeout: 10 * time.Second, - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! +func getFastHTTPClient(timeout time.Duration) *fasthttp.Client { + return &fasthttp.Client{ + ReadTimeout: timeout, + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! + } } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. @@ -89,10 +91,11 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + giteaApiRepos + uri + "?access_token=" + giteaAPIToken) + req.SetRequestURI(giteaRoot + giteaApiRepos + uri) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) - err = client.Do(req, res) + err = getFastHTTPClient(10*time.Second).Do(req, res) } log.Debug().Msg("acquisition")