github.com/bir3/gocompiler@v0.9.2202/src/xvendor/golang.org/x/mod/sumdb/server.go (about) 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 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/internal/lazyregexp" 15 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 16 "github.com/bir3/gocompiler/src/xvendor/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 var ServerPaths = []string{ 58 "/lookup/", 59 "/latest", 60 "/tile/", 61 } 62 63 var modVerRE = lazyregexp.New(`^[^@]+@v[0-9]+\.[0-9]+\.[0-9]+(-[^@]*)?(\+incompatible)?$`) 64 65 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 66 ctx := r.Context() 67 68 switch { 69 default: 70 http.NotFound(w, r) 71 72 case strings.HasPrefix(r.URL.Path, "/lookup/"): 73 mod := strings.TrimPrefix(r.URL.Path, "/lookup/") 74 if !modVerRE.MatchString(mod) { 75 http.Error(w, "invalid module@version syntax", http.StatusBadRequest) 76 return 77 } 78 i := strings.Index(mod, "@") 79 escPath, escVers := mod[:i], mod[i+1:] 80 path, err := module.UnescapePath(escPath) 81 if err != nil { 82 reportError(w, err) 83 return 84 } 85 vers, err := module.UnescapeVersion(escVers) 86 if err != nil { 87 reportError(w, err) 88 return 89 } 90 id, err := s.ops.Lookup(ctx, module.Version{Path: path, Version: vers}) 91 if err != nil { 92 reportError(w, err) 93 return 94 } 95 records, err := s.ops.ReadRecords(ctx, id, 1) 96 if err != nil { 97 // This should never happen - the lookup says the record exists. 98 http.Error(w, err.Error(), http.StatusInternalServerError) 99 return 100 } 101 if len(records) != 1 { 102 http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError) 103 return 104 } 105 msg, err := tlog.FormatRecord(id, records[0]) 106 if err != nil { 107 http.Error(w, err.Error(), http.StatusInternalServerError) 108 return 109 } 110 signed, err := s.ops.Signed(ctx) 111 if err != nil { 112 http.Error(w, err.Error(), http.StatusInternalServerError) 113 return 114 } 115 w.Header().Set("Content-Type", "text/plain; charset=UTF-8") 116 w.Write(msg) 117 w.Write(signed) 118 119 case r.URL.Path == "/latest": 120 data, err := s.ops.Signed(ctx) 121 if err != nil { 122 http.Error(w, err.Error(), http.StatusInternalServerError) 123 return 124 } 125 w.Header().Set("Content-Type", "text/plain; charset=UTF-8") 126 w.Write(data) 127 128 case strings.HasPrefix(r.URL.Path, "/tile/"): 129 t, err := tlog.ParseTilePath(r.URL.Path[1:]) 130 if err != nil { 131 http.Error(w, "invalid tile syntax", http.StatusBadRequest) 132 return 133 } 134 if t.L == -1 { 135 // Record data. 136 start := t.N << uint(t.H) 137 records, err := s.ops.ReadRecords(ctx, start, int64(t.W)) 138 if err != nil { 139 reportError(w, err) 140 return 141 } 142 if len(records) != t.W { 143 http.Error(w, "invalid record count returned by ReadRecords", http.StatusInternalServerError) 144 return 145 } 146 var data []byte 147 for i, text := range records { 148 msg, err := tlog.FormatRecord(start+int64(i), text) 149 if err != nil { 150 http.Error(w, err.Error(), http.StatusInternalServerError) 151 return 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 }