Source File
server.go
Belonging Package
golang.org/x/pkgsite/internal/frontend
package frontend
import (
)
cmplClient *redis.Client
taskIDChangeInterval time.Duration
staticPath template.TrustedSource
thirdPartyPath string
templateDir template.TrustedSource
devMode bool
errorPage []byte
appVersionLabel string
googleTagManagerID string
serveStats bool
reportingClient *errorreporting.Client
mu sync.Mutex // Protects all fields below
templates map[string]*template.Template
}
DataSourceGetter func(context.Context) internal.DataSource
Queue queue.Queue
CompletionClient *redis.Client
TaskIDChangeInterval time.Duration
StaticPath template.TrustedSource
ThirdPartyPath string
DevMode bool
AppVersionLabel string
GoogleTagManagerID string
ServeStats bool
ReportingClient *errorreporting.Client
}
func ( ServerConfig) ( *Server, error) {
defer derrors.Wrap(&, "NewServer(...)")
:= template.TrustedSourceJoin(.StaticPath)
, := parsePageTemplates()
if != nil {
return nil, fmt.Errorf("error parsing templates: %v", )
}
:= template.TrustedSourceJoin(, template.TrustedSourceFromConstant("html"),
template.TrustedSourceFromConstant("doc"))
dochtml.LoadTemplates()
:= &Server{
getDataSource: .DataSourceGetter,
queue: .Queue,
cmplClient: .CompletionClient,
staticPath: .StaticPath,
thirdPartyPath: .ThirdPartyPath,
templateDir: ,
devMode: .DevMode,
templates: ,
taskIDChangeInterval: .TaskIDChangeInterval,
appVersionLabel: .AppVersionLabel,
googleTagManagerID: .GoogleTagManagerID,
serveStats: .ServeStats,
reportingClient: .ReportingClient,
}
, := .renderErrorPage(context.Background(), http.StatusInternalServerError, "error.tmpl", nil)
if != nil {
return nil, fmt.Errorf("s.renderErrorPage(http.StatusInternalServerError, nil): %v", )
}
.errorPage =
return , nil
}
func ( *Server) ( func(string, http.Handler), *redis.Client, []string) {
var (
http.Handler = .errorHandler(.serveDetails)
http.Handler = .errorHandler(.serveFetch)
http.Handler = .errorHandler(.serveSearch)
)
if != nil {
= middleware.Cache("details", , detailsTTL, )()
= middleware.Cache("search", , middleware.TTL(defaultTTL), )()
("/_ah/", http.HandlerFunc(func( http.ResponseWriter, *http.Request) {
log.Infof(.Context(), "Request made to %q", .URL.Path)
}))
("/static/", .staticHandler())
("/third_party/", http.StripPrefix("/third_party", http.FileServer(http.Dir(.thirdPartyPath))))
("/favicon.ico", http.HandlerFunc(func( http.ResponseWriter, *http.Request) {
http.ServeFile(, , fmt.Sprintf("%s/img/favicon.ico", http.Dir(.staticPath.String())))
}))
("/mod/", http.HandlerFunc(.handleModuleDetailsRedirect))
("/pkg/", http.HandlerFunc(.handlePackageDetailsRedirect))
("/fetch/", )
("/play/compile", http.HandlerFunc(.proxyPlayground))
("/play/fmt", http.HandlerFunc(.handleFmt))
("/play/share", http.HandlerFunc(.proxyPlayground))
("/search", )
("/search-help", .staticPageHandler("search_help.tmpl", "Search Help"))
("/license-policy", .licensePolicyHandler())
("/about", http.RedirectHandler("https://go.dev/about", http.StatusFound))
("/badge/", http.HandlerFunc(.badgeHandler))
("/styleguide", http.HandlerFunc(.errorHandler(.serveStyleGuide)))
http.Redirect(, , "/cmd/cgo", http.StatusMovedPermanently)
}))
("/", )
if .serveStats {
("/detail-stats/",
middleware.Stats()(http.StripPrefix("/detail-stats", .errorHandler(.serveDetails))))
}
("/robots.txt", http.HandlerFunc(func( http.ResponseWriter, *http.Request) {
.Header().Set("Content-Type", "text/plain; charset=utf-8")
http.ServeContent(, , "", time.Time{}, strings.NewReader(`User-agent: *
Disallow: /search?*
Disallow: /fetch/*
`))
}))
}
func ( *http.Request) time.Duration {
:= .Header.Get("User-Agent")
for , := range crawlers {
if strings.Contains(, ) {
return tinyTTL
}
}
return detailsTTLForPath(.Context(), .URL.Path, .FormValue("tab"))
}
func ( context.Context, , string) time.Duration {
if == "/" {
return defaultTTL
}
, := parseDetailsURLPath()
if != nil {
log.Errorf(, "falling back to default TTL: %v", )
return defaultTTL
}
if .requestedVersion == internal.LatestVersion {
return shortTTL
}
if == "importedby" || == "versions" {
return defaultTTL
}
return longTTL
}
if , := unitTabLookup[]; {
if != "" {
+= "-"
}
+=
}
}
return
}
func ( *Server) (, string) http.HandlerFunc {
return func( http.ResponseWriter, *http.Request) {
.servePage(.Context(), , , .newBasePage(, ))
}
}
type licensePolicyPage struct {
basePage
LicenseFileNames []string
LicenseTypes []licenses.AcceptedLicenseInfo
}
func ( *Server) () http.HandlerFunc {
:= licenses.AcceptedLicenses()
return http.HandlerFunc(func( http.ResponseWriter, *http.Request) {
:= licensePolicyPage{
basePage: .newBasePage(, "Licenses"),
LicenseFileNames: licenses.FileNames,
LicenseTypes: ,
}
.servePage(.Context(), , "license_policy.tmpl", )
})
}
func ( *Server) ( *http.Request, string) basePage {
return basePage{
HTMLTitle: ,
Query: searchQuery(),
Experiments: experiment.FromContext(.Context()),
DevMode: .devMode,
AppVersionLabel: .appVersionLabel,
GoogleTagManagerID: .googleTagManagerID,
}
}
type errorPage struct {
basePage
templateName string
messageTemplate template.TrustedTemplate
MessageData interface{}
}
func ( *Server) () ( http.HandlerFunc, error) {
defer derrors.Wrap(&, "PanicHandler")
:= http.StatusInternalServerError
, := .renderErrorPage(context.Background(), , "error.tmpl", nil)
if != nil {
return nil,
}
return func( http.ResponseWriter, *http.Request) {
.WriteHeader()
if , := io.Copy(, bytes.NewReader()); != nil {
log.Errorf(.Context(), "Error copying panic template to ResponseWriter: %v", )
}
}, nil
}
type serverError struct {
status int // HTTP status code
responseText string // Response text to the user
epage *errorPage
err error // wrapped error
}
func ( *serverError) () string {
return fmt.Sprintf("%d (%s): %v (epage=%v)", .status, http.StatusText(.status), .err, .epage)
}
func ( *serverError) () error {
return .err
}
func ( *Server) ( func( http.ResponseWriter, *http.Request, internal.DataSource) error) http.HandlerFunc {
:= .getDataSource(.Context())
if := (, , ); != nil {
.serveError(, , )
}
}
}
func ( *Server) ( http.ResponseWriter, *http.Request, error) {
:= .Context()
var *serverError
if !errors.As(, &) {
= &serverError{status: http.StatusInternalServerError, err: }
}
if .status == http.StatusInternalServerError {
log.Error(, )
.reportError(, , , )
} else {
log.Infof(, "returning %d (%s) for error %v", .status, http.StatusText(.status), )
}
if .responseText == "" {
.responseText = http.StatusText(.status)
}
if .Method == http.MethodPost {
http.Error(, .responseText, .status)
return
}
.serveErrorPage(, , .status, .epage)
}
func ( *Server) ( context.Context, error, http.ResponseWriter, *http.Request) {
if .reportingClient == nil {
return
var []byte
if := (*derrors.StackError)(nil); errors.As(, &) {
= .Stack
}
.reportingClient.Report(errorreporting.Entry{
Error: ,
Req: ,
Stack: ,
})
.basePage = .newBasePage(, "")
}
if .templateName != "" {
= .templateName
}
} else {
= &errorPage{
basePage: .newBasePage(, ""),
}
}
, := .renderErrorPage(.Context(), , , )
if != nil {
log.Errorf(.Context(), "s.renderErrorPage(w, %d, %v): %v", , , )
= .errorPage
= http.StatusInternalServerError
}
.WriteHeader()
if , := io.Copy(, bytes.NewReader()); != nil {
log.Errorf(.Context(), "Error copying template %q buffer to ResponseWriter: %v", , )
}
}
func ( *Server) ( context.Context, int, string, *errorPage) ([]byte, error) {
:= fmt.Sprintf("%d %s", , http.StatusText())
if == nil {
= &errorPage{}
}
if .messageTemplate.String() == "" {
.messageTemplate = template.MakeTrustedTemplate(`<h3 class="Error-message">{{.}}</h3>`)
}
if .MessageData == nil {
.MessageData =
}
if .HTMLTitle == "" {
.HTMLTitle =
}
if == "" {
= "error.tmpl"
}
, := .findTemplate()
if != nil {
return nil,
}
, := .Clone()
if != nil {
return nil,
}
_, = .New("message").ParseFromTrustedTemplate(.messageTemplate)
if != nil {
return nil,
}
return executeTemplate(, , , )
}
func ( *Server) ( context.Context, http.ResponseWriter, string, interface{}) {
defer middleware.ElapsedStat(, "servePage")()
, := .renderPage(, , )
if != nil {
log.Errorf(, "s.renderPage(%q, %+v): %v", , , )
.WriteHeader(http.StatusInternalServerError)
= .errorPage
}
if , := io.Copy(, bytes.NewReader()); != nil {
log.Errorf(, "Error copying template %q buffer to ResponseWriter: %v", , )
.WriteHeader(http.StatusInternalServerError)
}
}
func ( *Server) ( context.Context, string, interface{}) ([]byte, error) {
defer middleware.ElapsedStat(, "renderPage")()
, := .findTemplate()
if != nil {
return nil,
}
return executeTemplate(, , , )
}
func ( *Server) ( string) (*template.Template, error) {
if .devMode {
.mu.Lock()
defer .mu.Unlock()
var error
.templates, = parsePageTemplates(.templateDir)
if != nil {
return nil, fmt.Errorf("error parsing templates: %v", )
}
}
:= .templates[]
if == nil {
return nil, fmt.Errorf("BUG: s.templates[%q] not found", )
}
return , nil
}
func ( context.Context, string, *template.Template, interface{}) ([]byte, error) {
var bytes.Buffer
if := .Execute(&, ); != nil {
log.Errorf(, "Error executing page template %q: %v", , )
return nil,
}
return .Bytes(), nil
}
var templateFuncs = template.FuncMap{
"add": func(, int) int { return + },
"pluralize": func( int, string) string {
if == 1 {
return
}
return + "s"
},
"commaseparate": func( []string) string {
return strings.Join(, ", ")
},
"stripscheme": stripScheme,
}
func ( string) string {
if := strings.Index(, "://"); > 0 {
return [+len("://"):]
}
return
}
func ( template.TrustedSource) (map[string]*template.Template, error) {
:= template.TrustedSourceFromConstant
:= template.TrustedSourceJoin
:= [][]template.TrustedSource{
{("badge.tmpl")},
{("error.tmpl")},
{("fetch.tmpl")},
{("index.tmpl")},
{("license_policy.tmpl")},
{("search.tmpl")},
{("search_help.tmpl")},
{("unit_details.tmpl"), ("unit.tmpl")},
{("unit_importedby.tmpl"), ("unit.tmpl")},
{("unit_imports.tmpl"), ("unit.tmpl")},
{("unit_licenses.tmpl"), ("unit.tmpl")},
{("unit_versions.tmpl"), ("unit.tmpl")},
}
:= make(map[string]*template.Template)
for , := range {
, := template.New("base.tmpl").Funcs(templateFuncs).ParseFilesFromTrustedSources((, ("html"), ("base.tmpl")))
if != nil {
return nil, fmt.Errorf("ParseFiles: %v", )
}
:= (, ("html"), ("helpers"), ("*.tmpl"))
if , := .ParseGlobFromTrustedSource(); != nil {
return nil, fmt.Errorf("ParseGlob(%q): %v", , )
}
var []template.TrustedSource
for , := range {
= append(, (, ("html"), ("pages"), ))
}
if , := .ParseFilesFromTrustedSources(...); != nil {
return nil, fmt.Errorf("ParseFilesFromTrustedSources(%v): %v", , )
}
[[0].String()] =
}
:= [][]template.TrustedSource{
{("styleguide"), ("main-layout")},
}
for , := range {
, := template.New("base.tmpl").Funcs(templateFuncs).ParseFilesFromTrustedSources((, ("base/base.tmpl")))
if != nil {
return nil, fmt.Errorf("ParseFilesFromTrustedSources: %v", )
}
:= (, ("**/*.partial.tmpl"))
if , := .ParseGlobFromTrustedSource(); != nil {
return nil, fmt.Errorf("ParseGlobFromTrustedSource(%q): %v", , )
}
var []template.TrustedSource
for , := range {
if , := .ParseGlobFromTrustedSource((, , ("*.tmpl"))); != nil {
return nil, fmt.Errorf("ParseGlobFromTrustedSource(%v): %v", , )
}
}
[[0].String()] =
}
return , nil
}
func ( *Server) () http.Handler {
:= .staticPath.String()
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |