server.go

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

Source Files