sumdb_proxy.go

v1.4.3
Doc Versions Source
1
package server
2
3
import (
4
	"context"
5
	"io"
6
	"log"
7
	"net/http"
8
	"strings"
9
)
10
11
// SumdbHandler returns a handler that serves local sumdb for our modules
12
// and proxies to upstream sumdb for everything else.
13
func (s *Server) SumdbHandler(local http.Handler, upstream, prefix string) http.Handler {
14
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
		path := strings.TrimPrefix(r.URL.Path, prefix)
16
17
		// /lookup/<module>@<version> — route based on module host.
18
		if strings.HasPrefix(path, "/lookup/") {
19
			lookupPath := strings.TrimPrefix(path, "/lookup/")
20
			if atIdx := strings.LastIndex(lookupPath, "@"); atIdx > 0 {
21
				modPath := lookupPath[:atIdx]
22
				if strings.HasPrefix(modPath, s.Cfg.Host+"/") {
23
					local.ServeHTTP(w, r)
24
					return
25
				}
26
			}
27
28
			if upstream != "" {
29
				s.proxySumdb(w, r, upstream, path)
30
				return
31
			}
32
		}
33
34
		// /latest and /tile/ — always serve from our local tree.
35
		local.ServeHTTP(w, r)
36
	})
37
}
38
39
func (s *Server) proxySumdb(w http.ResponseWriter, r *http.Request, upstream, path string) {
40
	// Check storage cache.
41
	cacheKey := "sumdb/proxy" + path
42
	if s.Store != nil {
43
		data, ct, err := s.Store.Get(r.Context(), cacheKey)
44
		if err == nil {
45
			w.Header().Set("Content-Type", ct)
46
			w.Write(data)
47
			return
48
		}
49
	}
50
51
	url := strings.TrimRight(upstream, "/") + path
52
	req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, url, nil)
53
	if err != nil {
54
		http.Error(w, "internal error", http.StatusInternalServerError)
55
		return
56
	}
57
58
	resp, err := s.HTTPClient.Do(req)
59
	if err != nil {
60
		log.Printf("sumdb proxy %s: %v", url, err)
61
		http.Error(w, "upstream sumdb error", http.StatusBadGateway)
62
		return
63
	}
64
	defer resp.Body.Close()
65
66
	body, err := io.ReadAll(resp.Body)
67
	if err != nil {
68
		http.Error(w, "upstream sumdb error", http.StatusBadGateway)
69
		return
70
	}
71
72
	ct := resp.Header.Get("Content-Type")
73
74
	// Cache successful responses.
75
	if resp.StatusCode == http.StatusOK && s.Store != nil {
76
		go func() {
77
			if err := s.Store.Put(context.Background(), cacheKey, body, ct); err != nil {
78
				log.Printf("sumdb cache put %s: %v", cacheKey, err)
79
			}
80
		}()
81
	}
82
83
	if ct != "" {
84
		w.Header().Set("Content-Type", ct)
85
	}
86
	w.WriteHeader(resp.StatusCode)
87
	w.Write(body)
88
}
89

Source Files