Commit 338d88b7 authored by acud's avatar acud Committed by GitHub

add raw data upload api (#120)

* add raw data upload api
parent d1e0c720
// 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 (
"bytes"
"encoding/binary"
"errors"
"hash"
"io"
"io/ioutil"
"net/http"
bmtlegacy "github.com/ethersphere/bmt/legacy"
"github.com/gorilla/mux"
"golang.org/x/crypto/sha3"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/bee/pkg/swarm"
)
type bzzPostResponse struct {
Hash swarm.Address `json:"hash"`
}
func hashFunc() hash.Hash {
return sha3.NewLegacyKeccak256()
}
func (s *server) bzzUploadHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
data, err := ioutil.ReadAll(r.Body)
if err != nil {
s.Logger.Debugf("bzz: read error: %v", err)
s.Logger.Error("bzz: read error")
jsonhttp.InternalServerError(w, "cannot read request")
return
}
p := bmtlegacy.NewTreePool(hashFunc, swarm.Branches, bmtlegacy.PoolSize)
hasher := bmtlegacy.New(p)
span := len(data)
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(span))
data = append(b, data...)
err = hasher.SetSpan(int64(span))
if err != nil {
s.Logger.Debugf("bzz: hasher set span: %v", err)
s.Logger.Error("bzz: hash data error")
jsonhttp.InternalServerError(w, "cannot hash data")
return
}
_, err = hasher.Write(data[8:])
if err != nil {
s.Logger.Debugf("bzz: hasher write: %v", err)
s.Logger.Error("bzz: hash data error")
jsonhttp.InternalServerError(w, "cannot hash data")
return
}
addr := swarm.NewAddress(hasher.Sum(nil))
_, err = s.Storer.Put(ctx, storage.ModePutUpload, swarm.NewChunk(addr, data[8:]))
if err != nil {
s.Logger.Debugf("bzz: write error: %v, addr %s", err, addr)
s.Logger.Error("bzz: write error")
jsonhttp.InternalServerError(w, "write error")
return
}
jsonhttp.OK(w, bzzPostResponse{Hash: addr})
}
func (s *server) bzzGetHandler(w http.ResponseWriter, r *http.Request) {
addr := mux.Vars(r)["address"]
ctx := r.Context()
address, err := swarm.ParseHexAddress(addr)
if err != nil {
s.Logger.Debugf("bzz: parse address %s: %v", addr, err)
s.Logger.Error("bzz: parse address error")
jsonhttp.BadRequest(w, "invalid address")
return
}
chunk, err := s.Storer.Get(ctx, storage.ModeGetRequest, address)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
s.Logger.Trace("bzz: not found. addr %s", address)
jsonhttp.NotFound(w, "not found")
return
}
s.Logger.Debugf("bzz: read error: %v ,addr %s", err, address)
s.Logger.Error("bzz: read error")
jsonhttp.InternalServerError(w, "read error")
return
}
w.Header().Set("Content-Type", "application/octet-stream")
_, _ = io.Copy(w, bytes.NewReader(chunk.Data()))
}
// 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_test
import (
"bytes"
"io/ioutil"
"net/http"
"testing"
"github.com/ethersphere/bee/pkg/api"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/storage/mock"
"github.com/ethersphere/bee/pkg/swarm"
)
// TestBzz tests that the data upload api responds as expected when uploading,
// downloading and requesting a resource that cannot be found.
func TestBzz(t *testing.T) {
var (
resource = "/bzz/"
content = []byte("foo")
expHash = "2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
mockStorer = mock.NewStorer()
client, cleanup = newTestServer(t, testServerOptions{
Storer: mockStorer,
})
)
defer cleanup()
t.Run("upload", func(t *testing.T) {
jsonhttptest.ResponseDirect(t, client, http.MethodPost, resource, bytes.NewReader(content), http.StatusOK, api.BzzPostResponse{
Hash: swarm.MustParseHexAddress(expHash),
})
})
t.Run("download", func(t *testing.T) {
resp := request(t, client, http.MethodGet, resource+expHash, nil, http.StatusOK)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(data, content) {
t.Fatalf("data mismatch. got %s, want %s", string(data), string(content))
}
})
t.Run("not found", func(t *testing.T) {
jsonhttptest.ResponseDirect(t, client, http.MethodGet, resource+"abcd", nil, http.StatusNotFound, jsonhttp.StatusResponse{
Message: "not found",
Code: http.StatusNotFound,
})
})
}
......@@ -18,31 +18,21 @@ import (
"github.com/ethersphere/bee/pkg/swarm"
)
// TestChunkUpload uploads a chunk to an API that verifies the chunk according
// TestChunkUploadDownload uploads a chunk to an API that verifies the chunk according
// to a given validator, then tries to download the uploaded data.
func TestChunkUploadDownload(t *testing.T) {
resource := func(addr swarm.Address) string {
return "/bzz-chunk/" + addr.String()
}
validHash, err := swarm.ParseHexAddress("aabbcc")
if err != nil {
t.Fatal(err)
}
invalidHash, err := swarm.ParseHexAddress("bbccdd")
if err != nil {
t.Fatal(err)
}
validContent := []byte("bbaatt")
invalidContent := []byte("bbaattss")
mockValidator := validator.NewMockValidator(validHash, validContent)
mockValidatingStorer := mock.NewValidatingStorer(mockValidator)
client, cleanup := newTestServer(t, testServerOptions{
Storer: mockValidatingStorer,
})
var (
resource = func(addr swarm.Address) string { return "/bzz-chunk/" + addr.String() }
validHash = swarm.MustParseHexAddress("aabbcc")
invalidHash = swarm.MustParseHexAddress("bbccdd")
validContent = []byte("bbaatt")
invalidContent = []byte("bbaattss")
mockValidator = validator.NewMockValidator(validHash, validContent)
mockValidatingStorer = mock.NewValidatingStorer(mockValidator)
client, cleanup = newTestServer(t, testServerOptions{
Storer: mockValidatingStorer,
})
)
defer cleanup()
t.Run("invalid hash", func(t *testing.T) {
......
......@@ -4,4 +4,7 @@
package api
type PingpongResponse = pingpongResponse
type (
PingpongResponse = pingpongResponse
BzzPostResponse = bzzPostResponse
)
......@@ -32,6 +32,14 @@ func (s *server) setupRouting() {
"POST": http.HandlerFunc(s.pingpongHandler),
})
router.Handle("/bzz/", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.bzzUploadHandler),
})
router.Handle("/bzz/{address}", jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.bzzGetHandler),
})
router.Handle("/bzz-chunk/{addr}", jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.chunkGetHandler),
"POST": http.HandlerFunc(s.chunkUploadHandler),
......
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