Commit 349d0796 authored by Janoš Guljaš's avatar Janoš Guljaš Committed by GitHub

implement swarm-index dirs api option as default path (#690)

parent d032e978
...@@ -292,6 +292,13 @@ paths: ...@@ -292,6 +292,13 @@ paths:
type: boolean type: boolean
required: false required: false
description: Represents the encrypting state of the collection description: Represents the encrypting state of the collection
- in: header
name: swarm-index
schema:
type: string
example: index.html
required: false
description: Default file to be referenced on path, if exists under that path
requestBody: requestBody:
content: content:
application/x-tar: application/x-tar:
......
...@@ -25,6 +25,7 @@ const ( ...@@ -25,6 +25,7 @@ const (
SwarmPinHeader = "Swarm-Pin" SwarmPinHeader = "Swarm-Pin"
SwarmTagUidHeader = "Swarm-Tag-Uid" SwarmTagUidHeader = "Swarm-Tag-Uid"
SwarmEncryptHeader = "Swarm-Encrypt" SwarmEncryptHeader = "Swarm-Encrypt"
SwarmIndextHeader = "Swarm-Index"
) )
var ( var (
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/gorilla/mux" "github.com/gorilla/mux"
...@@ -30,6 +31,7 @@ func (s *server) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) { ...@@ -30,6 +31,7 @@ func (s *server) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) {
nameOrHex := mux.Vars(r)["address"] nameOrHex := mux.Vars(r)["address"]
path := mux.Vars(r)["path"] path := mux.Vars(r)["path"]
path = strings.TrimRight(path, "/")
address, err := s.resolveNameOrAddress(nameOrHex) address, err := s.resolveNameOrAddress(nameOrHex)
if err != nil { if err != nil {
......
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"mime" "mime"
"net/http" "net/http"
"path/filepath" "path/filepath"
"strings"
"github.com/ethersphere/bee/pkg/collection/entry" "github.com/ethersphere/bee/pkg/collection/entry"
"github.com/ethersphere/bee/pkg/file/pipeline" "github.com/ethersphere/bee/pkg/file/pipeline"
...@@ -54,7 +55,7 @@ func (s *server) dirUploadHandler(w http.ResponseWriter, r *http.Request) { ...@@ -54,7 +55,7 @@ func (s *server) dirUploadHandler(w http.ResponseWriter, r *http.Request) {
// Add the tag to the context // Add the tag to the context
ctx := sctx.SetTag(r.Context(), tag) ctx := sctx.SetTag(r.Context(), tag)
reference, err := storeDir(ctx, r.Body, s.Storer, requestModePut(r), s.Logger, requestEncrypt(r)) reference, err := storeDir(ctx, r.Body, s.Storer, requestModePut(r), s.Logger, requestEncrypt(r), r.Header.Get(SwarmIndextHeader))
if err != nil { if err != nil {
logger.Debugf("dir upload: store dir err: %v", err) logger.Debugf("dir upload: store dir err: %v", err)
logger.Errorf("dir upload: store dir") logger.Errorf("dir upload: store dir")
...@@ -94,7 +95,7 @@ func validateRequest(r *http.Request) error { ...@@ -94,7 +95,7 @@ func validateRequest(r *http.Request) error {
// storeDir stores all files recursively contained in the directory given as a tar // storeDir stores all files recursively contained in the directory given as a tar
// it returns the hash for the uploaded manifest corresponding to the uploaded dir // it returns the hash for the uploaded manifest corresponding to the uploaded dir
func storeDir(ctx context.Context, reader io.ReadCloser, s storage.Storer, mode storage.ModePut, log logging.Logger, encrypt bool) (swarm.Address, error) { func storeDir(ctx context.Context, reader io.ReadCloser, s storage.Storer, mode storage.ModePut, log logging.Logger, encrypt bool, indexFilename string) (swarm.Address, error) {
logger := tracing.NewLoggerWithTraceID(ctx, log) logger := tracing.NewLoggerWithTraceID(ctx, log)
dirManifest, err := manifest.NewDefaultManifest(encrypt, s) dirManifest, err := manifest.NewDefaultManifest(encrypt, s)
...@@ -147,6 +148,18 @@ func storeDir(ctx context.Context, reader io.ReadCloser, s storage.Storer, mode ...@@ -147,6 +148,18 @@ func storeDir(ctx context.Context, reader io.ReadCloser, s storage.Storer, mode
return swarm.ZeroAddress, fmt.Errorf("add to manifest: %w", err) return swarm.ZeroAddress, fmt.Errorf("add to manifest: %w", err)
} }
if indexFilename != "" && filePath == indexFilename || strings.HasSuffix(filePath, "/"+indexFilename) {
filePath := strings.TrimSuffix(filePath, indexFilename)
filePath = strings.TrimRight(filePath, "/")
// add file entry to dir manifest
err = dirManifest.Add(filePath, manifest.NewEntry(fileReference))
if err != nil {
return swarm.ZeroAddress, fmt.Errorf("add to manifest: %w", err)
}
}
filesAdded++ filesAdded++
} }
......
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"path" "path"
...@@ -23,6 +22,7 @@ import ( ...@@ -23,6 +22,7 @@ import (
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/logging" "github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/manifest" "github.com/ethersphere/bee/pkg/manifest"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/storage/mock" "github.com/ethersphere/bee/pkg/storage/mock"
"github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/swarm"
"github.com/ethersphere/bee/pkg/tags" "github.com/ethersphere/bee/pkg/tags"
...@@ -85,8 +85,10 @@ func TestDirs(t *testing.T) { ...@@ -85,8 +85,10 @@ func TestDirs(t *testing.T) {
// valid tars // valid tars
for _, tc := range []struct { for _, tc := range []struct {
name string name string
files []f // files in dir for test case wantIndexFilename string
indexFilenameOption jsonhttptest.Option
files []f // files in dir for test case
}{ }{
{ {
name: "non-nested files without extension", name: "non-nested files without extension",
...@@ -143,6 +145,52 @@ func TestDirs(t *testing.T) { ...@@ -143,6 +145,52 @@ func TestDirs(t *testing.T) {
}, },
}, },
}, },
{
name: "no index filename",
files: []f{
{
data: []byte("<h1>Swarm"),
name: "index.html",
dir: "",
reference: swarm.MustParseHexAddress("bcb1bfe15c36f1a529a241f4d0c593e5648aa6d40859790894c6facb41a6ef28"),
header: http.Header{
"Content-Type": {"text/html; charset=utf-8"},
},
},
},
},
{
name: "explicit index filename",
wantIndexFilename: "index.html",
indexFilenameOption: jsonhttptest.WithRequestHeader(api.SwarmIndextHeader, "index.html"),
files: []f{
{
data: []byte("<h1>Swarm"),
name: "index.html",
dir: "",
reference: swarm.MustParseHexAddress("bcb1bfe15c36f1a529a241f4d0c593e5648aa6d40859790894c6facb41a6ef28"),
header: http.Header{
"Content-Type": {"text/html; charset=utf-8"},
},
},
},
},
{
name: "nested index filename",
wantIndexFilename: "index.html",
indexFilenameOption: jsonhttptest.WithRequestHeader(api.SwarmIndextHeader, "index.html"),
files: []f{
{
data: []byte("<h1>Swarm"),
name: "index.html",
dir: "dir",
reference: swarm.MustParseHexAddress("bcb1bfe15c36f1a529a241f4d0c593e5648aa6d40859790894c6facb41a6ef28"),
header: http.Header{
"Content-Type": {"text/html; charset=utf-8"},
},
},
},
},
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
// tar all the test case files // tar all the test case files
...@@ -150,12 +198,17 @@ func TestDirs(t *testing.T) { ...@@ -150,12 +198,17 @@ func TestDirs(t *testing.T) {
var respBytes []byte var respBytes []byte
// verify directory tar upload response options := []jsonhttptest.Option{
jsonhttptest.Request(t, client, http.MethodPost, dirUploadResource, http.StatusOK,
jsonhttptest.WithRequestBody(tarReader), jsonhttptest.WithRequestBody(tarReader),
jsonhttptest.WithRequestHeader("Content-Type", api.ContentTypeTar), jsonhttptest.WithRequestHeader("Content-Type", api.ContentTypeTar),
jsonhttptest.WithPutResponseBody(&respBytes), jsonhttptest.WithPutResponseBody(&respBytes),
) }
if tc.indexFilenameOption != nil {
options = append(options, tc.indexFilenameOption)
}
// verify directory tar upload response
jsonhttptest.Request(t, client, http.MethodPost, dirUploadResource, http.StatusOK, options...)
read := bytes.NewReader(respBytes) read := bytes.NewReader(respBytes)
...@@ -198,9 +251,8 @@ func TestDirs(t *testing.T) { ...@@ -198,9 +251,8 @@ func TestDirs(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// check if each file can be located and read validateFile := func(t *testing.T, file f, filePath string) {
for _, file := range tc.files { t.Helper()
filePath := path.Join(file.dir, file.name)
entry, err := verifyManifest.Lookup(filePath) entry, err := verifyManifest.Lookup(filePath)
if err != nil { if err != nil {
...@@ -210,14 +262,24 @@ func TestDirs(t *testing.T) { ...@@ -210,14 +262,24 @@ func TestDirs(t *testing.T) {
fileReference := entry.Reference() fileReference := entry.Reference()
if !bytes.Equal(file.reference.Bytes(), fileReference.Bytes()) { if !bytes.Equal(file.reference.Bytes(), fileReference.Bytes()) {
t.Fatalf("expected file reference to match %x, got %x", file.reference, fileReference) t.Fatalf("expected file reference to match %s, got %s", file.reference, fileReference)
} }
jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(fileReference.String()), http.StatusOK, jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(fileReference.String()), http.StatusOK,
jsonhttptest.WithExpectedResponse(file.data), jsonhttptest.WithExpectedResponse(file.data),
jsonhttptest.WithRequestHeader("Content-Type", file.header.Get("Content-Type")), jsonhttptest.WithRequestHeader("Content-Type", file.header.Get("Content-Type")),
) )
}
// check if each file can be located and read
for _, file := range tc.files {
validateFile(t, file, path.Join(file.dir, file.name))
// if there is an index filename to be tested
// try to download it using only the directory as the path
if file.name == tc.wantIndexFilename {
validateFile(t, file, file.dir)
}
} }
}) })
......
...@@ -78,6 +78,11 @@ func (s *server) setupRouting() { ...@@ -78,6 +78,11 @@ func (s *server) setupRouting() {
), ),
}) })
handle(router, "/bzz/{address}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
u := r.URL
u.Path += "/"
http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
}))
handle(router, "/bzz/{address}/{path:.*}", jsonhttp.MethodHandler{ handle(router, "/bzz/{address}/{path:.*}", jsonhttp.MethodHandler{
"GET": web.ChainHandlers( "GET": web.ChainHandlers(
s.newTracingHandler("bzz-download"), s.newTracingHandler("bzz-download"),
......
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