server.go

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

Source Files