server.go

v1.4.3
Doc Versions Source
1
package server
2
3
import (
4
	"fmt"
5
	"log"
6
	"net/http"
7
	"strconv"
8
	"time"
9
10
	"github.com/go-git/go-git/v6/plumbing/transport"
11
12
	"go.bigb.es/curator/internal/config"
13
	"go.bigb.es/curator/internal/dochtml"
14
	"go.bigb.es/curator/internal/git"
15
	"go.bigb.es/curator/internal/metrics"
16
	"go.bigb.es/curator/internal/storage"
17
	"go.bigb.es/curator/internal/store"
18
)
19
20
// OIDCSessionValidator validates OIDC session cookies.
21
type OIDCSessionValidator interface {
22
	ValidateSession(value string) string
23
}
24
25
type Server struct {
26
	Cfg          *config.Config
27
	Resolver     store.ModuleResolver
28
	Credentials  store.CredentialResolver
29
	ModuleLister store.ModuleLister
30
	Git          *git.Cache
31
	Store        storage.Storage
32
	AuthTokens   map[string]struct{}
33
	OIDCProvider OIDCSessionValidator
34
	HTTPClient   *http.Client
35
	DocRenderer  *dochtml.Renderer
36
}
37
38
// authForModule returns a transport.AuthMethod for the given module's credential,
39
// or nil if none is configured.
40
func (s *Server) authForModule(mod config.Module) transport.AuthMethod {
41
	if mod.CredentialName == "" || s.Credentials == nil {
42
		return nil
43
	}
44
	cred, err := s.Credentials.GetCredential(mod.CredentialName)
45
	if err != nil {
46
		log.Printf("credential %q lookup failed: %v", mod.CredentialName, err)
47
		return nil
48
	}
49
	auth, err := git.AuthMethodFromCredential(cred.Type, cred.Data)
50
	if err != nil {
51
		log.Printf("credential %q build failed: %v", mod.CredentialName, err)
52
		return nil
53
	}
54
	return auth
55
}
56
57
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
58
	handler := "import"
59
	start := time.Now()
60
61
	rw := &responseWriter{ResponseWriter: w, code: http.StatusOK}
62
63
	defer func() {
64
		metrics.HTTPRequestsTotal.WithLabelValues(handler, r.Method, strconv.Itoa(rw.code)).Inc()
65
		metrics.HTTPRequestDuration.WithLabelValues(handler).Observe(time.Since(start).Seconds())
66
	}()
67
68
	// Handle GOPROXY @latest requests.
69
	if escapedModPath, ok := ParseLatestPath(r.URL.Path); ok {
70
		handler = "proxy"
71
		s.serveLatest(rw, r, escapedModPath)
72
		return
73
	}
74
75
	// Handle GOPROXY requests.
76
	if modName, file, ok := ParseProxyPath(r.URL.Path); ok {
77
		handler = "proxy"
78
		s.serveProxy(rw, r, modName, file)
79
		return
80
	}
81
82
	if r.URL.Path == "/" && r.URL.Query().Get("go-get") != "1" {
83
		handler = "landing"
84
		s.serveLanding(rw, r)
85
		return
86
	}
87
88
	// Resolve the module name from the URL path, trying multi-segment names.
89
	resolver := func(name string) bool {
90
		_, ok := s.Resolver.ResolveModule(name)
91
		return ok
92
	}
93
	name, _, _ := ResolveModulePath(r.URL.Path, resolver)
94
	if name == "" {
95
		http.NotFound(rw, r)
96
		return
97
	}
98
99
	if !s.CanAccessModule(name, r) {
100
		http.NotFound(rw, r)
101
		return
102
	}
103
104
	mod, _ := s.Resolver.ResolveModule(name)
105
106
	importPrefix := s.Cfg.Host + "/" + name
107
108
	// If not a go-get request, serve documentation.
109
	if r.URL.Query().Get("go-get") != "1" {
110
		if s.DocRenderer != nil {
111
			handler = "docs"
112
			s.serveDocs(rw, r)
113
			return
114
		}
115
116
		if mod.Web != "" {
117
			http.Redirect(rw, r, mod.Web, http.StatusTemporaryRedirect)
118
			return
119
		}
120
121
		http.NotFound(rw, r)
122
		return
123
	}
124
125
	rw.Header().Set("Content-Type", "text/html; charset=utf-8")
126
	fmt.Fprintf(rw, `<!DOCTYPE html>
127
<html>
128
<head>
129
<meta name="go-import" content="%s %s %s">
130
<meta http-equiv="refresh" content="0; url=%s">
131
</head>
132
<body>
133
Redirecting to <a href="%s">%s</a>...
134
</body>
135
</html>
136
`, importPrefix, mod.VCS, mod.Repo, mod.Web, mod.Web, mod.Web)
137
}
138

Source Files