server.go

v1.3.5
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
	name := ModuleName(r.URL.Path)
77
	if name == "" {
78
		if r.URL.Path == "/" && r.URL.Query().Get("go-get") != "1" {
79
			handler = "landing"
80
			s.serveLanding(rw, r)
81
			return
82
		}
83
		http.NotFound(rw, r)
84
		return
85
	}
86
87
	if !s.CanAccessModule(name, r) {
88
		http.NotFound(rw, r)
89
		return
90
	}
91
92
	mod, _ := s.Resolver.ResolveModule(name)
93
94
	importPrefix := s.Cfg.Host + "/" + name
95
96
	// If not a go-get request, serve documentation.
97
	if r.URL.Query().Get("go-get") != "1" {
98
		if s.DocRenderer != nil {
99
			handler = "docs"
100
			s.serveDocs(rw, r)
101
			return
102
		}
103
104
		if mod.Web != "" {
105
			http.Redirect(rw, r, mod.Web, http.StatusTemporaryRedirect)
106
			return
107
		}
108
109
		http.NotFound(rw, r)
110
		return
111
	}
112
113
	rw.Header().Set("Content-Type", "text/html; charset=utf-8")
114
	fmt.Fprintf(rw, `<!DOCTYPE html>
115
<html>
116
<head>
117
<meta name="go-import" content="%s %s %s">
118
<meta http-equiv="refresh" content="0; url=%s">
119
</head>
120
<body>
121
Redirecting to <a href="%s">%s</a>...
122
</body>
123
</html>
124
`, importPrefix, mod.VCS, mod.Repo, mod.Web, mod.Web, mod.Web)
125
}
126

Source Files