Commit 307b154f authored by acud's avatar acud Committed by GitHub

api: split chunk and soc api (#1143)

parent 42c12ede
......@@ -137,9 +137,8 @@ func NewApiStore(host string, port int, ssl bool) PutGetter {
// Put implements storage.Putter.
func (a *ApiStore) Put(ctx context.Context, mode storage.ModePut, chs ...swarm.Chunk) (exist []bool, err error) {
for _, ch := range chs {
addr := ch.Address().String()
buf := bytes.NewReader(ch.Data())
url := strings.Join([]string{a.baseUrl, addr}, "/")
url := strings.Join([]string{a.baseUrl}, "/")
res, err := a.Client.Post(url, "application/octet-stream", buf)
if err != nil {
return nil, err
......
......@@ -6,6 +6,7 @@ package api_test
import (
"errors"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
......@@ -88,6 +89,23 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket.
return httpClient, conn, ts.Listener.Addr().String()
}
func request(t *testing.T, client *http.Client, method, resource string, body io.Reader, responseCode int) *http.Response {
t.Helper()
req, err := http.NewRequest(method, resource, body)
if err != nil {
t.Fatal(err)
}
resp, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != responseCode {
t.Fatalf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
return resp
}
func TestParseName(t *testing.T) {
const bzzHash = "89c17d0d8018a19057314aa035e61c9d23c47581a61dd3a79a7839692c617e4d"
......
......@@ -12,9 +12,8 @@ import (
"io/ioutil"
"net/http"
"github.com/ethersphere/bee/pkg/content"
"github.com/ethersphere/bee/pkg/bmtpool"
"github.com/ethersphere/bee/pkg/netstore"
"github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/sctx"
......@@ -24,20 +23,15 @@ import (
"github.com/gorilla/mux"
)
func (s *server) chunkUploadHandler(w http.ResponseWriter, r *http.Request) {
nameOrHex := mux.Vars(r)["addr"]
address, err := s.resolveNameOrAddress(nameOrHex)
if err != nil {
s.Logger.Debugf("chunk upload: parse chunk address %s: %v", nameOrHex, err)
s.Logger.Error("chunk upload: parse chunk address")
jsonhttp.BadRequest(w, "invalid chunk address")
return
}
type chunkAddressResponse struct {
Reference swarm.Address `json:"reference"`
}
func (s *server) chunkUploadHandler(w http.ResponseWriter, r *http.Request) {
var (
tag *tags.Tag
ctx = r.Context()
err error
)
if h := r.Header.Get(SwarmTagUidHeader); h != "" {
......@@ -68,22 +62,38 @@ func (s *server) chunkUploadHandler(w http.ResponseWriter, r *http.Request) {
if jsonhttp.HandleBodyReadError(err, w) {
return
}
s.Logger.Debugf("chunk upload: read chunk data error: %v, addr %s", err, address)
s.Logger.Debugf("chunk upload: read chunk data error: %v", err)
s.Logger.Error("chunk upload: read chunk data error")
jsonhttp.InternalServerError(w, "cannot read chunk data")
return
}
chunk := swarm.NewChunk(address, data)
if !content.Valid(chunk) {
if !soc.Valid(chunk) {
s.Logger.Debugf("chunk upload: invalid chunk: %s", address)
s.Logger.Error("chunk upload: invalid chunk")
jsonhttp.BadRequest(w, nil)
return
}
if len(data) < swarm.SpanSize {
s.Logger.Debug("chunk upload: not enough data")
s.Logger.Error("chunk upload: data length")
jsonhttp.BadRequest(w, "data length")
return
}
hasher := bmtpool.Get()
defer bmtpool.Put(hasher)
err = hasher.SetSpanBytes(data[:swarm.SpanSize])
if err != nil {
s.Logger.Debugf("chunk upload: set span: %v", err)
s.Logger.Error("chunk upload: span error")
jsonhttp.InternalServerError(w, "span error")
return
}
_, err = hasher.Write(data[swarm.SpanSize:])
if err != nil {
return
}
address := swarm.NewAddress(hasher.Sum(nil))
chunk := swarm.NewChunk(address, data)
seen, err := s.Storer.Put(ctx, requestModePut(r), chunk)
if err != nil {
s.Logger.Debugf("chunk upload: chunk write error: %v, addr %s", err, address)
......@@ -113,7 +123,7 @@ func (s *server) chunkUploadHandler(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Access-Control-Expose-Headers", SwarmTagUidHeader)
jsonhttp.OK(w, nil)
jsonhttp.OK(w, chunkAddressResponse{Reference: address})
}
func (s *server) chunkGetHandler(w http.ResponseWriter, r *http.Request) {
......
......@@ -6,7 +6,6 @@ package api_test
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"testing"
......@@ -31,9 +30,9 @@ func TestChunkUploadDownload(t *testing.T) {
var (
targets = "0x222"
resource = func(addr swarm.Address) string { return "/chunks/" + addr.String() }
chunksEndpoint = "/chunks"
chunksResource = func(a swarm.Address) string { return "/chunks/" + a.String() }
resourceTargets = func(addr swarm.Address) string { return "/chunks/" + addr.String() + "?targets=" + targets }
someHash = swarm.MustParseHexAddress("aabbcc")
chunk = testingc.GenerateTestRandomChunk()
mockStatestore = statestore.NewStateStore()
logger = logging.New(ioutil.Discard, 0)
......@@ -45,42 +44,23 @@ func TestChunkUploadDownload(t *testing.T) {
})
)
t.Run("invalid chunk", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(someHash), http.StatusBadRequest,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusBadRequest),
Code: http.StatusBadRequest,
}),
)
// make sure chunk is not retrievable
_ = request(t, client, http.MethodGet, resource(someHash), nil, http.StatusNotFound)
})
t.Run("empty chunk", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(someHash), http.StatusBadRequest,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusBadRequest),
Message: "data length",
Code: http.StatusBadRequest,
}),
)
// make sure chunk is not retrievable
_ = request(t, client, http.MethodGet, resource(someHash), nil, http.StatusNotFound)
})
t.Run("ok", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
// try to fetch the same chunk
resp := request(t, client, http.MethodGet, resource(chunk.Address()), nil, http.StatusOK)
resp := request(t, client, http.MethodGet, chunksResource(chunk.Address()), nil, http.StatusOK)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
......@@ -92,12 +72,9 @@ func TestChunkUploadDownload(t *testing.T) {
})
t.Run("pin-invalid-value", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "invalid-pin"),
)
......@@ -107,12 +84,9 @@ func TestChunkUploadDownload(t *testing.T) {
}
})
t.Run("pin-header-missing", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
// Also check if the chunk is NOT pinned
......@@ -121,12 +95,9 @@ func TestChunkUploadDownload(t *testing.T) {
}
})
t.Run("pin-ok", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "True"),
)
......@@ -145,20 +116,3 @@ func TestChunkUploadDownload(t *testing.T) {
}
})
}
func request(t *testing.T, client *http.Client, method, resource string, body io.Reader, responseCode int) *http.Response {
t.Helper()
req, err := http.NewRequest(method, resource, body)
if err != nil {
t.Fatal(err)
}
resp, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != responseCode {
t.Fatalf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
return resp
}
......@@ -10,6 +10,8 @@ type Server = server
type (
BytesPostResponse = bytesPostResponse
ChunkAddressResponse = chunkAddressResponse
SocPostResponse = socPostResponse
FileUploadResponse = fileUploadResponse
TagResponse = tagResponse
TagRequest = tagRequest
......
......@@ -65,7 +65,7 @@ func TestGatewayMode(t *testing.T) {
})
// should work without pinning
jsonhttptest.Request(t, client, http.MethodPost, "/chunks/"+chunk.Address().String(), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, "/chunks", http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
)
......
......@@ -18,7 +18,6 @@ import (
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/storage/mock"
testingc "github.com/ethersphere/bee/pkg/storage/testing"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags"
)
......@@ -28,7 +27,7 @@ import (
// it assumes some state of the DB before another case is run.
func TestPinChunkHandler(t *testing.T) {
var (
resource = func(addr swarm.Address) string { return "/chunks/" + addr.String() }
chunksEndpoint = "/chunks"
chunk = testingc.GenerateTestRandomChunk()
mockStorer = mock.NewStorer()
mockStatestore = statestore.NewStateStore()
......@@ -74,12 +73,9 @@ func TestPinChunkHandler(t *testing.T) {
// unpin on a chunk which is not pinned
t.Run("unpin-while-not-pinned", func(t *testing.T) {
// Post a chunk
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
jsonhttptest.Request(t, client, http.MethodDelete, "/pin/chunks/"+chunk.Address().String(), http.StatusBadRequest,
......@@ -93,12 +89,9 @@ func TestPinChunkHandler(t *testing.T) {
// pin a existing chunk first time
t.Run("pin-chunk-1", func(t *testing.T) {
// Post a chunk
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
jsonhttptest.Request(t, client, http.MethodPost, "/pin/chunks/"+chunk.Address().String(), http.StatusOK,
......@@ -175,12 +168,9 @@ func TestPinChunkHandler(t *testing.T) {
// Add 2 chunks, pin it and check if they show up in the list
t.Run("list-chunks", func(t *testing.T) {
// Post a chunk
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
jsonhttptest.Request(t, client, http.MethodPost, "/pin/chunks/"+chunk.Address().String(), http.StatusOK,
......@@ -192,12 +182,9 @@ func TestPinChunkHandler(t *testing.T) {
// post another chunk
chunk2 := testingc.GenerateTestRandomChunk()
jsonhttptest.Request(t, client, http.MethodPost, resource(chunk2.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksEndpoint, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk2.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk2.Address()}),
)
jsonhttptest.Request(t, client, http.MethodPost, "/pin/chunks/"+chunk2.Address().String(), http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
......
......@@ -71,11 +71,21 @@ func (s *server) setupRouting() {
),
})
handle(router, "/chunks", jsonhttp.MethodHandler{
"POST": web.ChainHandlers(
jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkWithSpanSize),
web.FinalHandlerFunc(s.chunkUploadHandler),
),
})
handle(router, "/chunks/{addr}", jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.chunkGetHandler),
})
handle(router, "/soc/{owner}/{id}", jsonhttp.MethodHandler{
"POST": web.ChainHandlers(
jsonhttp.NewMaxBodyBytesHandler(swarm.ChunkWithSpanSize),
web.FinalHandlerFunc(s.chunkUploadHandler),
web.FinalHandlerFunc(s.socUploadHandler),
),
})
......
// Copyright 2021 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 (
"encoding/hex"
"errors"
"io/ioutil"
"net/http"
"github.com/ethersphere/bee/pkg/bmtpool"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/soc"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/gorilla/mux"
)
var (
errBadRequestParams = errors.New("owner, id or span is not well formed")
)
type socPostResponse struct {
Reference swarm.Address `json:"reference"`
}
func (s *server) socUploadHandler(w http.ResponseWriter, r *http.Request) {
owner, err := hex.DecodeString(mux.Vars(r)["owner"])
if err != nil {
s.Logger.Debugf("soc upload: bad owner: %v", err)
s.Logger.Error("soc upload: %v", errBadRequestParams)
jsonhttp.BadRequest(w, "bad owner")
return
}
id, err := hex.DecodeString(mux.Vars(r)["id"])
if err != nil {
s.Logger.Debugf("soc upload: bad id: %v", err)
s.Logger.Error("soc upload: %v", errBadRequestParams)
jsonhttp.BadRequest(w, "bad id")
return
}
sigStr := r.URL.Query().Get("sig")
if sigStr == "" {
s.Logger.Debugf("soc upload: empty signature")
s.Logger.Error("soc upload: empty signature")
jsonhttp.BadRequest(w, "empty signature")
return
}
sig, err := hex.DecodeString(sigStr)
if err != nil {
s.Logger.Debugf("soc upload: bad signature: %v", err)
s.Logger.Error("soc upload: bad signature")
jsonhttp.BadRequest(w, "bad signature")
return
}
data, err := ioutil.ReadAll(r.Body)
if err != nil {
if jsonhttp.HandleBodyReadError(err, w) {
return
}
s.Logger.Debugf("soc upload: read chunk data error: %v", err)
s.Logger.Error("soc upload: read chunk data error")
jsonhttp.InternalServerError(w, "cannot read chunk data")
return
}
if len(data) < swarm.SpanSize {
s.Logger.Debugf("soc upload: chunk data too short")
s.Logger.Error("soc upload: %v", errBadRequestParams)
jsonhttp.BadRequest(w, "short chunk data")
return
}
if len(data) > swarm.ChunkSize+swarm.SpanSize {
s.Logger.Debugf("soc upload: chunk data exceeds %d bytes", swarm.ChunkSize+swarm.SpanSize)
s.Logger.Error("soc upload: chunk data error")
jsonhttp.RequestEntityTooLarge(w, "payload too large")
return
}
ch, err := chunk(data)
if err != nil {
s.Logger.Debugf("soc upload: create content addressed chunk: %v", err)
s.Logger.Error("soc upload: chunk data error")
jsonhttp.BadRequest(w, "chunk data error")
return
}
chunk, err := soc.NewSignedChunk(id, ch, owner, sig)
if err != nil {
s.Logger.Debugf("soc upload: read chunk data error: %v", err)
s.Logger.Error("soc upload: read chunk data error")
jsonhttp.InternalServerError(w, "cannot read chunk data")
return
}
if !soc.Valid(chunk) {
s.Logger.Debugf("soc upload: invalid chunk: %v", err)
s.Logger.Error("soc upload: invalid chunk")
jsonhttp.Unauthorized(w, "invalid chunk")
return
}
ctx := r.Context()
_, err = s.Storer.Put(ctx, requestModePut(r), chunk)
if err != nil {
s.Logger.Debugf("soc upload: chunk write error: %v", err)
s.Logger.Error("soc upload: chunk write error")
jsonhttp.BadRequest(w, "chunk write error")
return
}
jsonhttp.Created(w, chunkAddressResponse{Reference: chunk.Address()})
}
func chunk(data []byte) (swarm.Chunk, error) {
hasher := bmtpool.Get()
defer bmtpool.Put(hasher)
err := hasher.SetSpanBytes(data[:swarm.SpanSize])
if err != nil {
return nil, err
}
_, err = hasher.Write(data[swarm.SpanSize:])
if err != nil {
return nil, err
}
return swarm.NewChunk(swarm.NewAddress(hasher.Sum(nil)), data), nil
}
// Copyright 2021 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_test
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/api"
"github.com/ethersphere/bee/pkg/crypto"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/soc"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage/mock"
testingc "github.com/ethersphere/bee/pkg/storage/testing"
"github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags"
)
func TestSoc(t *testing.T) {
var (
socResource = func(owner, id, sig string) string { return fmt.Sprintf("/soc/%s/%s?sig=%s", owner, id, sig) }
_ = testingc.GenerateTestRandomChunk()
mockStatestore = statestore.NewStateStore()
logger = logging.New(ioutil.Discard, 0)
tag = tags.NewTags(mockStatestore, logger)
_ = common.HexToAddress("8d3766440f0d7b949a5e32995d09619a7f86e632")
mockStorer = mock.NewStorer()
client, _, _ = newTestServer(t, testServerOptions{
Storer: mockStorer,
Tags: tag,
})
)
t.Run("cmpty data", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, socResource("8d3766440f0d7b949a5e32995d09619a7f86e632", "bb", "cc"), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "short chunk data",
Code: http.StatusBadRequest,
}),
)
})
t.Run("malformed id", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, socResource("8d3766440f0d7b949a5e32995d09619a7f86e632", "bbzz", "cc"), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "bad id",
Code: http.StatusBadRequest,
}),
)
})
t.Run("malformed owner", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, socResource("xyz", "aa", "bb"), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "bad owner",
Code: http.StatusBadRequest,
}),
)
})
t.Run("malformed signature", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, socResource("8d3766440f0d7b949a5e32995d09619a7f86e632", "aa", "badsig"), http.StatusBadRequest,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "bad signature",
Code: http.StatusBadRequest,
}),
)
})
t.Run("signature invalid", func(t *testing.T) {
s, owner, payload := mockSoc(t)
id := make([]byte, soc.IdSize)
// modify the sign
sig := s.Signature()
sig[12] = 0x98
sig[10] = 0x12
jsonhttptest.Request(t, client, http.MethodPost, socResource(hex.EncodeToString(owner), hex.EncodeToString(id), hex.EncodeToString(sig)), http.StatusUnauthorized,
jsonhttptest.WithRequestBody(bytes.NewReader(payload)),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "invalid chunk",
Code: http.StatusUnauthorized,
}),
)
})
t.Run("ok", func(t *testing.T) {
s, owner, payload := mockSoc(t)
id := make([]byte, soc.IdSize)
sig := s.Signature()
addr, err := s.Address()
if err != nil {
t.Fatal(err)
}
jsonhttptest.Request(t, client, http.MethodPost, socResource(hex.EncodeToString(owner), hex.EncodeToString(id), hex.EncodeToString(sig)), http.StatusCreated,
jsonhttptest.WithRequestBody(bytes.NewReader(payload)),
jsonhttptest.WithExpectedJSONResponse(api.SocPostResponse{
Reference: addr,
}),
)
// try to fetch the same chunk
rsrc := fmt.Sprintf("/chunks/" + addr.String())
resp := request(t, client, http.MethodGet, rsrc, nil, http.StatusOK)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
ch, err := s.ToChunk()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(ch.Data(), data) {
t.Fatal("data retrieved doesnt match uploaded content")
}
})
}
// returns a valid, mocked SOC
func mockSoc(t *testing.T) (*soc.Soc, []byte, []byte) {
// create a valid soc
id := make([]byte, soc.IdSize)
privKey, err := crypto.GenerateSecp256k1Key()
if err != nil {
t.Fatal(err)
}
signer := crypto.NewDefaultSigner(privKey)
bmtHashOfFoo := "2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
address := swarm.MustParseHexAddress(bmtHashOfFoo)
foo := "foo"
fooLength := len(foo)
fooBytes := make([]byte, 8+fooLength)
binary.LittleEndian.PutUint64(fooBytes, uint64(fooLength))
copy(fooBytes[8:], foo)
ch := swarm.NewChunk(address, fooBytes)
sch := soc.New(id, ch)
if err != nil {
t.Fatal(err)
}
err = sch.AddSigner(signer)
if err != nil {
t.Fatal(err)
}
_, _ = sch.ToChunk()
return sch, sch.OwnerAddress(), ch.Data()
}
......@@ -38,7 +38,7 @@ func TestTags(t *testing.T) {
filesResource = "/files"
dirResource = "/dirs"
bytesResource = "/bytes"
chunksResource = func(addr swarm.Address) string { return "/chunks/" + addr.String() }
chunksResource = "/chunks"
tagsResource = "/tags"
chunk = testingc.GenerateTestRandomChunk()
someTagName = "file.jpg"
......@@ -78,7 +78,7 @@ func TestTags(t *testing.T) {
})
t.Run("create tag with invalid id", func(t *testing.T) {
jsonhttptest.Request(t, client, http.MethodPost, chunksResource(chunk.Address()), http.StatusBadRequest,
jsonhttptest.Request(t, client, http.MethodPost, chunksResource, http.StatusBadRequest,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "cannot get tag",
......@@ -120,20 +120,14 @@ func TestTags(t *testing.T) {
t.Fatalf("sent tag name %s does not match received tag name %s", someTagName, tr.Name)
}
_ = jsonhttptest.Request(t, client, http.MethodPost, chunksResource(chunk.Address()), http.StatusOK,
_ = jsonhttptest.Request(t, client, http.MethodPost, chunksResource, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
)
rcvdHeaders := jsonhttptest.Request(t, client, http.MethodPost, chunksResource(chunk.Address()), http.StatusOK,
rcvdHeaders := jsonhttptest.Request(t, client, http.MethodPost, chunksResource, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: http.StatusText(http.StatusOK),
Code: http.StatusOK,
}),
jsonhttptest.WithExpectedJSONResponse(api.ChunkAddressResponse{Reference: chunk.Address()}),
jsonhttptest.WithRequestHeader(api.SwarmTagUidHeader, strconv.FormatUint(uint64(tr.Uid), 10)),
)
......@@ -218,7 +212,7 @@ func TestTags(t *testing.T) {
addr := test.RandomAddress()
// upload content with tag
jsonhttptest.Request(t, client, http.MethodPost, chunksResource(chunk.Address()), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodPost, chunksResource, http.StatusOK,
jsonhttptest.WithRequestBody(bytes.NewReader(chunk.Data())),
jsonhttptest.WithRequestHeader(api.SwarmTagUidHeader, fmt.Sprint(tagId)),
)
......
......@@ -58,6 +58,25 @@ func NewChunk(id Id, ch swarm.Chunk, signer crypto.Signer) (swarm.Chunk, error)
return s.ToChunk()
}
// NewChunk is a convenience function to create a single-owner chunk ready to be sent
// on the network.
func NewSignedChunk(id Id, ch swarm.Chunk, owner, sig []byte) (swarm.Chunk, error) {
s := New(id, ch)
s.signature = sig
o, err := NewOwner(owner)
if err != nil {
return nil, err
}
s.owner = o
// create chunk
socAddress, err := s.Address()
if err != nil {
return nil, err
}
return swarm.NewChunk(socAddress, s.toBytes()), nil
}
// New creates a new Soc representation from arbitrary soc id and
// a content-addressed chunk.
//
......@@ -109,6 +128,10 @@ func (s *Soc) Address() (swarm.Address, error) {
return CreateAddress(s.id, s.owner)
}
func (s *Soc) Signature() []byte {
return s.signature
}
// FromChunk recreates an Soc representation from swarm.Chunk data.
func FromChunk(sch swarm.Chunk) (*Soc, error) {
chunkData := sch.Data()
......@@ -174,19 +197,22 @@ func (s *Soc) ToChunk() (swarm.Chunk, error) {
if err != nil {
return nil, err
}
// prepare the payload
buf := bytes.NewBuffer(nil)
buf.Write(s.id)
buf.Write(signature)
buf.Write(s.Chunk.Data())
s.signature = signature
// create chunk
socAddress, err := s.Address()
if err != nil {
return nil, err
}
return swarm.NewChunk(socAddress, buf.Bytes()), nil
return swarm.NewChunk(socAddress, s.toBytes()), nil
}
func (s *Soc) toBytes() []byte {
buf := bytes.NewBuffer(nil)
buf.Write(s.id)
buf.Write(s.signature)
buf.Write(s.Chunk.Data())
return buf.Bytes()
}
// toSignDigest creates a digest suitable for signing to represent the soc.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment