Source file src/pkg/cmd/go/internal/sumweb/server.go
1
2
3
4
5
6 package sumweb
7
8 import (
9 "context"
10 "internal/lazyregexp"
11 "net/http"
12 "os"
13 "strings"
14
15 "cmd/go/internal/tlog"
16 )
17
18
19
20
21 type Server interface {
22
23 NewContext(r *http.Request) (context.Context, error)
24
25
26 Signed(ctx context.Context) ([]byte, error)
27
28
29 ReadRecords(ctx context.Context, id, n int64) ([][]byte, error)
30
31
32
33 Lookup(ctx context.Context, key string) (int64, error)
34
35
36
37 ReadTileData(ctx context.Context, t tlog.Tile) ([]byte, error)
38 }
39
40
41
42
43 type Handler struct {
44 Server Server
45 }
46
47
48
49
50
51
52
53
54
55
56 var Paths = []string{
57 "/lookup/",
58 "/latest",
59 "/tile/",
60 }
61
62 var modVerRE = lazyregexp.New(`^[^@]+@v[0-9]+\.[0-9]+\.[0-9]+(-[^@]*)?(\+incompatible)?$`)
63
64 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
65 ctx, err := h.Server.NewContext(r)
66 if err != nil {
67 http.Error(w, err.Error(), 500)
68 return
69 }
70
71 switch {
72 default:
73 http.NotFound(w, r)
74
75 case strings.HasPrefix(r.URL.Path, "/lookup/"):
76 mod := strings.TrimPrefix(r.URL.Path, "/lookup/")
77 if !modVerRE.MatchString(mod) {
78 http.Error(w, "invalid module@version syntax", http.StatusBadRequest)
79 return
80 }
81 i := strings.Index(mod, "@")
82 encPath, encVers := mod[:i], mod[i+1:]
83 path, err := decodePath(encPath)
84 if err != nil {
85 reportError(w, r, err)
86 return
87 }
88 vers, err := decodeVersion(encVers)
89 if err != nil {
90 reportError(w, r, err)
91 return
92 }
93 id, err := h.Server.Lookup(ctx, path+"@"+vers)
94 if err != nil {
95 reportError(w, r, err)
96 return
97 }
98 records, err := h.Server.ReadRecords(ctx, id, 1)
99 if err != nil {
100
101 http.Error(w, err.Error(), http.StatusInternalServerError)
102 return
103 }
104 if len(records) != 1 {
105 http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError)
106 return
107 }
108 msg, err := tlog.FormatRecord(id, records[0])
109 if err != nil {
110 http.Error(w, err.Error(), http.StatusInternalServerError)
111 return
112 }
113 signed, err := h.Server.Signed(ctx)
114 if err != nil {
115 http.Error(w, err.Error(), http.StatusInternalServerError)
116 return
117 }
118 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
119 w.Write(msg)
120 w.Write(signed)
121
122 case r.URL.Path == "/latest":
123 data, err := h.Server.Signed(ctx)
124 if err != nil {
125 http.Error(w, err.Error(), http.StatusInternalServerError)
126 return
127 }
128 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
129 w.Write(data)
130
131 case strings.HasPrefix(r.URL.Path, "/tile/"):
132 t, err := tlog.ParseTilePath(r.URL.Path[1:])
133 if err != nil {
134 http.Error(w, "invalid tile syntax", http.StatusBadRequest)
135 return
136 }
137 if t.L == -1 {
138
139 start := t.N << uint(t.H)
140 records, err := h.Server.ReadRecords(ctx, start, int64(t.W))
141 if err != nil {
142 reportError(w, r, err)
143 return
144 }
145 if len(records) != t.W {
146 http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError)
147 return
148 }
149 var data []byte
150 for i, text := range records {
151 msg, err := tlog.FormatRecord(start+int64(i), text)
152 if err != nil {
153 http.Error(w, err.Error(), http.StatusInternalServerError)
154 }
155 data = append(data, msg...)
156 }
157 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
158 w.Write(data)
159 return
160 }
161
162 data, err := h.Server.ReadTileData(ctx, t)
163 if err != nil {
164 reportError(w, r, err)
165 return
166 }
167 w.Header().Set("Content-Type", "application/octet-stream")
168 w.Write(data)
169 }
170 }
171
172
173
174
175
176
177 func reportError(w http.ResponseWriter, r *http.Request, err error) {
178 if os.IsNotExist(err) {
179 http.Error(w, err.Error(), http.StatusNotFound)
180 return
181 }
182 http.Error(w, err.Error(), http.StatusInternalServerError)
183 }
184
View as plain text