-
Notifications
You must be signed in to change notification settings - Fork 185
/
Copy pathdetails.go
111 lines (100 loc) · 3.44 KB
/
details.go
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
111
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package frontend
import (
"context"
"errors"
"net/http"
"strings"
"golang.org/x/pkgsite/internal/frontend/page"
"golang.org/x/pkgsite/internal/frontend/serrors"
"golang.org/x/pkgsite/internal/frontend/urlinfo"
mstats "golang.org/x/pkgsite/internal/middleware/stats"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/stdlib"
)
// serveDetails handles requests for package/directory/module details pages. It
// expects paths of the form "/<module-path>[@<version>?tab=<tab>]".
// stdlib module pages are handled at "/std", and requests to "/mod/std" will
// be redirected to that path.
func (s *Server) serveDetails(w http.ResponseWriter, r *http.Request, ds internal.DataSource) (err error) {
defer mstats.Elapsed(r.Context(), "serveDetails")()
ctx := r.Context()
if r.Method != http.MethodGet && r.Method != http.MethodHead {
return &serrors.ServerError{Status: http.StatusMethodNotAllowed}
}
if r.URL.Path == "/" {
s.serveHomepage(ctx, w, r)
return nil
}
if strings.HasSuffix(r.URL.Path, "/") {
url := *r.URL
url.Path = strings.TrimSuffix(r.URL.Path, "/")
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
return
}
// If page statistics are enabled, use the "exp" query param to adjust
// the active experiments.
if s.serveStats {
ctx = setExperimentsFromQueryParam(ctx, r)
}
urlInfo, err := urlinfo.ExtractURLPathInfo(r.URL.Path)
if err != nil {
var epage *page.ErrorPage
if uerr := new(urlinfo.UserError); errors.As(err, &uerr) {
epage = &page.ErrorPage{MessageData: uerr.UserMessage}
}
return &serrors.ServerError{
Status: http.StatusBadRequest,
Err: err,
Epage: epage,
}
}
if !urlinfo.IsSupportedVersion(urlInfo.FullPath, urlInfo.RequestedVersion) {
return serrors.InvalidVersionError(urlInfo.FullPath, urlInfo.RequestedVersion)
}
if urlPath := stdlibRedirectURL(urlInfo.FullPath); urlPath != "" {
http.Redirect(w, r, urlPath, http.StatusMovedPermanently)
return
}
if err := checkExcluded(ctx, ds, urlInfo.FullPath, urlInfo.RequestedVersion); err != nil {
return err
}
return s.serveUnitPage(ctx, w, r, ds, urlInfo)
}
func stdlibRedirectURL(fullPath string) string {
if !strings.HasPrefix(fullPath, stdlib.GitHubRepo) {
return ""
}
if fullPath == stdlib.GitHubRepo || fullPath == stdlib.GitHubRepo+"/src" {
return "/std"
}
urlPath2 := strings.TrimPrefix(strings.TrimPrefix(fullPath, stdlib.GitHubRepo+"/"), "src/")
if fullPath == urlPath2 {
return ""
}
return "/" + urlPath2
}
func checkExcluded(ctx context.Context, ds internal.DataSource, fullPath, version string) error {
if caseSensitiveExcludedPaths[fullPath] {
return &serrors.ServerError{Status: http.StatusNotFound}
}
db, ok := ds.(internal.PostgresDB)
if !ok {
return nil
}
if db.IsExcluded(ctx, fullPath, version) {
// Return NotFound; don't let the user know that the package was excluded.
return &serrors.ServerError{Status: http.StatusNotFound}
}
return nil
}
// Paths to exclude if they match exactly.
// These are very rare, so it's simpler to hardcode them rather than use the DB.
var caseSensitiveExcludedPaths = map[string]bool{
"github.com./ibm/sarama": true, // https://go.dev/issue/71342
"github.com./burntsushi/toml": true, // https://go.dev/issue/68357
"github.com./burntSushi/toml": true,
"github.com./Burntsushi/toml": true,
}