github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/swarm/api/http/server.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 // 10 // 11 // 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 25 /* 26 27 */ 28 29 package http 30 31 import ( 32 "bufio" 33 "bytes" 34 "encoding/json" 35 "fmt" 36 "io" 37 "io/ioutil" 38 "mime" 39 "mime/multipart" 40 "net/http" 41 "os" 42 "path" 43 "regexp" 44 "strconv" 45 "strings" 46 "time" 47 48 "github.com/ethereum/go-ethereum/common" 49 "github.com/ethereum/go-ethereum/metrics" 50 "github.com/ethereum/go-ethereum/swarm/api" 51 "github.com/ethereum/go-ethereum/swarm/log" 52 "github.com/ethereum/go-ethereum/swarm/storage" 53 "github.com/ethereum/go-ethereum/swarm/storage/mru" 54 55 "github.com/rs/cors" 56 ) 57 58 type resourceResponse struct { 59 Manifest storage.Address `json:"manifest"` 60 Resource string `json:"resource"` 61 Update storage.Address `json:"update"` 62 } 63 64 var ( 65 postRawCount = metrics.NewRegisteredCounter("api.http.post.raw.count", nil) 66 postRawFail = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil) 67 postFilesCount = metrics.NewRegisteredCounter("api.http.post.files.count", nil) 68 postFilesFail = metrics.NewRegisteredCounter("api.http.post.files.fail", nil) 69 deleteCount = metrics.NewRegisteredCounter("api.http.delete.count", nil) 70 deleteFail = metrics.NewRegisteredCounter("api.http.delete.fail", nil) 71 getCount = metrics.NewRegisteredCounter("api.http.get.count", nil) 72 getFail = metrics.NewRegisteredCounter("api.http.get.fail", nil) 73 getFileCount = metrics.NewRegisteredCounter("api.http.get.file.count", nil) 74 getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil) 75 getFileFail = metrics.NewRegisteredCounter("api.http.get.file.fail", nil) 76 getListCount = metrics.NewRegisteredCounter("api.http.get.list.count", nil) 77 getListFail = metrics.NewRegisteredCounter("api.http.get.list.fail", nil) 78 ) 79 80 type methodHandler map[string]http.Handler 81 82 func (m methodHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 83 v, ok := m[r.Method] 84 if ok { 85 v.ServeHTTP(rw, r) 86 return 87 } 88 rw.WriteHeader(http.StatusMethodNotAllowed) 89 } 90 91 func NewServer(api *api.API, corsString string) *Server { 92 var allowedOrigins []string 93 for _, domain := range strings.Split(corsString, ",") { 94 allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) 95 } 96 c := cors.New(cors.Options{ 97 AllowedOrigins: allowedOrigins, 98 AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodPatch, http.MethodPut}, 99 MaxAge: 600, 100 AllowedHeaders: []string{"*"}, 101 }) 102 103 server := &Server{api: api} 104 105 defaultMiddlewares := []Adapter{ 106 RecoverPanic, 107 SetRequestID, 108 SetRequestHost, 109 InitLoggingResponseWriter, 110 ParseURI, 111 InstrumentOpenTracing, 112 } 113 114 mux := http.NewServeMux() 115 mux.Handle("/bzz:/", methodHandler{ 116 "GET": Adapt( 117 http.HandlerFunc(server.HandleBzzGet), 118 defaultMiddlewares..., 119 ), 120 "POST": Adapt( 121 http.HandlerFunc(server.HandlePostFiles), 122 defaultMiddlewares..., 123 ), 124 "DELETE": Adapt( 125 http.HandlerFunc(server.HandleDelete), 126 defaultMiddlewares..., 127 ), 128 }) 129 mux.Handle("/bzz-raw:/", methodHandler{ 130 "GET": Adapt( 131 http.HandlerFunc(server.HandleGet), 132 defaultMiddlewares..., 133 ), 134 "POST": Adapt( 135 http.HandlerFunc(server.HandlePostRaw), 136 defaultMiddlewares..., 137 ), 138 }) 139 mux.Handle("/bzz-immutable:/", methodHandler{ 140 "GET": Adapt( 141 http.HandlerFunc(server.HandleGet), 142 defaultMiddlewares..., 143 ), 144 }) 145 mux.Handle("/bzz-hash:/", methodHandler{ 146 "GET": Adapt( 147 http.HandlerFunc(server.HandleGet), 148 defaultMiddlewares..., 149 ), 150 }) 151 mux.Handle("/bzz-list:/", methodHandler{ 152 "GET": Adapt( 153 http.HandlerFunc(server.HandleGetList), 154 defaultMiddlewares..., 155 ), 156 }) 157 mux.Handle("/bzz-resource:/", methodHandler{ 158 "GET": Adapt( 159 http.HandlerFunc(server.HandleGetResource), 160 defaultMiddlewares..., 161 ), 162 "POST": Adapt( 163 http.HandlerFunc(server.HandlePostResource), 164 defaultMiddlewares..., 165 ), 166 }) 167 168 mux.Handle("/", methodHandler{ 169 "GET": Adapt( 170 http.HandlerFunc(server.HandleRootPaths), 171 SetRequestID, 172 InitLoggingResponseWriter, 173 ), 174 }) 175 server.Handler = c.Handler(mux) 176 177 return server 178 } 179 180 func (s *Server) ListenAndServe(addr string) error { 181 s.listenAddr = addr 182 return http.ListenAndServe(addr, s) 183 } 184 185 // 186 // 187 // 188 // 189 type Server struct { 190 http.Handler 191 api *api.API 192 listenAddr string 193 } 194 195 func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) { 196 log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI) 197 if r.Header.Get("Accept") == "application/x-tar" { 198 uri := GetURI(r.Context()) 199 _, credentials, _ := r.BasicAuth() 200 reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri) 201 if err != nil { 202 if isDecryptError(err) { 203 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String())) 204 RespondError(w, r, err.Error(), http.StatusUnauthorized) 205 return 206 } 207 RespondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError) 208 return 209 } 210 defer reader.Close() 211 212 w.Header().Set("Content-Type", "application/x-tar") 213 w.WriteHeader(http.StatusOK) 214 io.Copy(w, reader) 215 return 216 } 217 218 s.HandleGetFile(w, r) 219 } 220 221 func (s *Server) HandleRootPaths(w http.ResponseWriter, r *http.Request) { 222 switch r.RequestURI { 223 case "/": 224 RespondTemplate(w, r, "landing-page", "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 200) 225 return 226 case "/robots.txt": 227 w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat)) 228 fmt.Fprintf(w, "User-agent: *\nDisallow: /") 229 case "/favicon.ico": 230 w.WriteHeader(http.StatusOK) 231 w.Write(faviconBytes) 232 default: 233 RespondError(w, r, "Not Found", http.StatusNotFound) 234 } 235 } 236 237 // 238 // 239 func (s *Server) HandlePostRaw(w http.ResponseWriter, r *http.Request) { 240 ruid := GetRUID(r.Context()) 241 log.Debug("handle.post.raw", "ruid", ruid) 242 243 postRawCount.Inc(1) 244 245 toEncrypt := false 246 uri := GetURI(r.Context()) 247 if uri.Addr == "encrypt" { 248 toEncrypt = true 249 } 250 251 if uri.Path != "" { 252 postRawFail.Inc(1) 253 RespondError(w, r, "raw POST request cannot contain a path", http.StatusBadRequest) 254 return 255 } 256 257 if uri.Addr != "" && uri.Addr != "encrypt" { 258 postRawFail.Inc(1) 259 RespondError(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest) 260 return 261 } 262 263 if r.Header.Get("Content-Length") == "" { 264 postRawFail.Inc(1) 265 RespondError(w, r, "missing Content-Length header in request", http.StatusBadRequest) 266 return 267 } 268 269 addr, _, err := s.api.Store(r.Context(), r.Body, r.ContentLength, toEncrypt) 270 if err != nil { 271 postRawFail.Inc(1) 272 RespondError(w, r, err.Error(), http.StatusInternalServerError) 273 return 274 } 275 276 log.Debug("stored content", "ruid", ruid, "key", addr) 277 278 w.Header().Set("Content-Type", "text/plain") 279 w.WriteHeader(http.StatusOK) 280 fmt.Fprint(w, addr) 281 } 282 283 // 284 // 285 // 286 // 287 // 288 func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) { 289 ruid := GetRUID(r.Context()) 290 log.Debug("handle.post.files", "ruid", ruid) 291 postFilesCount.Inc(1) 292 293 contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 294 if err != nil { 295 postFilesFail.Inc(1) 296 RespondError(w, r, err.Error(), http.StatusBadRequest) 297 return 298 } 299 300 toEncrypt := false 301 uri := GetURI(r.Context()) 302 if uri.Addr == "encrypt" { 303 toEncrypt = true 304 } 305 306 var addr storage.Address 307 if uri.Addr != "" && uri.Addr != "encrypt" { 308 addr, err = s.api.Resolve(r.Context(), uri.Addr) 309 if err != nil { 310 postFilesFail.Inc(1) 311 RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError) 312 return 313 } 314 log.Debug("resolved key", "ruid", ruid, "key", addr) 315 } else { 316 addr, err = s.api.NewManifest(r.Context(), toEncrypt) 317 if err != nil { 318 postFilesFail.Inc(1) 319 RespondError(w, r, err.Error(), http.StatusInternalServerError) 320 return 321 } 322 log.Debug("new manifest", "ruid", ruid, "key", addr) 323 } 324 325 newAddr, err := s.api.UpdateManifest(r.Context(), addr, func(mw *api.ManifestWriter) error { 326 switch contentType { 327 case "application/x-tar": 328 _, err := s.handleTarUpload(r, mw) 329 if err != nil { 330 RespondError(w, r, fmt.Sprintf("error uploading tarball: %v", err), http.StatusInternalServerError) 331 return err 332 } 333 return nil 334 case "multipart/form-data": 335 return s.handleMultipartUpload(r, params["boundary"], mw) 336 337 default: 338 return s.handleDirectUpload(r, mw) 339 } 340 }) 341 if err != nil { 342 postFilesFail.Inc(1) 343 RespondError(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError) 344 return 345 } 346 347 log.Debug("stored content", "ruid", ruid, "key", newAddr) 348 349 w.Header().Set("Content-Type", "text/plain") 350 w.WriteHeader(http.StatusOK) 351 fmt.Fprint(w, newAddr) 352 } 353 354 func (s *Server) handleTarUpload(r *http.Request, mw *api.ManifestWriter) (storage.Address, error) { 355 log.Debug("handle.tar.upload", "ruid", GetRUID(r.Context())) 356 357 defaultPath := r.URL.Query().Get("defaultpath") 358 359 key, err := s.api.UploadTar(r.Context(), r.Body, GetURI(r.Context()).Path, defaultPath, mw) 360 if err != nil { 361 return nil, err 362 } 363 return key, nil 364 } 365 366 func (s *Server) handleMultipartUpload(r *http.Request, boundary string, mw *api.ManifestWriter) error { 367 ruid := GetRUID(r.Context()) 368 log.Debug("handle.multipart.upload", "ruid", ruid) 369 mr := multipart.NewReader(r.Body, boundary) 370 for { 371 part, err := mr.NextPart() 372 if err == io.EOF { 373 return nil 374 } else if err != nil { 375 return fmt.Errorf("error reading multipart form: %s", err) 376 } 377 378 var size int64 379 var reader io.Reader = part 380 if contentLength := part.Header.Get("Content-Length"); contentLength != "" { 381 size, err = strconv.ParseInt(contentLength, 10, 64) 382 if err != nil { 383 return fmt.Errorf("error parsing multipart content length: %s", err) 384 } 385 reader = part 386 } else { 387 // 388 tmp, err := ioutil.TempFile("", "swarm-multipart") 389 if err != nil { 390 return err 391 } 392 defer os.Remove(tmp.Name()) 393 defer tmp.Close() 394 size, err = io.Copy(tmp, part) 395 if err != nil { 396 return fmt.Errorf("error copying multipart content: %s", err) 397 } 398 if _, err := tmp.Seek(0, io.SeekStart); err != nil { 399 return fmt.Errorf("error copying multipart content: %s", err) 400 } 401 reader = tmp 402 } 403 404 // 405 name := part.FileName() 406 if name == "" { 407 name = part.FormName() 408 } 409 uri := GetURI(r.Context()) 410 path := path.Join(uri.Path, name) 411 entry := &api.ManifestEntry{ 412 Path: path, 413 ContentType: part.Header.Get("Content-Type"), 414 Size: size, 415 ModTime: time.Now(), 416 } 417 log.Debug("adding path to new manifest", "ruid", ruid, "bytes", entry.Size, "path", entry.Path) 418 contentKey, err := mw.AddEntry(r.Context(), reader, entry) 419 if err != nil { 420 return fmt.Errorf("error adding manifest entry from multipart form: %s", err) 421 } 422 log.Debug("stored content", "ruid", ruid, "key", contentKey) 423 } 424 } 425 426 func (s *Server) handleDirectUpload(r *http.Request, mw *api.ManifestWriter) error { 427 ruid := GetRUID(r.Context()) 428 log.Debug("handle.direct.upload", "ruid", ruid) 429 key, err := mw.AddEntry(r.Context(), r.Body, &api.ManifestEntry{ 430 Path: GetURI(r.Context()).Path, 431 ContentType: r.Header.Get("Content-Type"), 432 Mode: 0644, 433 Size: r.ContentLength, 434 ModTime: time.Now(), 435 }) 436 if err != nil { 437 return err 438 } 439 log.Debug("stored content", "ruid", ruid, "key", key) 440 return nil 441 } 442 443 // 444 // 445 // 446 func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) { 447 ruid := GetRUID(r.Context()) 448 uri := GetURI(r.Context()) 449 log.Debug("handle.delete", "ruid", ruid) 450 deleteCount.Inc(1) 451 newKey, err := s.api.Delete(r.Context(), uri.Addr, uri.Path) 452 if err != nil { 453 deleteFail.Inc(1) 454 RespondError(w, r, fmt.Sprintf("could not delete from manifest: %v", err), http.StatusInternalServerError) 455 return 456 } 457 458 w.Header().Set("Content-Type", "text/plain") 459 w.WriteHeader(http.StatusOK) 460 fmt.Fprint(w, newKey) 461 } 462 463 // 464 // 465 // 466 // 467 // 468 // 469 func resourcePostMode(path string) (isRaw bool, frequency uint64, err error) { 470 re, err := regexp.Compile("^(raw)?/?([0-9]+)?$") 471 if err != nil { 472 return isRaw, frequency, err 473 } 474 m := re.FindAllStringSubmatch(path, 2) 475 var freqstr = "0" 476 if len(m) > 0 { 477 if m[0][1] != "" { 478 isRaw = true 479 } 480 if m[0][2] != "" { 481 freqstr = m[0][2] 482 } 483 } else if len(path) > 0 { 484 return isRaw, frequency, fmt.Errorf("invalid path") 485 } 486 frequency, err = strconv.ParseUint(freqstr, 10, 64) 487 return isRaw, frequency, err 488 } 489 490 // 491 // 492 // 493 // 494 // 495 // 496 // 497 func (s *Server) HandlePostResource(w http.ResponseWriter, r *http.Request) { 498 ruid := GetRUID(r.Context()) 499 log.Debug("handle.post.resource", "ruid", ruid) 500 var err error 501 502 // 503 body, err := ioutil.ReadAll(r.Body) 504 if err != nil { 505 RespondError(w, r, err.Error(), http.StatusInternalServerError) 506 return 507 } 508 var updateRequest mru.Request 509 if err := updateRequest.UnmarshalJSON(body); err != nil { // 510 RespondError(w, r, err.Error(), http.StatusBadRequest) // 511 return 512 } 513 514 if updateRequest.IsUpdate() { 515 // 516 // 517 // 518 if err = updateRequest.Verify(); err != nil { 519 RespondError(w, r, err.Error(), http.StatusForbidden) 520 return 521 } 522 } 523 524 if updateRequest.IsNew() { 525 err = s.api.ResourceCreate(r.Context(), &updateRequest) 526 if err != nil { 527 code, err2 := s.translateResourceError(w, r, "resource creation fail", err) 528 RespondError(w, r, err2.Error(), code) 529 return 530 } 531 } 532 533 if updateRequest.IsUpdate() { 534 _, err = s.api.ResourceUpdate(r.Context(), &updateRequest.SignedResourceUpdate) 535 if err != nil { 536 RespondError(w, r, err.Error(), http.StatusInternalServerError) 537 return 538 } 539 } 540 541 // 542 // 543 544 if updateRequest.IsNew() { 545 // 546 // 547 // 548 m, err := s.api.NewResourceManifest(r.Context(), updateRequest.RootAddr().Hex()) 549 if err != nil { 550 RespondError(w, r, fmt.Sprintf("failed to create resource manifest: %v", err), http.StatusInternalServerError) 551 return 552 } 553 554 // 555 // 556 // 557 // 558 outdata, err := json.Marshal(m) 559 if err != nil { 560 RespondError(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError) 561 return 562 } 563 fmt.Fprint(w, string(outdata)) 564 } 565 w.Header().Add("Content-type", "application/json") 566 } 567 568 // 569 // 570 // 571 // 572 // 573 // 574 // 575 func (s *Server) HandleGetResource(w http.ResponseWriter, r *http.Request) { 576 ruid := GetRUID(r.Context()) 577 uri := GetURI(r.Context()) 578 log.Debug("handle.get.resource", "ruid", ruid) 579 var err error 580 581 // 582 manifestAddr := uri.Address() 583 if manifestAddr == nil { 584 manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr) 585 if err != nil { 586 getFail.Inc(1) 587 RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 588 return 589 } 590 } else { 591 w.Header().Set("Cache-Control", "max-age=2147483648") 592 } 593 594 // 595 rootAddr, err := s.api.ResolveResourceManifest(r.Context(), manifestAddr) 596 if err != nil { 597 getFail.Inc(1) 598 RespondError(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", uri.Addr, err), http.StatusNotFound) 599 return 600 } 601 602 log.Debug("handle.get.resource: resolved", "ruid", ruid, "manifestkey", manifestAddr, "rootchunk addr", rootAddr) 603 604 // 605 var params []string 606 if len(uri.Path) > 0 { 607 if uri.Path == "meta" { 608 unsignedUpdateRequest, err := s.api.ResourceNewRequest(r.Context(), rootAddr) 609 if err != nil { 610 getFail.Inc(1) 611 RespondError(w, r, fmt.Sprintf("cannot retrieve resource metadata for rootAddr=%s: %s", rootAddr.Hex(), err), http.StatusNotFound) 612 return 613 } 614 rawResponse, err := unsignedUpdateRequest.MarshalJSON() 615 if err != nil { 616 RespondError(w, r, fmt.Sprintf("cannot encode unsigned UpdateRequest: %v", err), http.StatusInternalServerError) 617 return 618 } 619 w.Header().Add("Content-type", "application/json") 620 w.WriteHeader(http.StatusOK) 621 fmt.Fprint(w, string(rawResponse)) 622 return 623 624 } 625 626 params = strings.Split(uri.Path, "/") 627 628 } 629 var name string 630 var data []byte 631 now := time.Now() 632 633 switch len(params) { 634 case 0: // 635 name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatest(rootAddr)) 636 case 2: // 637 var version uint64 638 var period uint64 639 version, err = strconv.ParseUint(params[1], 10, 32) 640 if err != nil { 641 break 642 } 643 period, err = strconv.ParseUint(params[0], 10, 32) 644 if err != nil { 645 break 646 } 647 name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupVersion(rootAddr, uint32(period), uint32(version))) 648 case 1: // 649 var period uint64 650 period, err = strconv.ParseUint(params[0], 10, 32) 651 if err != nil { 652 break 653 } 654 name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatestVersionInPeriod(rootAddr, uint32(period))) 655 default: // 656 err = mru.NewError(storage.ErrInvalidValue, "invalid mutable resource request") 657 } 658 659 // 660 if err != nil { 661 code, err2 := s.translateResourceError(w, r, "mutable resource lookup fail", err) 662 RespondError(w, r, err2.Error(), code) 663 return 664 } 665 666 // 667 log.Debug("Found update", "name", name, "ruid", ruid) 668 w.Header().Set("Content-Type", "application/octet-stream") 669 http.ServeContent(w, r, "", now, bytes.NewReader(data)) 670 } 671 672 func (s *Server) translateResourceError(w http.ResponseWriter, r *http.Request, supErr string, err error) (int, error) { 673 code := 0 674 defaultErr := fmt.Errorf("%s: %v", supErr, err) 675 rsrcErr, ok := err.(*mru.Error) 676 if !ok && rsrcErr != nil { 677 code = rsrcErr.Code() 678 } 679 switch code { 680 case storage.ErrInvalidValue: 681 return http.StatusBadRequest, defaultErr 682 case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit: 683 return http.StatusNotFound, defaultErr 684 case storage.ErrUnauthorized, storage.ErrInvalidSignature: 685 return http.StatusUnauthorized, defaultErr 686 case storage.ErrDataOverflow: 687 return http.StatusRequestEntityTooLarge, defaultErr 688 } 689 690 return http.StatusInternalServerError, defaultErr 691 } 692 693 // 694 // 695 // 696 // 697 // 698 func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) { 699 ruid := GetRUID(r.Context()) 700 uri := GetURI(r.Context()) 701 log.Debug("handle.get", "ruid", ruid, "uri", uri) 702 getCount.Inc(1) 703 _, pass, _ := r.BasicAuth() 704 705 addr, err := s.api.ResolveURI(r.Context(), uri, pass) 706 if err != nil { 707 getFail.Inc(1) 708 RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 709 return 710 } 711 w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // 712 713 log.Debug("handle.get: resolved", "ruid", ruid, "key", addr) 714 715 // 716 // 717 718 etag := common.Bytes2Hex(addr) 719 noneMatchEtag := r.Header.Get("If-None-Match") 720 w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // 721 if noneMatchEtag != "" { 722 if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) { 723 w.WriteHeader(http.StatusNotModified) 724 return 725 } 726 } 727 728 // 729 reader, isEncrypted := s.api.Retrieve(r.Context(), addr) 730 if _, err := reader.Size(r.Context(), nil); err != nil { 731 getFail.Inc(1) 732 RespondError(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound) 733 return 734 } 735 736 w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted)) 737 738 switch { 739 case uri.Raw(): 740 // 741 // 742 contentType := "application/octet-stream" 743 if typ := r.URL.Query().Get("content_type"); typ != "" { 744 contentType = typ 745 } 746 w.Header().Set("Content-Type", contentType) 747 http.ServeContent(w, r, "", time.Now(), reader) 748 case uri.Hash(): 749 w.Header().Set("Content-Type", "text/plain") 750 w.WriteHeader(http.StatusOK) 751 fmt.Fprint(w, addr) 752 } 753 } 754 755 // 756 // 757 // 758 func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) { 759 ruid := GetRUID(r.Context()) 760 uri := GetURI(r.Context()) 761 _, credentials, _ := r.BasicAuth() 762 log.Debug("handle.get.list", "ruid", ruid, "uri", uri) 763 getListCount.Inc(1) 764 765 // 766 if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { 767 http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently) 768 return 769 } 770 771 addr, err := s.api.Resolve(r.Context(), uri.Addr) 772 if err != nil { 773 getListFail.Inc(1) 774 RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 775 return 776 } 777 log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr) 778 779 list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path) 780 if err != nil { 781 getListFail.Inc(1) 782 if isDecryptError(err) { 783 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String())) 784 RespondError(w, r, err.Error(), http.StatusUnauthorized) 785 return 786 } 787 RespondError(w, r, err.Error(), http.StatusInternalServerError) 788 return 789 } 790 791 // 792 // 793 if strings.Contains(r.Header.Get("Accept"), "text/html") { 794 w.Header().Set("Content-Type", "text/html") 795 err := TemplatesMap["bzz-list"].Execute(w, &htmlListData{ 796 URI: &api.URI{ 797 Scheme: "bzz", 798 Addr: uri.Addr, 799 Path: uri.Path, 800 }, 801 List: &list, 802 }) 803 if err != nil { 804 getListFail.Inc(1) 805 log.Error(fmt.Sprintf("error rendering list HTML: %s", err)) 806 } 807 return 808 } 809 810 w.Header().Set("Content-Type", "application/json") 811 json.NewEncoder(w).Encode(&list) 812 } 813 814 // 815 // 816 func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) { 817 ruid := GetRUID(r.Context()) 818 uri := GetURI(r.Context()) 819 _, credentials, _ := r.BasicAuth() 820 log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI) 821 getFileCount.Inc(1) 822 823 // 824 if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { 825 http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently) 826 return 827 } 828 var err error 829 manifestAddr := uri.Address() 830 831 if manifestAddr == nil { 832 manifestAddr, err = s.api.ResolveURI(r.Context(), uri, credentials) 833 if err != nil { 834 getFileFail.Inc(1) 835 RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound) 836 return 837 } 838 } else { 839 w.Header().Set("Cache-Control", "max-age=2147483648, immutable") // 840 } 841 842 log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr) 843 844 reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path) 845 846 etag := common.Bytes2Hex(contentKey) 847 noneMatchEtag := r.Header.Get("If-None-Match") 848 w.Header().Set("ETag", fmt.Sprintf("%q", etag)) // 849 if noneMatchEtag != "" { 850 if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) { 851 w.WriteHeader(http.StatusNotModified) 852 return 853 } 854 } 855 856 if err != nil { 857 if isDecryptError(err) { 858 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr)) 859 RespondError(w, r, err.Error(), http.StatusUnauthorized) 860 return 861 } 862 863 switch status { 864 case http.StatusNotFound: 865 getFileNotFound.Inc(1) 866 RespondError(w, r, err.Error(), http.StatusNotFound) 867 default: 868 getFileFail.Inc(1) 869 RespondError(w, r, err.Error(), http.StatusInternalServerError) 870 } 871 return 872 } 873 874 // 875 // 876 if status == http.StatusMultipleChoices { 877 list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path) 878 if err != nil { 879 getFileFail.Inc(1) 880 if isDecryptError(err) { 881 w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr)) 882 RespondError(w, r, err.Error(), http.StatusUnauthorized) 883 return 884 } 885 RespondError(w, r, err.Error(), http.StatusInternalServerError) 886 return 887 } 888 889 log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", ruid) 890 // 891 ShowMultipleChoices(w, r, list) 892 return 893 } 894 895 // 896 if _, err := reader.Size(r.Context(), nil); err != nil { 897 getFileNotFound.Inc(1) 898 RespondError(w, r, fmt.Sprintf("file not found %s: %s", uri, err), http.StatusNotFound) 899 return 900 } 901 902 w.Header().Set("Content-Type", contentType) 903 http.ServeContent(w, r, "", time.Now(), newBufferedReadSeeker(reader, getFileBufferSize)) 904 } 905 906 // 907 // 908 // 909 // 910 // 911 const getFileBufferSize = 4 * 32 * 1024 912 913 // 914 // 915 type bufferedReadSeeker struct { 916 r io.Reader 917 s io.Seeker 918 } 919 920 // 921 // 922 func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker { 923 return bufferedReadSeeker{ 924 r: bufio.NewReaderSize(readSeeker, size), 925 s: readSeeker, 926 } 927 } 928 929 func (b bufferedReadSeeker) Read(p []byte) (n int, err error) { 930 return b.r.Read(p) 931 } 932 933 func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) { 934 return b.s.Seek(offset, whence) 935 } 936 937 type loggingResponseWriter struct { 938 http.ResponseWriter 939 statusCode int 940 } 941 942 func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { 943 return &loggingResponseWriter{w, http.StatusOK} 944 } 945 946 func (lrw *loggingResponseWriter) WriteHeader(code int) { 947 lrw.statusCode = code 948 lrw.ResponseWriter.WriteHeader(code) 949 } 950 951 func isDecryptError(err error) bool { 952 return strings.Contains(err.Error(), api.ErrDecrypt.Error()) 953 }