github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/http/server.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:43</date> 10 //</624450112046764032> 11 12 13 /* 14 Swarm的简单HTTP服务器接口 15 **/ 16 17 package http 18 19 import ( 20 "bufio" 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "mime" 27 "mime/multipart" 28 "net/http" 29 "os" 30 "path" 31 "strconv" 32 "strings" 33 "time" 34 35 "github.com/ethereum/go-ethereum/common" 36 "github.com/ethereum/go-ethereum/metrics" 37 "github.com/ethereum/go-ethereum/swarm/api" 38 "github.com/ethereum/go-ethereum/swarm/log" 39 "github.com/ethereum/go-ethereum/swarm/storage" 40 "github.com/ethereum/go-ethereum/swarm/storage/feed" 41 "github.com/rs/cors" 42 ) 43 44 var ( 45 postRawCount = metrics.NewRegisteredCounter("api.http.post.raw.count", nil) 46 postRawFail = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil) 47 postFilesCount = metrics.NewRegisteredCounter("api.http.post.files.count", nil) 48 postFilesFail = metrics.NewRegisteredCounter("api.http.post.files.fail", nil) 49 deleteCount = metrics.NewRegisteredCounter("api.http.delete.count", nil) 50 deleteFail = metrics.NewRegisteredCounter("api.http.delete.fail", nil) 51 getCount = metrics.NewRegisteredCounter("api.http.get.count", nil) 52 getFail = metrics.NewRegisteredCounter("api.http.get.fail", nil) 53 getFileCount = metrics.NewRegisteredCounter("api.http.get.file.count", nil) 54 getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil) 55 getFileFail = metrics.NewRegisteredCounter("api.http.get.file.fail", nil) 56 getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil) 57 getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil) 58 ) 59 60 type methodHandler map[string]http.Handler 61 62 func (m methodHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 63 v, ok := m[r.Method] 64 if ok { 65 v.ServeHTTP(rw, r) 66 return 67 } 68 rw.WriteHeader(http.StatusMethodNotAllowed) 69 } 70 71 func NewServer(api *api.API, corsString string) *Server { 72 var allowedOrigins []string 73 for _, domain := range strings.Split(corsString, ",") { 74 allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) 75 } 76 c := cors.New(cors.Options{ 77 AllowedOrigins: allowedOrigins, 78 AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodPatch, http.MethodPut}, 79 MaxAge: 600, 80 AllowedHeaders: []string{"*"}, 81 }) 82 83 server := &Server{api: api} 84 85 defaultMiddlewares := []Adapter{ 86 RecoverPanic, 87 SetRequestID, 88 SetRequestHost, 89 InitLoggingResponseWriter, 90 ParseURI, 91 InstrumentOpenTracing, 92 } 93 94 mux := http.NewServeMux() 95 mux.Handle("/bzz:/", methodHandler{ 96 "GET": Adapt( 97 http.HandlerFunc(server.HandleBzzGet), 98 defaultMiddlewares..., 99 ), 100 "POST": Adapt( 101 http.HandlerFunc(server.HandlePostFiles), 102 defaultMiddlewares..., 103 ), 104 "DELETE": Adapt( 105 http.HandlerFunc(server.HandleDelete), 106 defaultMiddlewares..., 107 ), 108 }) 109 mux.Handle("/bzz-raw:/", methodHandler{ 110 "GET": Adapt( 111 http.HandlerFunc(server.HandleGet), 112 defaultMiddlewares..., 113 ), 114 "POST": Adapt( 115 http.HandlerFunc(server.HandlePostRaw), 116 defaultMiddlewares..., 117 ), 118 }) 119 mux.Handle("/bzz-immutable:/", methodHandler{ 120 "GET": Adapt( 121 http.HandlerFunc(server.HandleBzzGet), 122 defaultMiddlewares..., 123 ), 124 }) 125 mux.Handle("/bzz-hash:/", methodHandler{ 126 "GET": Adapt( 127 http.HandlerFunc(server.HandleGet), 128 defaultMiddlewares..., 129 ), 130 }) 131 mux.Handle("/bzz-list:/", methodHandler{ 132 "GET": Adapt( 133 http.HandlerFunc(server.HandleGetList), 134 defaultMiddlewares..., 135 ), 136 }) 137 mux.Handle("/bzz-feed:/", methodHandler{ 138 "GET": Adapt( 139 http.HandlerFunc(server.HandleGetFeed), 140 defaultMiddlewares..., 141 ), 142 "POST": Adapt( 143 http.HandlerFunc(server.HandlePostFeed), 144 defaultMiddlewares..., 145 ), 146 }) 147 148 mux.Handle("/", methodHandler{ 149 "GET": Adapt( 150 http.HandlerFunc(server.HandleRootPaths), 151 SetRequestID, 152 InitLoggingResponseWriter, 153 ), 154 }) 155 server.Handler = c.Handler(mux) 156 157 return server 158 } 159 160 func (s *Server) ListenAndServe(addr string) error { 161 s.listenAddr = addr 162 return http.ListenAndServe(addr, s) 163 } 164 165 //用于注册BZZ URL方案处理程序的浏览器API: 166 //https://developer.mozilla.org/en/docs/web-based_协议处理程序 167 //用于注册BZZ URL方案处理程序的电子(铬)API: 168 //https://github.com/atom/electron/blob/master/docs/api/protocol.md网站 169 type Server struct { 170 http.Handler 171 api *api.API 172 listenAddr string 173 } 174 175 func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) { 176 log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI) 177 if r.Header.Get("Accept") == "application/x-tar" { 178 uri := GetURI(r.Context()) 179 _, credentials, _ := r.BasicAuth() 180 reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri) 181 if err != nil { 182 if isDecryptError(err) { 183 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String())) 184 respondError(w, r, err.Error(), http.StatusUnauthorized) 185 return 186 } 187 respondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError) 188 return 189 } 190 defer reader.Close() 191 192 w.Header().Set("Content-Type", "application/x-tar") 193 194 fileName := uri.Addr 195 if found := path.Base(uri.Path); found != "" && found != "." && found != "/" { 196 fileName = found 197 } 198 w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.tar\"", fileName)) 199 200 w.WriteHeader(http.StatusOK) 201 io.Copy(w, reader) 202 return 203 } 204 205 s.HandleGetFile(w, r) 206 } 207 208 func (s *Server) HandleRootPaths(w http.ResponseWriter, r *http.Request) { 209 switch r.RequestURI { 210 case "/": 211 respondTemplate(w, r, "landing-page", "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 200) 212 return 213 case "/robots.txt": 214 w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat)) 215 fmt.Fprintf(w, "User-agent: *\nDisallow: /") 216 case "/favicon.ico": 217 w.WriteHeader(http.StatusOK) 218 w.Write(faviconBytes) 219 default: 220 respondError(w, r, "Not Found", http.StatusNotFound) 221 } 222 } 223 224 //handlePostraw处理对raw bzz raw的post请求:/uri,存储该请求 225 //swarm中的body并返回结果存储地址作为文本/纯响应 226 func (s *Server) HandlePostRaw(w http.ResponseWriter, r *http.Request) { 227 ruid := GetRUID(r.Context()) 228 log.Debug("handle.post.raw", "ruid", ruid) 229 230 postRawCount.Inc(1) 231 232 toEncrypt := false 233 uri := GetURI(r.Context()) 234 if uri.Addr == "encrypt" { 235 toEncrypt = true 236 } 237 238 if uri.Path != "" { 239 postRawFail.Inc(1) 240 respondError(w, r, "raw POST request cannot contain a path", http.StatusBadRequest) 241 return 242 } 243 244 if uri.Addr != "" && uri.Addr != "encrypt" { 245 postRawFail.Inc(1) 246 respondError(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest) 247 return 248 } 249 250 if r.Header.Get("Content-Length") == "" { 251 postRawFail.Inc(1) 252 respondError(w, r, "missing Content-Length header in request", http.StatusBadRequest) 253 return 254 } 255 256 addr, _, err := s.api.Store(r.Context(), r.Body, r.ContentLength, toEncrypt) 257 if err != nil { 258 postRawFail.Inc(1) 259 respondError(w, r, err.Error(), http.StatusInternalServerError) 260 return 261 } 262 263 log.Debug("stored content", "ruid", ruid, "key", addr) 264 265 w.Header().Set("Content-Type", "text/plain") 266 w.WriteHeader(http.StatusOK) 267 fmt.Fprint(w, addr) 268 } 269 270 //handlePostfiles处理对 271 //bzz:/<hash>/<path>其中包含单个文件或多个文件 272 //(tar存档或多部分表单),将这些文件添加到 273 //现有清单或<path>下的新清单,并返回 274 //作为文本/纯响应的结果清单哈希 275 func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) { 276 ruid := GetRUID(r.Context()) 277 log.Debug("handle.post.files", "ruid", ruid) 278 postFilesCount.Inc(1) 279 280 contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 281 if err != nil { 282 postFilesFail.Inc(1) 283 respondError(w, r, err.Error(), http.StatusBadRequest) 284 return 285 } 286 287 toEncrypt := false 288 uri := GetURI(r.Context()) 289 if uri.Addr == "encrypt" { 290 toEncrypt = true 291 } 292 293 var addr storage.Address 294 if uri.Addr != "" && uri.Addr != "encrypt" { 295 addr, err = s.api.Resolve(r.Context(), uri.Addr) 296 if err != nil { 297 postFilesFail.Inc(1) 298 respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError) 299 return 300 } 301 log.Debug("resolved key", "ruid", ruid, "key", addr) 302 } else { 303 addr, err = s.api.NewManifest(r.Context(), toEncrypt) 304 if err != nil { 305 postFilesFail.Inc(1) 306 respondError(w, r, err.Error(), http.StatusInternalServerError) 307 return 308 } 309 log.Debug("new manifest", "ruid", ruid, "key", addr) 310 } 311 312 newAddr, err := s.api.UpdateManifest(r.Context(), addr, func(mw *api.ManifestWriter) error { 313 switch contentType { 314 case "application/x-tar": 315 _, err := s.handleTarUpload(r, mw) 316 if err != nil { 317 respondError(w, r, fmt.Sprintf("error uploading tarball: %v", err), http.StatusInternalServerError) 318 return err 319 } 320 return nil 321 case "multipart/form-data": 322 return s.handleMultipartUpload(r, params["boundary"], mw) 323 324 default: 325 return s.handleDirectUpload(r, mw) 326 } 327 }) 328 if err != nil { 329 postFilesFail.Inc(1) 330 respondError(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError) 331 return 332 } 333 334 log.Debug("stored content", "ruid", ruid, "key", newAddr) 335 336 w.Header().Set("Content-Type", "text/plain") 337 w.WriteHeader(http.StatusOK) 338 fmt.Fprint(w, newAddr) 339 } 340 341 func (s *Server) handleTarUpload(r *http.Request, mw *api.ManifestWriter) (storage.Address, error) { 342 log.Debug("handle.tar.upload", "ruid", GetRUID(r.Context())) 343 344 defaultPath := r.URL.Query().Get("defaultpath") 345 346 key, err := s.api.UploadTar(r.Context(), r.Body, GetURI(r.Context()).Path, defaultPath, mw) 347 if err != nil { 348 return nil, err 349 } 350 return key, nil 351 } 352 353 func (s *Server) handleMultipartUpload(r *http.Request, boundary string, mw *api.ManifestWriter) error { 354 ruid := GetRUID(r.Context()) 355 log.Debug("handle.multipart.upload", "ruid", ruid) 356 mr := multipart.NewReader(r.Body, boundary) 357 for { 358 part, err := mr.NextPart() 359 if err == io.EOF { 360 return nil 361 } else if err != nil { 362 return fmt.Errorf("error reading multipart form: %s", err) 363 } 364 365 var size int64 366 var reader io.Reader 367 if contentLength := part.Header.Get("Content-Length"); contentLength != "" { 368 size, err = strconv.ParseInt(contentLength, 10, 64) 369 if err != nil { 370 return fmt.Errorf("error parsing multipart content length: %s", err) 371 } 372 reader = part 373 } else { 374 //将部件复制到tmp文件以获取其大小 375 tmp, err := ioutil.TempFile("", "swarm-multipart") 376 if err != nil { 377 return err 378 } 379 defer os.Remove(tmp.Name()) 380 defer tmp.Close() 381 size, err = io.Copy(tmp, part) 382 if err != nil { 383 return fmt.Errorf("error copying multipart content: %s", err) 384 } 385 if _, err := tmp.Seek(0, io.SeekStart); err != nil { 386 return fmt.Errorf("error copying multipart content: %s", err) 387 } 388 reader = tmp 389 } 390 391 //在请求的路径下添加条目 392 name := part.FileName() 393 if name == "" { 394 name = part.FormName() 395 } 396 uri := GetURI(r.Context()) 397 path := path.Join(uri.Path, name) 398 entry := &api.ManifestEntry{ 399 Path: path, 400 ContentType: part.Header.Get("Content-Type"), 401 Size: size, 402 } 403 log.Debug("adding path to new manifest", "ruid", ruid, "bytes", entry.Size, "path", entry.Path) 404 contentKey, err := mw.AddEntry(r.Context(), reader, entry) 405 if err != nil { 406 return fmt.Errorf("error adding manifest entry from multipart form: %s", err) 407 } 408 log.Debug("stored content", "ruid", ruid, "key", contentKey) 409 } 410 } 411 412 func (s *Server) handleDirectUpload(r *http.Request, mw *api.ManifestWriter) error { 413 ruid := GetRUID(r.Context()) 414 log.Debug("handle.direct.upload", "ruid", ruid) 415 key, err := mw.AddEntry(r.Context(), r.Body, &api.ManifestEntry{ 416 Path: GetURI(r.Context()).Path, 417 ContentType: r.Header.Get("Content-Type"), 418 Mode: 0644, 419 Size: r.ContentLength, 420 }) 421 if err != nil { 422 return err 423 } 424 log.Debug("stored content", "ruid", ruid, "key", key) 425 return nil 426 } 427 428 //handleDelete处理对bzz:/<manifest>/<path>的删除请求,删除 429 //<path>from<manifest>并返回结果清单哈希作为 430 //文本/纯响应 431 func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) { 432 ruid := GetRUID(r.Context()) 433 uri := GetURI(r.Context()) 434 log.Debug("handle.delete", "ruid", ruid) 435 deleteCount.Inc(1) 436 newKey, err := s.api.Delete(r.Context(), uri.Addr, uri.Path) 437 if err != nil { 438 deleteFail.Inc(1) 439 respondError(w, r, fmt.Sprintf("could not delete from manifest: %v", err), http.StatusInternalServerError) 440 return 441 } 442 443 w.Header().Set("Content-Type", "text/plain") 444 w.WriteHeader(http.StatusOK) 445 fmt.Fprint(w, newKey) 446 } 447 448 //处理源清单创建和源更新 449 //post请求允许feeds包中定义的JSON结构:`feed.updateRequestJSON` 450 //请求可以是a)创建源清单,b)更新源,或c)a+b:创建源清单并发布第一个更新 451 func (s *Server) HandlePostFeed(w http.ResponseWriter, r *http.Request) { 452 ruid := GetRUID(r.Context()) 453 uri := GetURI(r.Context()) 454 log.Debug("handle.post.feed", "ruid", ruid) 455 var err error 456 457 //创建和更新必须发送feed.updateRequestJSON JSON结构 458 body, err := ioutil.ReadAll(r.Body) 459 if err != nil { 460 respondError(w, r, err.Error(), http.StatusInternalServerError) 461 return 462 } 463 464 fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query()) 465 if err != nil { //无法分析查询字符串或检索清单 466 getFail.Inc(1) 467 httpStatus := http.StatusBadRequest 468 if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI { 469 httpStatus = http.StatusNotFound 470 } 471 respondError(w, r, fmt.Sprintf("cannot retrieve feed from manifest: %s", err), httpStatus) 472 return 473 } 474 475 var updateRequest feed.Request 476 updateRequest.Feed = *fd 477 query := r.URL.Query() 478 479 if err := updateRequest.FromValues(query, body); err != nil { //解码来自查询参数的请求 480 respondError(w, r, err.Error(), http.StatusBadRequest) 481 return 482 } 483 484 switch { 485 case updateRequest.IsUpdate(): 486 //验证签名是否完整,以及签名者是否获得授权。 487 //更新此源 488 //请尽早检查,以避免创建源,然后无法设置其第一次更新。 489 if err = updateRequest.Verify(); err != nil { 490 respondError(w, r, err.Error(), http.StatusForbidden) 491 return 492 } 493 _, err = s.api.FeedsUpdate(r.Context(), &updateRequest) 494 if err != nil { 495 respondError(w, r, err.Error(), http.StatusInternalServerError) 496 return 497 } 498 fallthrough 499 case query.Get("manifest") == "1": 500 //我们创建一个清单,以便稍后使用bzz://later检索源更新 501 //此清单具有特殊的“源类型”清单,并保存 502 //用于稍后检索源更新的源标识 503 m, err := s.api.NewFeedManifest(r.Context(), &updateRequest.Feed) 504 if err != nil { 505 respondError(w, r, fmt.Sprintf("failed to create feed manifest: %v", err), http.StatusInternalServerError) 506 return 507 } 508 //清单的密钥将传回客户端 509 //客户端可以通过其feed成员直接访问feed 510 //清单键可以设置为ENS名称的冲突解决程序中的内容 511 outdata, err := json.Marshal(m) 512 if err != nil { 513 respondError(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError) 514 return 515 } 516 fmt.Fprint(w, string(outdata)) 517 518 w.Header().Add("Content-type", "application/json") 519 default: 520 respondError(w, r, "Missing signature in feed update request", http.StatusBadRequest) 521 } 522 } 523 524 //handlegetfeed检索swarm feed更新: 525 //bzz feed://<manifest address or ens name>-获取最新的源更新,给定清单地址 526 //-或 527 //直接指定用户+主题(可选)、子主题名称(可选),不带清单: 528 //BZZ进料://?user=0x…&topic=0x…&name=subtopic名称 529 //主题默认为0x000…如果未指定。 530 //如果未指定名称,则默认为空字符串。 531 //因此,空名称和主题引用用户的默认提要。 532 // 533 //可选参数: 534 //time=xx-在time之前获取最新更新(以epoch秒为单位) 535 //hint.time=xx-提示查找算法在该时间前后查找更新 536 //hint.level=xx-提示查找算法查找此频率级别附近的更新 537 //meta=1-获取源元数据和状态信息,而不是执行源查询 538 //注意:meta=1将在不久的将来被弃用 539 func (s *Server) HandleGetFeed(w http.ResponseWriter, r *http.Request) { 540 ruid := GetRUID(r.Context()) 541 uri := GetURI(r.Context()) 542 log.Debug("handle.get.feed", "ruid", ruid) 543 var err error 544 545 fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query()) 546 if err != nil { //无法分析查询字符串或检索清单 547 getFail.Inc(1) 548 httpStatus := http.StatusBadRequest 549 if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI { 550 httpStatus = http.StatusNotFound 551 } 552 respondError(w, r, fmt.Sprintf("cannot retrieve feed information from manifest: %s", err), httpStatus) 553 return 554 } 555 556 //确定查询是指定了期间和版本,还是指定了元数据查询 557 if r.URL.Query().Get("meta") == "1" { 558 unsignedUpdateRequest, err := s.api.FeedsNewRequest(r.Context(), fd) 559 if err != nil { 560 getFail.Inc(1) 561 respondError(w, r, fmt.Sprintf("cannot retrieve feed metadata for feed=%s: %s", fd.Hex(), err), http.StatusNotFound) 562 return 563 } 564 rawResponse, err := unsignedUpdateRequest.MarshalJSON() 565 if err != nil { 566 respondError(w, r, fmt.Sprintf("cannot encode unsigned feed update request: %v", err), http.StatusInternalServerError) 567 return 568 } 569 w.Header().Add("Content-type", "application/json") 570 w.WriteHeader(http.StatusOK) 571 fmt.Fprint(w, string(rawResponse)) 572 return 573 } 574 575 lookupParams := &feed.Query{Feed: *fd} 576 if err = lookupParams.FromValues(r.URL.Query()); err != nil { //分析期间,版本 577 respondError(w, r, fmt.Sprintf("invalid feed update request:%s", err), http.StatusBadRequest) 578 return 579 } 580 581 data, err := s.api.FeedsLookup(r.Context(), lookupParams) 582 583 //switch语句中的任何错误都将在此处结束 584 if err != nil { 585 code, err2 := s.translateFeedError(w, r, "feed lookup fail", err) 586 respondError(w, r, err2.Error(), code) 587 return 588 } 589 590 //一切正常,提供检索到的更新 591 log.Debug("Found update", "feed", fd.Hex(), "ruid", ruid) 592 w.Header().Set("Content-Type", api.MimeOctetStream) 593 http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data)) 594 } 595 596 func (s *Server) translateFeedError(w http.ResponseWriter, r *http.Request, supErr string, err error) (int, error) { 597 code := 0 598 defaultErr := fmt.Errorf("%s: %v", supErr, err) 599 rsrcErr, ok := err.(*feed.Error) 600 if !ok && rsrcErr != nil { 601 code = rsrcErr.Code() 602 } 603 switch code { 604 case storage.ErrInvalidValue: 605 return http.StatusBadRequest, defaultErr 606 case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit: 607 return http.StatusNotFound, defaultErr 608 case storage.ErrUnauthorized, storage.ErrInvalidSignature: 609 return http.StatusUnauthorized, defaultErr 610 case storage.ErrDataOverflow: 611 return http.StatusRequestEntityTooLarge, defaultErr 612 } 613 614 return http.StatusInternalServerError, defaultErr 615 } 616 617 //handleget处理get请求 618 //-bzz raw://<key>并用存储在 619 //给定的存储密钥 620 //-bzz hash://<key>并用存储内容的哈希进行响应 621 //在给定的存储键上作为文本/纯响应 622 func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) { 623 ruid := GetRUID(r.Context()) 624 uri := GetURI(r.Context()) 625 log.Debug("handle.get", "ruid", ruid, "uri", uri) 626 getCount.Inc(1) 627 _, pass, _ := r.BasicAuth() 628 629 addr, err := s.api.ResolveURI(r.Context(), uri, pass) 630 if err != nil { 631 getFail.Inc(1) 632 respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 633 return 634 } 635 w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //URL的类型为bzz://<hex key>/path,因此我们确信它是不可变的。 636 637 log.Debug("handle.get: resolved", "ruid", ruid, "key", addr) 638 639 //如果设置了path,则将<key>解释为清单并返回 640 //给定路径的原始条目 641 642 etag := common.Bytes2Hex(addr) 643 noneMatchEtag := r.Header.Get("If-None-Match") 644 w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //将etag设置为manifest key或raw entry key。 645 if noneMatchEtag != "" { 646 if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) { 647 w.WriteHeader(http.StatusNotModified) 648 return 649 } 650 } 651 652 //通过检索文件的大小检查根块是否存在 653 reader, isEncrypted := s.api.Retrieve(r.Context(), addr) 654 if _, err := reader.Size(r.Context(), nil); err != nil { 655 getFail.Inc(1) 656 respondError(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound) 657 return 658 } 659 660 w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted)) 661 662 switch { 663 case uri.Raw(): 664 //允许请求使用查询覆盖内容类型 665 //参数 666 if typ := r.URL.Query().Get("content_type"); typ != "" { 667 w.Header().Set("Content-Type", typ) 668 } 669 http.ServeContent(w, r, "", time.Now(), reader) 670 case uri.Hash(): 671 w.Header().Set("Content-Type", "text/plain") 672 w.WriteHeader(http.StatusOK) 673 fmt.Fprint(w, addr) 674 } 675 } 676 677 //handleGetList处理对bzz list的get请求:/<manifest>/<path>并返回 678 //<manifest>中<path>下包含的所有文件的列表,分组为 679 //使用“/”作为分隔符的常见前缀 680 func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) { 681 ruid := GetRUID(r.Context()) 682 uri := GetURI(r.Context()) 683 _, credentials, _ := r.BasicAuth() 684 log.Debug("handle.get.list", "ruid", ruid, "uri", uri) 685 getListCount.Inc(1) 686 687 //确保根路径有一个尾随斜杠,以便相对URL工作 688 if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { 689 http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently) 690 return 691 } 692 693 addr, err := s.api.Resolve(r.Context(), uri.Addr) 694 if err != nil { 695 getListFail.Inc(1) 696 respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 697 return 698 } 699 log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr) 700 701 list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path) 702 if err != nil { 703 getListFail.Inc(1) 704 if isDecryptError(err) { 705 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String())) 706 respondError(w, r, err.Error(), http.StatusUnauthorized) 707 return 708 } 709 respondError(w, r, err.Error(), http.StatusInternalServerError) 710 return 711 } 712 713 //如果客户端需要HTML(例如浏览器),则将列表呈现为 714 //带相对URL的HTML索引 715 if strings.Contains(r.Header.Get("Accept"), "text/html") { 716 w.Header().Set("Content-Type", "text/html") 717 err := TemplatesMap["bzz-list"].Execute(w, &htmlListData{ 718 URI: &api.URI{ 719 Scheme: "bzz", 720 Addr: uri.Addr, 721 Path: uri.Path, 722 }, 723 List: &list, 724 }) 725 if err != nil { 726 getListFail.Inc(1) 727 log.Error(fmt.Sprintf("error rendering list HTML: %s", err)) 728 } 729 return 730 } 731 732 w.Header().Set("Content-Type", "application/json") 733 json.NewEncoder(w).Encode(&list) 734 } 735 736 //handlegetfile处理对bzz://<manifest>><path>的get请求并响应 737 //文件内容位于给定的<manifest> 738 func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) { 739 ruid := GetRUID(r.Context()) 740 uri := GetURI(r.Context()) 741 _, credentials, _ := r.BasicAuth() 742 log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI) 743 getFileCount.Inc(1) 744 745 //确保根路径有一个尾随斜杠,以便相对URL工作 746 if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { 747 http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently) 748 return 749 } 750 var err error 751 manifestAddr := uri.Address() 752 753 if manifestAddr == nil { 754 manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr) 755 if err != nil { 756 getFileFail.Inc(1) 757 respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 758 return 759 } 760 } else { 761 w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //URL的类型为bzz://<hex key>/path,因此我们确信它是不可变的。 762 } 763 764 log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr) 765 766 reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path) 767 768 etag := common.Bytes2Hex(contentKey) 769 noneMatchEtag := r.Header.Get("If-None-Match") 770 w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //将etag设置为实际内容键。 771 if noneMatchEtag != "" { 772 if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) { 773 w.WriteHeader(http.StatusNotModified) 774 return 775 } 776 } 777 778 if err != nil { 779 if isDecryptError(err) { 780 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr)) 781 respondError(w, r, err.Error(), http.StatusUnauthorized) 782 return 783 } 784 785 switch status { 786 case http.StatusNotFound: 787 getFileNotFound.Inc(1) 788 respondError(w, r, err.Error(), http.StatusNotFound) 789 default: 790 getFileFail.Inc(1) 791 respondError(w, r, err.Error(), http.StatusInternalServerError) 792 } 793 return 794 } 795 796 //请求导致文件不明确 797 //例如,清单中提供/readme.md和readinglist.txt。 798 if status == http.StatusMultipleChoices { 799 list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path) 800 if err != nil { 801 getFileFail.Inc(1) 802 if isDecryptError(err) { 803 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr)) 804 respondError(w, r, err.Error(), http.StatusUnauthorized) 805 return 806 } 807 respondError(w, r, err.Error(), http.StatusInternalServerError) 808 return 809 } 810 811 log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", ruid) 812 //显示指向可用条目的良好页面链接 813 ShowMultipleChoices(w, r, list) 814 return 815 } 816 817 //通过检索文件的大小检查根块是否存在 818 if _, err := reader.Size(r.Context(), nil); err != nil { 819 getFileNotFound.Inc(1) 820 respondError(w, r, fmt.Sprintf("file not found %s: %s", uri, err), http.StatusNotFound) 821 return 822 } 823 824 if contentType != "" { 825 w.Header().Set("Content-Type", contentType) 826 } 827 828 fileName := uri.Addr 829 if found := path.Base(uri.Path); found != "" && found != "." && found != "/" { 830 fileName = found 831 } 832 w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName)) 833 834 http.ServeContent(w, r, fileName, time.Now(), newBufferedReadSeeker(reader, getFileBufferSize)) 835 } 836 837 //在Lazychunkreader上用于bufio.reader的缓冲区大小传递给 838 //handlegetfile中的http.serveContent。 839 //警告:此值影响块请求和chunker join goroutine的数量 840 //按文件请求。 841 //建议值是IO的4倍。复制默认缓冲区值32KB。 842 const getFileBufferSize = 4 * 32 * 1024 843 844 //BufferedReadSeeker将Bufio.Reader包装为公开Seek方法 845 //来自NewBufferedReadSeeker中提供的IO.readSeeker。 846 type bufferedReadSeeker struct { 847 r io.Reader 848 s io.Seeker 849 } 850 851 //NewBufferedReadSeeker创建BufferedReadSeeker的新实例, 852 //输出IO.readseeker。参数“size”是读取缓冲区的大小。 853 func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker { 854 return bufferedReadSeeker{ 855 r: bufio.NewReaderSize(readSeeker, size), 856 s: readSeeker, 857 } 858 } 859 860 func (b bufferedReadSeeker) Read(p []byte) (n int, err error) { 861 return b.r.Read(p) 862 } 863 864 func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) { 865 return b.s.Seek(offset, whence) 866 } 867 868 type loggingResponseWriter struct { 869 http.ResponseWriter 870 statusCode int 871 } 872 873 func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { 874 return &loggingResponseWriter{w, http.StatusOK} 875 } 876 877 func (lrw *loggingResponseWriter) WriteHeader(code int) { 878 lrw.statusCode = code 879 lrw.ResponseWriter.WriteHeader(code) 880 } 881 882 func isDecryptError(err error) bool { 883 return strings.Contains(err.Error(), api.ErrDecrypt.Error()) 884 } 885