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:
type: boolean
required: false
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:
content:
application/x-tar:
......
......@@ -25,6 +25,7 @@ const (
SwarmPinHeader = "Swarm-Pin"
SwarmTagUidHeader = "Swarm-Tag-Uid"
SwarmEncryptHeader = "Swarm-Encrypt"
SwarmIndextHeader = "Swarm-Index"
)
var (
......
......@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"github.com/gorilla/mux"
......@@ -30,6 +31,7 @@ func (s *server) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) {
nameOrHex := mux.Vars(r)["address"]
path := mux.Vars(r)["path"]
path = strings.TrimRight(path, "/")
address, err := s.resolveNameOrAddress(nameOrHex)
if err != nil {
......
......@@ -15,6 +15,7 @@ import (
"mime"
"net/http"
"path/filepath"
"strings"
"github.com/ethersphere/bee/pkg/collection/entry"
"github.com/ethersphere/bee/pkg/file/pipeline"
......@@ -54,7 +55,7 @@ func (s *server) dirUploadHandler(w http.ResponseWriter, r *http.Request) {
// Add the tag to the context
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 {
logger.Debugf("dir upload: store dir err: %v", err)
logger.Errorf("dir upload: store dir")
......@@ -94,7 +95,7 @@ func validateRequest(r *http.Request) error {
// 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
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)
dirManifest, err := manifest.NewDefaultManifest(encrypt, s)
......@@ -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)
}
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++
}
......
......@@ -9,7 +9,6 @@ import (
"bytes"
"context"
"encoding/json"
statestore "github.com/ethersphere/bee/pkg/statestore/mock"
"io/ioutil"
"net/http"
"path"
......@@ -23,6 +22,7 @@ import (
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/logging"
"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/swarm"
"github.com/ethersphere/bee/pkg/tags"
......@@ -85,8 +85,10 @@ func TestDirs(t *testing.T) {
// valid tars
for _, tc := range []struct {
name string
files []f // files in dir for test case
name string
wantIndexFilename string
indexFilenameOption jsonhttptest.Option
files []f // files in dir for test case
}{
{
name: "non-nested files without extension",
......@@ -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) {
// tar all the test case files
......@@ -150,12 +198,17 @@ func TestDirs(t *testing.T) {
var respBytes []byte
// verify directory tar upload response
jsonhttptest.Request(t, client, http.MethodPost, dirUploadResource, http.StatusOK,
options := []jsonhttptest.Option{
jsonhttptest.WithRequestBody(tarReader),
jsonhttptest.WithRequestHeader("Content-Type", api.ContentTypeTar),
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)
......@@ -198,9 +251,8 @@ func TestDirs(t *testing.T) {
t.Fatal(err)
}
// check if each file can be located and read
for _, file := range tc.files {
filePath := path.Join(file.dir, file.name)
validateFile := func(t *testing.T, file f, filePath string) {
t.Helper()
entry, err := verifyManifest.Lookup(filePath)
if err != nil {
......@@ -210,14 +262,24 @@ func TestDirs(t *testing.T) {
fileReference := entry.Reference()
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.WithExpectedResponse(file.data),
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() {
),
})
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{
"GET": web.ChainHandlers(
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