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 }