Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / mod@v0.3.0 / sumdb / server.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package sumdb implements the HTTP protocols for serving or accessing a module checksum database.
6 package sumdb
7
8 import (
9         "context"
10         "net/http"
11         "os"
12         "strings"
13
14         "golang.org/x/mod/internal/lazyregexp"
15         "golang.org/x/mod/module"
16         "golang.org/x/mod/sumdb/tlog"
17 )
18
19 // A ServerOps provides the external operations
20 // (underlying database access and so on) needed by the Server.
21 type ServerOps interface {
22         // Signed returns the signed hash of the latest tree.
23         Signed(ctx context.Context) ([]byte, error)
24
25         // ReadRecords returns the content for the n records id through id+n-1.
26         ReadRecords(ctx context.Context, id, n int64) ([][]byte, error)
27
28         // Lookup looks up a record for the given module,
29         // returning the record ID.
30         Lookup(ctx context.Context, m module.Version) (int64, error)
31
32         // ReadTileData reads the content of tile t.
33         // It is only invoked for hash tiles (t.L ≥ 0).
34         ReadTileData(ctx context.Context, t tlog.Tile) ([]byte, error)
35 }
36
37 // A Server is the checksum database HTTP server,
38 // which implements http.Handler and should be invoked
39 // to serve the paths listed in ServerPaths.
40 type Server struct {
41         ops ServerOps
42 }
43
44 // NewServer returns a new Server using the given operations.
45 func NewServer(ops ServerOps) *Server {
46         return &Server{ops: ops}
47 }
48
49 // ServerPaths are the URL paths the Server can (and should) serve.
50 //
51 // Typically a server will do:
52 //
53 //      srv := sumdb.NewServer(ops)
54 //      for _, path := range sumdb.ServerPaths {
55 //              http.Handle(path, srv)
56 //      }
57 //
58 var ServerPaths = []string{
59         "/lookup/",
60         "/latest",
61         "/tile/",
62 }
63
64 var modVerRE = lazyregexp.New(`^[^@]+@v[0-9]+\.[0-9]+\.[0-9]+(-[^@]*)?(\+incompatible)?$`)
65
66 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
67         ctx := r.Context()
68
69         switch {
70         default:
71                 http.NotFound(w, r)
72
73         case strings.HasPrefix(r.URL.Path, "/lookup/"):
74                 mod := strings.TrimPrefix(r.URL.Path, "/lookup/")
75                 if !modVerRE.MatchString(mod) {
76                         http.Error(w, "invalid module@version syntax", http.StatusBadRequest)
77                         return
78                 }
79                 i := strings.Index(mod, "@")
80                 escPath, escVers := mod[:i], mod[i+1:]
81                 path, err := module.UnescapePath(escPath)
82                 if err != nil {
83                         reportError(w, err)
84                         return
85                 }
86                 vers, err := module.UnescapeVersion(escVers)
87                 if err != nil {
88                         reportError(w, err)
89                         return
90                 }
91                 id, err := s.ops.Lookup(ctx, module.Version{Path: path, Version: vers})
92                 if err != nil {
93                         reportError(w, err)
94                         return
95                 }
96                 records, err := s.ops.ReadRecords(ctx, id, 1)
97                 if err != nil {
98                         // This should never happen - the lookup says the record exists.
99                         http.Error(w, err.Error(), http.StatusInternalServerError)
100                         return
101                 }
102                 if len(records) != 1 {
103                         http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError)
104                         return
105                 }
106                 msg, err := tlog.FormatRecord(id, records[0])
107                 if err != nil {
108                         http.Error(w, err.Error(), http.StatusInternalServerError)
109                         return
110                 }
111                 signed, err := s.ops.Signed(ctx)
112                 if err != nil {
113                         http.Error(w, err.Error(), http.StatusInternalServerError)
114                         return
115                 }
116                 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
117                 w.Write(msg)
118                 w.Write(signed)
119
120         case r.URL.Path == "/latest":
121                 data, err := s.ops.Signed(ctx)
122                 if err != nil {
123                         http.Error(w, err.Error(), http.StatusInternalServerError)
124                         return
125                 }
126                 w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
127                 w.Write(data)
128
129         case strings.HasPrefix(r.URL.Path, "/tile/"):
130                 t, err := tlog.ParseTilePath(r.URL.Path[1:])
131                 if err != nil {
132                         http.Error(w, "invalid tile syntax", http.StatusBadRequest)
133                         return
134                 }
135                 if t.L == -1 {
136                         // Record data.
137                         start := t.N << uint(t.H)
138                         records, err := s.ops.ReadRecords(ctx, start, int64(t.W))
139                         if err != nil {
140                                 reportError(w, err)
141                                 return
142                         }
143                         if len(records) != t.W {
144                                 http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError)
145                                 return
146                         }
147                         var data []byte
148                         for i, text := range records {
149                                 msg, err := tlog.FormatRecord(start+int64(i), text)
150                                 if err != nil {
151                                         http.Error(w, err.Error(), http.StatusInternalServerError)
152                                 }
153                                 data = append(data, msg...)
154                         }
155                         w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
156                         w.Write(data)
157                         return
158                 }
159
160                 data, err := s.ops.ReadTileData(ctx, t)
161                 if err != nil {
162                         reportError(w, err)
163                         return
164                 }
165                 w.Header().Set("Content-Type", "application/octet-stream")
166                 w.Write(data)
167         }
168 }
169
170 // reportError reports err to w.
171 // If it's a not-found, the reported error is 404.
172 // Otherwise it is an internal server error.
173 // The caller must only call reportError in contexts where
174 // a not-found err should be reported as 404.
175 func reportError(w http.ResponseWriter, err error) {
176         if os.IsNotExist(err) {
177                 http.Error(w, err.Error(), http.StatusNotFound)
178                 return
179         }
180         http.Error(w, err.Error(), http.StatusInternalServerError)
181 }