Przeglądaj źródła

swarm/api/http: redirect root manifest requests to include trailing slash (#14806)

Lewis Marshall 8 lat temu
rodzic
commit
f4841ff43d
2 zmienionych plików z 68 dodań i 0 usunięć
  1. 6 0
      swarm/api/http/server.go
  2. 62 0
      swarm/api/http/server_test.go

+ 6 - 0
swarm/api/http/server.go

@@ -522,6 +522,12 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
 // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
 // with the content of the file at <path> from the given <manifest>
 func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
+	// ensure the root path has a trailing slash so that relative URLs work
+	if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
+		http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
+		return
+	}
+
 	key, err := s.api.Resolve(r.uri)
 	if err != nil {
 		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))

+ 62 - 0
swarm/api/http/server_test.go

@@ -18,12 +18,16 @@ package http_test
 
 import (
 	"bytes"
+	"errors"
+	"fmt"
 	"io/ioutil"
 	"net/http"
 	"sync"
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/swarm/api"
+	swarm "github.com/ethereum/go-ethereum/swarm/api/client"
 	"github.com/ethereum/go-ethereum/swarm/storage"
 	"github.com/ethereum/go-ethereum/swarm/testutil"
 )
@@ -128,3 +132,61 @@ func TestBzzrGetPath(t *testing.T) {
 	}
 
 }
+
+// TestBzzRootRedirect tests that getting the root path of a manifest without
+// a trailing slash gets redirected to include the trailing slash so that
+// relative URLs work as expected.
+func TestBzzRootRedirect(t *testing.T) {
+	srv := testutil.NewTestSwarmServer(t)
+	defer srv.Close()
+
+	// create a manifest with some data at the root path
+	client := swarm.NewClient(srv.URL)
+	data := []byte("data")
+	file := &swarm.File{
+		ReadCloser: ioutil.NopCloser(bytes.NewReader(data)),
+		ManifestEntry: api.ManifestEntry{
+			Path:        "",
+			ContentType: "text/plain",
+			Size:        int64(len(data)),
+		},
+	}
+	hash, err := client.Upload(file, "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// define a CheckRedirect hook which ensures there is only a single
+	// redirect to the correct URL
+	redirected := false
+	httpClient := http.Client{
+		CheckRedirect: func(req *http.Request, via []*http.Request) error {
+			if redirected {
+				return errors.New("too many redirects")
+			}
+			redirected = true
+			expectedPath := "/bzz:/" + hash + "/"
+			if req.URL.Path != expectedPath {
+				return fmt.Errorf("expected redirect to %q, got %q", expectedPath, req.URL.Path)
+			}
+			return nil
+		},
+	}
+
+	// perform the GET request and assert the response
+	res, err := httpClient.Get(srv.URL + "/bzz:/" + hash)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if !redirected {
+		t.Fatal("expected GET /bzz:/<hash> to redirect to /bzz:/<hash>/ but it didn't")
+	}
+	gotData, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(gotData, data) {
+		t.Fatalf("expected response to equal %q, got %q", data, gotData)
+	}
+}