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