// Copyright 2020 The Swarm Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package api import ( "errors" "fmt" "net/http" "strings" "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/postage" "github.com/ethersphere/bee/pkg/sctx" "github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/tags" "github.com/ethersphere/bee/pkg/tracing" "github.com/gorilla/mux" ) type bytesPostResponse struct { Reference swarm.Address `json:"reference"` } // bytesUploadHandler handles upload of raw binary data of arbitrary length. func (s *server) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger) tag, created, err := s.getOrCreateTag(r.Header.Get(SwarmTagHeader)) if err != nil { logger.Debugf("bytes upload: get or create tag: %v", err) logger.Error("bytes upload: get or create tag") jsonhttp.InternalServerError(w, "cannot get or create tag") return } if !created { // only in the case when tag is sent via header (i.e. not created by this request) if estimatedTotalChunks := requestCalculateNumberOfChunks(r); estimatedTotalChunks > 0 { err = tag.IncN(tags.TotalChunks, estimatedTotalChunks) if err != nil { s.logger.Debugf("bytes upload: increment tag: %v", err) s.logger.Error("bytes upload: increment tag") jsonhttp.InternalServerError(w, "increment tag") return } } } // Add the tag to the context ctx := sctx.SetTag(r.Context(), tag) batch, err := requestPostageBatchId(r) if err != nil { logger.Debugf("bytes upload: postage batch id:%v", err) logger.Error("bytes upload: postage batch id") jsonhttp.BadRequest(w, nil) return } putter, err := newStamperPutter(s.storer, s.post, s.signer, batch) if err != nil { logger.Debugf("bytes upload: get putter:%v", err) logger.Error("bytes upload: putter") jsonhttp.BadRequest(w, nil) return } p := requestPipelineFn(putter, r) address, err := p(ctx, r.Body) if err != nil { logger.Debugf("bytes upload: split write all: %v", err) logger.Error("bytes upload: split write all") switch { case errors.Is(err, postage.ErrBucketFull): jsonhttp.PaymentRequired(w, "batch is overissued") default: jsonhttp.InternalServerError(w, nil) } return } if created { _, err = tag.DoneSplit(address) if err != nil { logger.Debugf("bytes upload: done split: %v", err) logger.Error("bytes upload: done split failed") jsonhttp.InternalServerError(w, nil) return } } if strings.ToLower(r.Header.Get(SwarmPinHeader)) == "true" { if err := s.pinning.CreatePin(ctx, address, false); err != nil { logger.Debugf("bytes upload: creation of pin for %q failed: %v", address, err) logger.Error("bytes upload: creation of pin failed") jsonhttp.InternalServerError(w, nil) return } } w.Header().Set(SwarmTagHeader, fmt.Sprint(tag.Uid)) w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) jsonhttp.Created(w, bytesPostResponse{ Reference: address, }) } // bytesGetHandler handles retrieval of raw binary data of arbitrary length. func (s *server) bytesGetHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger).Logger nameOrHex := mux.Vars(r)["address"] address, err := s.resolveNameOrAddress(nameOrHex) if err != nil { logger.Debugf("bytes: parse address %s: %v", nameOrHex, err) logger.Error("bytes: parse address error") jsonhttp.NotFound(w, nil) return } additionalHeaders := http.Header{ "Content-Type": {"application/octet-stream"}, } s.downloadHandler(w, r, address, additionalHeaders, true) }