Skip to content

Commit 8254822

Browse files
authored
Merge pull request #13 from SiaFoundation/single-slab
Single-slab endpoints
2 parents 4a56afe + b663748 commit 8254822

File tree

14 files changed

+182
-218
lines changed

14 files changed

+182
-218
lines changed

api/api.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,8 @@ type SlabsUploadRequest struct {
198198

199199
// SlabsDownloadRequest is the request type for the /slabs/download endpoint.
200200
type SlabsDownloadRequest struct {
201-
Slabs []slab.Slice `json:"slabs"`
202-
Offset int64 `json:"offset"`
203-
Length int64 `json:"length"`
204-
Contracts []Contract `json:"contracts"`
201+
Slab slab.Slice `json:"slab"`
202+
Contracts []Contract `json:"contracts"`
205203
}
206204

207205
// SlabsDeleteRequest is the request type for the /slabs/delete endpoint.
@@ -212,10 +210,10 @@ type SlabsDeleteRequest struct {
212210

213211
// SlabsMigrateRequest is the request type for the /slabs/migrate endpoint.
214212
type SlabsMigrateRequest struct {
215-
Slabs []slab.Slab `json:"slabs"`
216-
From []Contract `json:"from"`
217-
To []Contract `json:"to"`
218-
CurrentHeight uint64 `json:"currentHeight"`
213+
Slab slab.Slab `json:"slab"`
214+
From []Contract `json:"from"`
215+
To []Contract `json:"to"`
216+
CurrentHeight uint64 `json:"currentHeight"`
219217
}
220218

221219
// ObjectsResponse is the response type for the /objects endpoint.

api/api_test.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,20 +95,20 @@ type mockSlabMover struct {
9595
hosts []slab.Host
9696
}
9797

98-
func (sm *mockSlabMover) UploadSlabs(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []api.Contract) ([]slab.Slab, error) {
99-
return slab.UploadSlabs(r, m, n, sm.hosts)
98+
func (sm *mockSlabMover) UploadSlab(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []api.Contract) (slab.Slab, error) {
99+
return slab.UploadSlab(r, m, n, sm.hosts)
100100
}
101101

102-
func (sm *mockSlabMover) DownloadSlabs(ctx context.Context, w io.Writer, slabs []slab.Slice, offset, length int64, contracts []api.Contract) error {
103-
return slab.DownloadSlabs(w, slabs, offset, length, sm.hosts)
102+
func (sm *mockSlabMover) DownloadSlab(ctx context.Context, w io.Writer, s slab.Slice, contracts []api.Contract) error {
103+
return slab.DownloadSlab(w, s, sm.hosts)
104104
}
105105

106106
func (sm *mockSlabMover) DeleteSlabs(ctx context.Context, slabs []slab.Slab, contracts []api.Contract) error {
107107
return slab.DeleteSlabs(slabs, sm.hosts)
108108
}
109109

110-
func (sm *mockSlabMover) MigrateSlabs(ctx context.Context, slabs []slab.Slab, currentHeight uint64, from, to []api.Contract) error {
111-
return slab.MigrateSlabs(slabs, sm.hosts, sm.hosts)
110+
func (sm *mockSlabMover) MigrateSlab(ctx context.Context, s *slab.Slab, currentHeight uint64, from, to []api.Contract) error {
111+
return slab.MigrateSlab(s, sm.hosts, sm.hosts)
112112
}
113113

114114
type node struct {
@@ -144,7 +144,7 @@ func runServer(n *node) (*api.Client, func()) {
144144
}
145145
go func() {
146146
srv := api.NewServer(mockSyncer{}, mockChainManager{}, mockTxPool{}, n.w, n.hdb, mockRHP{}, n.cs, n.sm, n.os)
147-
http.Serve(l, jape.AuthMiddleware(srv, "password"))
147+
http.Serve(l, jape.BasicAuth("password")(srv))
148148
}()
149149
c := api.NewClient("http://"+l.Addr().String(), "password")
150150
return c, func() { l.Close() }
@@ -199,20 +199,17 @@ func TestObject(t *testing.T) {
199199
// upload
200200
data := frand.Bytes(12345)
201201
key := object.GenerateEncryptionKey()
202-
slabs, err := c.UploadSlabs(key.Encrypt(bytes.NewReader(data)), 2, 3, 0, contracts)
202+
s, err := c.UploadSlab(key.Encrypt(bytes.NewReader(data)), 2, 3, 0, contracts)
203203
if err != nil {
204204
t.Fatal(err)
205205
}
206206
o := object.Object{
207-
Key: key,
208-
Slabs: make([]slab.Slice, len(slabs)),
209-
}
210-
for i := range slabs {
211-
o.Slabs[i] = slab.Slice{
212-
Slab: slabs[i],
207+
Key: key,
208+
Slabs: []slab.Slice{{
209+
Slab: s,
213210
Offset: 0,
214211
Length: uint32(len(data)),
215-
}
212+
}},
216213
}
217214

218215
// store object
@@ -228,17 +225,17 @@ func TestObject(t *testing.T) {
228225

229226
// download
230227
var buf bytes.Buffer
231-
if err := c.DownloadSlabs(key.Decrypt(&buf, 0), o.Slabs, 0, o.Size(), contracts); err != nil {
228+
if err := c.DownloadSlab(key.Decrypt(&buf, 0), o.Slabs[0], contracts); err != nil {
232229
t.Fatal(err)
233230
} else if !bytes.Equal(buf.Bytes(), data) {
234231
t.Fatalf("data mismatch:\n%v (%v)\n%v (%v)", buf.Bytes(), len(buf.Bytes()), data, len(data))
235232
}
236233

237234
// delete slabs
238-
if err := c.DeleteSlabs(slabs, contracts); err != nil {
235+
if err := c.DeleteSlabs([]slab.Slab{s}, contracts); err != nil {
239236
t.Fatal(err)
240237
}
241-
if err := c.DownloadSlabs(ioutil.Discard, o.Slabs, 0, o.Size(), contracts); err == nil {
238+
if err := c.DownloadSlab(ioutil.Discard, o.Slabs[0], contracts); err == nil {
242239
t.Error("slabs should no longer be retrievable")
243240
}
244241

api/client.go

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"io"
99
"io/ioutil"
10-
"mime/multipart"
1110
"net/http"
1211
"time"
1312

@@ -348,58 +347,43 @@ func (c *Client) HostSetResolves(name string) (ips []string, err error) {
348347
return
349348
}
350349

351-
// UploadSlabs uploads data to a set of hosts.
352-
func (c *Client) UploadSlabs(src io.Reader, m, n uint8, height uint64, contracts []Contract) (slabs []slab.Slab, err error) {
353-
c.c.Custom("POST", "/slabs/upload", []byte{}, &slabs)
354-
355-
// apparently, the only way to stream a multipart upload is via io.Pipe :/
356-
r, w := io.Pipe()
357-
mw := multipart.NewWriter(w)
358-
errChan := make(chan error, 1)
359-
go func() {
360-
defer w.Close()
361-
defer mw.Close()
362-
js, _ := json.Marshal(SlabsUploadRequest{
363-
MinShards: m,
364-
TotalShards: n,
365-
Contracts: contracts,
366-
CurrentHeight: height,
367-
})
368-
mw.WriteField("meta", string(js))
369-
part, _ := mw.CreateFormFile("data", "data")
370-
_, err := io.Copy(part, src)
371-
errChan <- err
372-
}()
373-
req, err := http.NewRequest("POST", fmt.Sprintf("%v%v", c.c.BaseURL, "/slabs/upload"), r)
350+
// UploadSlab uploads data to a set of hosts. At most m*SectorSize bytes will be
351+
// read from src.
352+
func (c *Client) UploadSlab(src io.Reader, m, n uint8, height uint64, contracts []Contract) (s slab.Slab, err error) {
353+
c.c.Custom("POST", "/slabs/upload", []byte{}, &s)
354+
355+
js, _ := json.Marshal(SlabsUploadRequest{
356+
MinShards: m,
357+
TotalShards: n,
358+
Contracts: contracts,
359+
CurrentHeight: height,
360+
})
361+
body := io.MultiReader(bytes.NewReader(js), io.LimitReader(src, int64(m)*rhpv2.SectorSize))
362+
req, err := http.NewRequest("POST", fmt.Sprintf("%v%v", c.c.BaseURL, "/slabs/upload"), body)
374363
if err != nil {
375364
panic(err)
376365
}
377-
req.Header.Set("Content-Type", mw.FormDataContentType())
378366
req.SetBasicAuth("", c.c.Password)
379367
resp, err := http.DefaultClient.Do(req)
380368
if err != nil {
381-
return nil, err
382-
} else if err := <-errChan; err != nil {
383-
return nil, err
369+
return slab.Slab{}, err
384370
}
385371
defer io.Copy(ioutil.Discard, resp.Body)
386372
defer resp.Body.Close()
387373
if resp.StatusCode != 200 {
388374
err, _ := ioutil.ReadAll(resp.Body)
389-
return nil, errors.New(string(err))
375+
return slab.Slab{}, errors.New(string(err))
390376
}
391-
err = json.NewDecoder(resp.Body).Decode(&slabs)
377+
err = json.NewDecoder(resp.Body).Decode(&s)
392378
return
393379
}
394380

395-
// DownloadSlabs downloads data from a set of hosts.
396-
func (c *Client) DownloadSlabs(dst io.Writer, slabs []slab.Slice, offset, length int64, contracts []Contract) (err error) {
381+
// DownloadSlab downloads data from a set of hosts.
382+
func (c *Client) DownloadSlab(dst io.Writer, s slab.Slice, contracts []Contract) (err error) {
397383
c.c.Custom("POST", "/slabs/download", SlabsDownloadRequest{}, (*[]byte)(nil))
398384

399385
js, _ := json.Marshal(SlabsDownloadRequest{
400-
Slabs: slabs,
401-
Offset: offset,
402-
Length: length,
386+
Slab: s,
403387
Contracts: contracts,
404388
})
405389
req, err := http.NewRequest("POST", fmt.Sprintf("%v%v", c.c.BaseURL, "/slabs/download"), bytes.NewReader(js))
@@ -422,15 +406,15 @@ func (c *Client) DownloadSlabs(dst io.Writer, slabs []slab.Slice, offset, length
422406
return
423407
}
424408

425-
// MigrateSlabs migrates the specified slabs.
426-
func (c *Client) MigrateSlabs(slabs []slab.Slab, from, to []Contract, currentHeight uint64) (err error) {
409+
// MigrateSlab migrates the specified slab.
410+
func (c *Client) MigrateSlab(s *slab.Slab, from, to []Contract, currentHeight uint64) (err error) {
427411
req := SlabsMigrateRequest{
428-
Slabs: slabs,
412+
Slab: *s,
429413
From: from,
430414
To: to,
431415
CurrentHeight: currentHeight,
432416
}
433-
err = c.c.POST("/slabs/migrate", req, nil)
417+
err = c.c.POST("/slabs/migrate", req, s)
434418
return
435419
}
436420

api/server.go

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ type (
8888

8989
// A SlabMover uploads, downloads, and migrates slabs.
9090
SlabMover interface {
91-
UploadSlabs(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []Contract) ([]slab.Slab, error)
92-
DownloadSlabs(ctx context.Context, w io.Writer, slabs []slab.Slice, offset, length int64, contracts []Contract) error
91+
UploadSlab(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []Contract) (slab.Slab, error)
92+
DownloadSlab(ctx context.Context, w io.Writer, slab slab.Slice, contracts []Contract) error
9393
DeleteSlabs(ctx context.Context, slabs []slab.Slab, contracts []Contract) error
94-
MigrateSlabs(ctx context.Context, slabs []slab.Slab, currentHeight uint64, from, to []Contract) error
94+
MigrateSlab(ctx context.Context, s *slab.Slab, currentHeight uint64, from, to []Contract) error
9595
}
9696

9797
// An ObjectStore stores objects.
@@ -538,21 +538,18 @@ func (s *server) hostsetsResolveHandler(jc jape.Context) {
538538
}
539539

540540
func (s *server) slabsUploadHandler(jc jape.Context) {
541-
jc.Custom((*[]byte)(nil), []slab.Slab{})
541+
jc.Custom((*[]byte)(nil), slab.Slab{})
542542

543543
var sur SlabsUploadRequest
544-
if err := json.NewDecoder(strings.NewReader(jc.Request.PostFormValue("meta"))).Decode(&sur); err != nil {
544+
dec := json.NewDecoder(jc.Request.Body)
545+
if err := dec.Decode(&sur); err != nil {
545546
http.Error(jc.ResponseWriter, err.Error(), http.StatusBadRequest)
546547
return
547548
}
548-
f, _, err := jc.Request.FormFile("data")
549-
if err != nil {
550-
http.Error(jc.ResponseWriter, err.Error(), http.StatusBadRequest)
551-
return
552-
}
553-
slabs, err := s.sm.UploadSlabs(jc.Request.Context(), f, sur.MinShards, sur.TotalShards, sur.CurrentHeight, sur.Contracts)
554-
if jc.Check("couldn't upload slabs", err) == nil {
555-
jc.Encode(slabs)
549+
data := io.LimitReader(io.MultiReader(dec.Buffered(), jc.Request.Body), int64(sur.MinShards)*rhpv2.SectorSize)
550+
slab, err := s.sm.UploadSlab(jc.Request.Context(), data, sur.MinShards, sur.TotalShards, sur.CurrentHeight, sur.Contracts)
551+
if jc.Check("couldn't upload slab", err) == nil {
552+
jc.Encode(slab)
556553
}
557554
}
558555

@@ -563,24 +560,20 @@ func (s *server) slabsDownloadHandler(jc jape.Context) {
563560
if jc.Decode(&sdr) != nil {
564561
return
565562
}
566-
if sdr.Length == 0 {
567-
for _, ss := range sdr.Slabs {
568-
sdr.Length += int64(ss.Length)
569-
}
570-
}
571-
// TODO: if we encounter an error halfway through the download, it's too
572-
// late to change the response code and send an error message. Not sure how
573-
// best to handle this.
574-
err := s.sm.DownloadSlabs(jc.Request.Context(), jc.ResponseWriter, sdr.Slabs, sdr.Offset, sdr.Length, sdr.Contracts)
563+
err := s.sm.DownloadSlab(jc.Request.Context(), jc.ResponseWriter, sdr.Slab, sdr.Contracts)
575564
jc.Check("couldn't download slabs", err)
576565
}
577566

578567
func (s *server) slabsMigrateHandler(jc jape.Context) {
579568
var smr SlabsMigrateRequest
580-
if jc.Decode(&smr) == nil {
581-
err := s.sm.MigrateSlabs(jc.Request.Context(), smr.Slabs, smr.CurrentHeight, smr.From, smr.To)
582-
jc.Check("couldn't migrate slabs", err)
569+
if jc.Decode(&smr) != nil {
570+
return
571+
}
572+
err := s.sm.MigrateSlab(jc.Request.Context(), &smr.Slab, smr.CurrentHeight, smr.From, smr.To)
573+
if jc.Check("couldn't migrate slabs", err) != nil {
574+
return
583575
}
576+
jc.Encode(smr.Slab)
584577
}
585578

586579
func (s *server) slabsDeleteHandler(jc jape.Context) {

cmd/renterd/slab.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,18 @@ func (sm slabMover) withHosts(ctx context.Context, contracts []api.Contract, fn
3939
return fn(hosts)
4040
}
4141

42-
func (sm slabMover) UploadSlabs(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []api.Contract) (slabs []slab.Slab, err error) {
42+
func (sm slabMover) UploadSlab(ctx context.Context, r io.Reader, m, n uint8, currentHeight uint64, contracts []api.Contract) (s slab.Slab, err error) {
4343
sm.pool.SetCurrentHeight(currentHeight)
4444
err = sm.withHosts(ctx, contracts, func(hosts []slab.Host) error {
45-
slabs, err = slab.UploadSlabs(r, m, n, hosts)
45+
s, err = slab.UploadSlab(r, m, n, hosts)
4646
return err
4747
})
4848
return
4949
}
5050

51-
func (sm slabMover) DownloadSlabs(ctx context.Context, w io.Writer, slabs []slab.Slice, offset, length int64, contracts []api.Contract) error {
51+
func (sm slabMover) DownloadSlab(ctx context.Context, w io.Writer, s slab.Slice, contracts []api.Contract) error {
5252
return sm.withHosts(ctx, contracts, func(hosts []slab.Host) error {
53-
return slab.DownloadSlabs(w, slabs, offset, length, hosts)
53+
return slab.DownloadSlab(w, s, hosts)
5454
})
5555
}
5656

@@ -60,7 +60,7 @@ func (sm slabMover) DeleteSlabs(ctx context.Context, slabs []slab.Slab, contract
6060
})
6161
}
6262

63-
func (sm slabMover) MigrateSlabs(ctx context.Context, slabs []slab.Slab, currentHeight uint64, from, to []api.Contract) (err error) {
63+
func (sm slabMover) MigrateSlab(ctx context.Context, s *slab.Slab, currentHeight uint64, from, to []api.Contract) (err error) {
6464
sm.pool.SetCurrentHeight(currentHeight)
6565
var fromHosts []slab.Host
6666
for _, c := range from {
@@ -93,7 +93,7 @@ func (sm slabMover) MigrateSlabs(ctx context.Context, slabs []slab.Slab, current
9393
err = ctx.Err()
9494
}
9595
}()
96-
return slab.MigrateSlabs(slabs, fromHosts, toHosts)
96+
return slab.MigrateSlab(s, fromHosts, toHosts)
9797
}
9898

9999
func newSlabMover() slabMover {

cmd/renterd/web.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,20 +133,22 @@ func (t treeMux) ServeHTTP(w http.ResponseWriter, req *http.Request) {
133133

134134
func startWeb(l net.Listener, node *node, password string) error {
135135
renter := api.NewServer(&syncer{node.g, node.tp}, &chainManager{node.cm}, txpool{node.tp}, node.w, node.hdb, rhpImpl{}, node.cs, newSlabMover(), node.os)
136+
auth := jape.BasicAuth(password)
136137
return http.Serve(l, treeMux{
137138
h: createUIHandler(),
138139
sub: map[string]treeMux{
139-
"/api": {h: jape.AuthMiddleware(renter, password)},
140+
"/api": {h: auth(renter)},
140141
},
141142
})
142143
}
143144

144145
func startStatelessWeb(l net.Listener, password string) error {
145146
renter := api.NewStatelessServer(rhpImpl{}, newSlabMover())
147+
auth := jape.BasicAuth(password)
146148
return http.Serve(l, treeMux{
147149
h: createUIHandler(),
148150
sub: map[string]treeMux{
149-
"/api": {h: jape.AuthMiddleware(renter, password)},
151+
"/api": {h: auth(renter)},
150152
},
151153
})
152154
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3
77
github.com/klauspost/reedsolomon v1.9.16
88
gitlab.com/NebulousLabs/encoding v0.0.0-20200604091946-456c3dc907fe
9-
go.sia.tech/jape v0.4.0
9+
go.sia.tech/jape v0.5.0
1010
go.sia.tech/siad v1.5.7
1111
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
1212
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ gitlab.com/NebulousLabs/threadgroup v0.0.0-20200608151952-38921fbef213 h1:owERlK
147147
gitlab.com/NebulousLabs/threadgroup v0.0.0-20200608151952-38921fbef213/go.mod h1:vIutAvl7lmJqLVYTCBY5WDdJomP+V74At8LCeEYoH8w=
148148
gitlab.com/NebulousLabs/writeaheadlog v0.0.0-20200618142844-c59a90f49130/go.mod h1:SxigdS5Q1ui+OMgGAXt1E/Fg3RB6PvKXMov2O3gvIzs=
149149
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
150-
go.sia.tech/jape v0.4.0 h1:jB3aYycHSotYYDJs4kmR9GUfHN5NPlaTF1v+ELJnT0w=
151-
go.sia.tech/jape v0.4.0/go.mod h1:bu+ka8FgKq7MNH2JRTTOjfvVNku97V4ffyKr0dKoU90=
150+
go.sia.tech/jape v0.5.0 h1:6BLMZEePInWwQcJO1mcmrPBpF0QuvkrXmHSWGKeR0tY=
151+
go.sia.tech/jape v0.5.0/go.mod h1:bu+ka8FgKq7MNH2JRTTOjfvVNku97V4ffyKr0dKoU90=
152152
go.sia.tech/siad v1.5.7 h1:yFVNyMrCSl6vE0XjlhitFYXxgpVf15ILuCSbp7ZfExM=
153153
go.sia.tech/siad v1.5.7/go.mod h1:/xtHgMhNKI+cpwm5kjl9u7EG4kqMiYpmucDG710GaRY=
154154
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=

internal/slabutil/mock.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func (h *MockHost) DownloadSector(w io.Writer, root consensus.Hash256, offset, l
3131
sector, ok := h.sectors[root]
3232
if !ok {
3333
return errors.New("unknown root")
34+
} else if uint64(offset)+uint64(length) > rhpv2.SectorSize {
35+
return errors.New("offset+length out of bounds")
3436
}
3537
_, err := w.Write(sector[offset:][:length])
3638
return err

0 commit comments

Comments
 (0)