github.com/ethersphere/bee/v2@v2.2.0/pkg/api/bytes.go (about)

     1  // Copyright 2020 The Swarm 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 api
     6  
     7  import (
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  	"net/http"
    12  	"strconv"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/accesscontrol"
    15  	"github.com/ethersphere/bee/v2/pkg/cac"
    16  	"github.com/ethersphere/bee/v2/pkg/file/redundancy"
    17  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    18  	"github.com/ethersphere/bee/v2/pkg/postage"
    19  	"github.com/ethersphere/bee/v2/pkg/storage"
    20  	"github.com/ethersphere/bee/v2/pkg/swarm"
    21  	"github.com/ethersphere/bee/v2/pkg/tracing"
    22  	"github.com/gorilla/mux"
    23  	"github.com/opentracing/opentracing-go/ext"
    24  	olog "github.com/opentracing/opentracing-go/log"
    25  )
    26  
    27  type bytesPostResponse struct {
    28  	Reference swarm.Address `json:"reference"`
    29  }
    30  
    31  // bytesUploadHandler handles upload of raw binary data of arbitrary length.
    32  func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) {
    33  	span, logger, ctx := s.tracer.StartSpanFromContext(r.Context(), "post_bytes", s.logger.WithName("post_bytes").Build())
    34  	defer span.Finish()
    35  
    36  	headers := struct {
    37  		BatchID        []byte           `map:"Swarm-Postage-Batch-Id" validate:"required"`
    38  		SwarmTag       uint64           `map:"Swarm-Tag"`
    39  		Pin            bool             `map:"Swarm-Pin"`
    40  		Deferred       *bool            `map:"Swarm-Deferred-Upload"`
    41  		Encrypt        bool             `map:"Swarm-Encrypt"`
    42  		RLevel         redundancy.Level `map:"Swarm-Redundancy-Level"`
    43  		Act            bool             `map:"Swarm-Act"`
    44  		HistoryAddress swarm.Address    `map:"Swarm-Act-History-Address"`
    45  	}{}
    46  	if response := s.mapStructure(r.Header, &headers); response != nil {
    47  		response("invalid header params", logger, w)
    48  		return
    49  	}
    50  
    51  	var (
    52  		tag      uint64
    53  		err      error
    54  		deferred = defaultUploadMethod(headers.Deferred)
    55  	)
    56  
    57  	ctx = redundancy.SetLevelInContext(ctx, headers.RLevel)
    58  
    59  	if deferred || headers.Pin {
    60  		tag, err = s.getOrCreateSessionID(headers.SwarmTag)
    61  		if err != nil {
    62  			logger.Debug("get or create tag failed", "error", err)
    63  			logger.Error(nil, "get or create tag failed")
    64  			switch {
    65  			case errors.Is(err, storage.ErrNotFound):
    66  				jsonhttp.NotFound(w, "tag not found")
    67  			default:
    68  				jsonhttp.InternalServerError(w, "cannot get or create tag")
    69  			}
    70  			ext.LogError(span, err, olog.String("action", "tag.create"))
    71  			return
    72  		}
    73  		span.SetTag("tagID", tag)
    74  	}
    75  
    76  	putter, err := s.newStamperPutter(ctx, putterOptions{
    77  		BatchID:  headers.BatchID,
    78  		TagID:    tag,
    79  		Pin:      headers.Pin,
    80  		Deferred: deferred,
    81  	})
    82  	if err != nil {
    83  		logger.Debug("get putter failed", "error", err)
    84  		logger.Error(nil, "get putter failed")
    85  		switch {
    86  		case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable):
    87  			jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist")
    88  		case errors.Is(err, postage.ErrNotFound):
    89  			jsonhttp.NotFound(w, "batch with id not found")
    90  		case errors.Is(err, errInvalidPostageBatch):
    91  			jsonhttp.BadRequest(w, "invalid batch id")
    92  		case errors.Is(err, errUnsupportedDevNodeOperation):
    93  			jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation)
    94  		default:
    95  			jsonhttp.BadRequest(w, nil)
    96  		}
    97  		ext.LogError(span, err, olog.String("action", "new.StamperPutter"))
    98  		return
    99  	}
   100  
   101  	ow := &cleanupOnErrWriter{
   102  		ResponseWriter: w,
   103  		onErr:          putter.Cleanup,
   104  		logger:         logger,
   105  	}
   106  
   107  	p := requestPipelineFn(putter, headers.Encrypt, headers.RLevel)
   108  	reference, err := p(ctx, r.Body)
   109  	if err != nil {
   110  		logger.Debug("split write all failed", "error", err)
   111  		logger.Error(nil, "split write all failed")
   112  		switch {
   113  		case errors.Is(err, postage.ErrBucketFull):
   114  			jsonhttp.PaymentRequired(ow, "batch is overissued")
   115  		default:
   116  			jsonhttp.InternalServerError(ow, "split write all failed")
   117  		}
   118  		ext.LogError(span, err, olog.String("action", "split.WriteAll"))
   119  		return
   120  	}
   121  
   122  	encryptedReference := reference
   123  	if headers.Act {
   124  		encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress)
   125  		if err != nil {
   126  			logger.Debug("access control upload failed", "error", err)
   127  			logger.Error(nil, "access control upload failed")
   128  			switch {
   129  			case errors.Is(err, accesscontrol.ErrNotFound):
   130  				jsonhttp.NotFound(w, "act or history entry not found")
   131  			case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity):
   132  				jsonhttp.BadRequest(w, "invalid public key")
   133  			case errors.Is(err, accesscontrol.ErrUnexpectedType):
   134  				jsonhttp.BadRequest(w, "failed to create history")
   135  			default:
   136  				jsonhttp.InternalServerError(w, errActUpload)
   137  			}
   138  			return
   139  		}
   140  	}
   141  	span.SetTag("root_address", encryptedReference)
   142  
   143  	err = putter.Done(reference)
   144  	if err != nil {
   145  		logger.Debug("done split failed", "error", err)
   146  		logger.Error(nil, "done split failed")
   147  		jsonhttp.InternalServerError(ow, "done split failed")
   148  		ext.LogError(span, err, olog.String("action", "putter.Done"))
   149  		return
   150  	}
   151  
   152  	if tag != 0 {
   153  		w.Header().Set(SwarmTagHeader, fmt.Sprint(tag))
   154  	}
   155  
   156  	span.LogFields(olog.Bool("success", true))
   157  
   158  	w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader)
   159  	jsonhttp.Created(w, bytesPostResponse{
   160  		Reference: encryptedReference,
   161  	})
   162  }
   163  
   164  // bytesGetHandler handles retrieval of raw binary data of arbitrary length.
   165  func (s *Service) bytesGetHandler(w http.ResponseWriter, r *http.Request) {
   166  	logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("get_bytes_by_address").Build())
   167  
   168  	paths := struct {
   169  		Address swarm.Address `map:"address,resolve" validate:"required"`
   170  	}{}
   171  	if response := s.mapStructure(mux.Vars(r), &paths); response != nil {
   172  		response("invalid path params", logger, w)
   173  		return
   174  	}
   175  
   176  	address := paths.Address
   177  	if v := getAddressFromContext(r.Context()); !v.IsZero() {
   178  		address = v
   179  	}
   180  
   181  	additionalHeaders := http.Header{
   182  		ContentTypeHeader: {"application/octet-stream"},
   183  	}
   184  
   185  	s.downloadHandler(logger, w, r, address, additionalHeaders, true, false)
   186  }
   187  
   188  func (s *Service) bytesHeadHandler(w http.ResponseWriter, r *http.Request) {
   189  	logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("head_bytes_by_address").Build())
   190  
   191  	paths := struct {
   192  		Address swarm.Address `map:"address,resolve" validate:"required"`
   193  	}{}
   194  	if response := s.mapStructure(mux.Vars(r), &paths); response != nil {
   195  		response("invalid path params", logger, w)
   196  		return
   197  	}
   198  
   199  	address := paths.Address
   200  	if v := getAddressFromContext(r.Context()); !v.IsZero() {
   201  		address = v
   202  	}
   203  
   204  	getter := s.storer.Download(true)
   205  
   206  	ch, err := getter.Get(r.Context(), address)
   207  	if err != nil {
   208  		logger.Debug("get root chunk failed", "chunk_address", address, "error", err)
   209  		logger.Error(nil, "get rook chunk failed")
   210  		w.WriteHeader(http.StatusNotFound)
   211  		return
   212  	}
   213  	w.Header().Add("Access-Control-Expose-Headers", "Accept-Ranges, Content-Encoding")
   214  	w.Header().Add(ContentTypeHeader, "application/octet-stream")
   215  	var span int64
   216  
   217  	if cac.Valid(ch) {
   218  		span = int64(binary.LittleEndian.Uint64(ch.Data()[:swarm.SpanSize]))
   219  	} else {
   220  		// soc
   221  		span = int64(len(ch.Data()))
   222  	}
   223  	w.Header().Set(ContentLengthHeader, strconv.FormatInt(span, 10))
   224  	w.WriteHeader(http.StatusOK) // HEAD requests do not write a body
   225  }