Browse Source

swarm/api: url scheme bzz-hash to get hashes of swarm content (#15238) (#15715)

* swarm/api: url scheme bzz-hash to get hashes of swarm content (#15238)

Update URI to support bzz-hash scheme and handle such HTTP requests by
responding with hash of the content as a text/plain response.

* swarm/api: return hash of the content for bzz-hash:// requests

* swarm/api: revert "return hash of the content for bzz-hash:// requests"

Return hashes of the content that would be returned by bzz-raw
request.

* swarm/api/http: handle error in TestBzzGetPath

* swarm/api: remove extra blank line in comment
Janoš Guljaš 7 years ago
parent
commit
542d51895f
4 changed files with 80 additions and 16 deletions
  1. 23 13
      swarm/api/http/server.go
  2. 35 1
      swarm/api/http/server_test.go
  3. 8 2
      swarm/api/uri.go
  4. 14 0
      swarm/api/uri_test.go

+ 23 - 13
swarm/api/http/server.go

@@ -290,9 +290,12 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
 	fmt.Fprint(w, newKey)
 }
 
-// HandleGetRaw handles a GET request to bzz-raw://<key> and responds with
-// the raw content stored at the given storage key
-func (s *Server) HandleGetRaw(w http.ResponseWriter, r *Request) {
+// HandleGet handles a GET request to
+// - bzz-raw://<key> and responds with the raw content stored at the
+//   given storage key
+// - bzz-hash://<key> and responds with the hash of the content stored
+//   at the given storage key as a text/plain response
+func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
 	key, err := s.api.Resolve(r.uri)
 	if err != nil {
 		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
@@ -345,15 +348,22 @@ func (s *Server) HandleGetRaw(w http.ResponseWriter, r *Request) {
 		return
 	}
 
-	// allow the request to overwrite the content type using a query
-	// parameter
-	contentType := "application/octet-stream"
-	if typ := r.URL.Query().Get("content_type"); typ != "" {
-		contentType = typ
-	}
-	w.Header().Set("Content-Type", contentType)
+	switch {
+	case r.uri.Raw():
+		// allow the request to overwrite the content type using a query
+		// parameter
+		contentType := "application/octet-stream"
+		if typ := r.URL.Query().Get("content_type"); typ != "" {
+			contentType = typ
+		}
+		w.Header().Set("Content-Type", contentType)
 
-	http.ServeContent(w, &r.Request, "", time.Now(), reader)
+		http.ServeContent(w, &r.Request, "", time.Now(), reader)
+	case r.uri.Hash():
+		w.Header().Set("Content-Type", "text/plain")
+		w.WriteHeader(http.StatusOK)
+		fmt.Fprint(w, key)
+	}
 }
 
 // HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
@@ -619,8 +629,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		s.HandleDelete(w, req)
 
 	case "GET":
-		if uri.Raw() || uri.DeprecatedRaw() {
-			s.HandleGetRaw(w, req)
+		if uri.Raw() || uri.Hash() || uri.DeprecatedRaw() {
+			s.HandleGet(w, req)
 			return
 		}
 

+ 35 - 1
swarm/api/http/server_test.go

@@ -33,7 +33,7 @@ import (
 	"github.com/ethereum/go-ethereum/swarm/testutil"
 )
 
-func TestBzzrGetPath(t *testing.T) {
+func TestBzzGetPath(t *testing.T) {
 
 	var err error
 
@@ -104,6 +104,38 @@ func TestBzzrGetPath(t *testing.T) {
 		}
 	}
 
+	for k, v := range testrequests {
+		var resp *http.Response
+		var respbody []byte
+
+		url := srv.URL + "/bzz-hash:/"
+		if k[:] != "" {
+			url += common.ToHex(key[0])[2:] + "/" + k[1:]
+		}
+		resp, err = http.Get(url)
+		if err != nil {
+			t.Fatalf("Request failed: %v", err)
+		}
+		defer resp.Body.Close()
+		respbody, err = ioutil.ReadAll(resp.Body)
+		if err != nil {
+			t.Fatalf("Read request body: %v", err)
+		}
+
+		if string(respbody) != key[v].String() {
+			isexpectedfailrequest := false
+
+			for _, r := range expectedfailrequests {
+				if k[:] == r {
+					isexpectedfailrequest = true
+				}
+			}
+			if !isexpectedfailrequest {
+				t.Fatalf("Response body does not match, expected: %v, got %v", key[v], string(respbody))
+			}
+		}
+	}
+
 	for _, c := range []struct {
 		path string
 		json string
@@ -197,6 +229,7 @@ func TestBzzrGetPath(t *testing.T) {
 		srv.URL + "/bzz-immutable:/nonhash",
 		srv.URL + "/bzz-raw:/nonhash",
 		srv.URL + "/bzz-list:/nonhash",
+		srv.URL + "/bzz-hash:/nonhash",
 	}
 
 	nonhashresponses := []string{
@@ -204,6 +237,7 @@ func TestBzzrGetPath(t *testing.T) {
 		"error resolving nonhash: immutable address not a content hash: &#34;nonhash&#34;",
 		"error resolving nonhash: no DNS to resolve name: &#34;nonhash&#34;",
 		"error resolving nonhash: no DNS to resolve name: &#34;nonhash&#34;",
+		"error resolving nonhash: no DNS to resolve name: &#34;nonhash&#34;",
 	}
 
 	for i, url := range nonhashtests {

+ 8 - 2
swarm/api/uri.go

@@ -36,6 +36,8 @@ type URI struct {
 	// * bzzr - raw swarm content
 	// * bzzi - immutable URI of an entry in a swarm manifest
 	//          (address is not resolved)
+	// * bzz-hash - hash of swarm content
+	//
 	Scheme string
 
 	// Addr is either a hexadecimal storage key or it an address which
@@ -56,7 +58,7 @@ type URI struct {
 // * <scheme>://<addr>
 // * <scheme>://<addr>/<path>
 //
-// with scheme one of bzz, bzz-raw, bzz-immutable or bzz-list
+// with scheme one of bzz, bzz-raw, bzz-immutable, bzz-list or bzz-hash
 // or deprecated ones bzzr and bzzi
 func Parse(rawuri string) (*URI, error) {
 	u, err := url.Parse(rawuri)
@@ -67,7 +69,7 @@ func Parse(rawuri string) (*URI, error) {
 
 	// check the scheme is valid
 	switch uri.Scheme {
-	case "bzz", "bzz-raw", "bzz-immutable", "bzz-list", "bzzr", "bzzi":
+	case "bzz", "bzz-raw", "bzz-immutable", "bzz-list", "bzz-hash", "bzzr", "bzzi":
 	default:
 		return nil, fmt.Errorf("unknown scheme %q", u.Scheme)
 	}
@@ -110,6 +112,10 @@ func (u *URI) DeprecatedImmutable() bool {
 	return u.Scheme == "bzzi"
 }
 
+func (u *URI) Hash() bool {
+	return u.Scheme == "bzz-hash"
+}
+
 func (u *URI) String() string {
 	return u.Scheme + ":/" + u.Addr + "/" + u.Path
 }

+ 14 - 0
swarm/api/uri_test.go

@@ -29,6 +29,7 @@ func TestParseURI(t *testing.T) {
 		expectRaw                 bool
 		expectImmutable           bool
 		expectList                bool
+		expectHash                bool
 		expectDeprecatedRaw       bool
 		expectDeprecatedImmutable bool
 	}
@@ -98,6 +99,16 @@ func TestParseURI(t *testing.T) {
 			uri:       "bzz://abc123/path/to/entry",
 			expectURI: &URI{Scheme: "bzz", Addr: "abc123", Path: "path/to/entry"},
 		},
+		{
+			uri:        "bzz-hash:",
+			expectURI:  &URI{Scheme: "bzz-hash"},
+			expectHash: true,
+		},
+		{
+			uri:        "bzz-hash:/",
+			expectURI:  &URI{Scheme: "bzz-hash"},
+			expectHash: true,
+		},
 		{
 			uri:        "bzz-list:",
 			expectURI:  &URI{Scheme: "bzz-list"},
@@ -152,6 +163,9 @@ func TestParseURI(t *testing.T) {
 		if actual.List() != x.expectList {
 			t.Fatalf("expected %s list to be %t, got %t", x.uri, x.expectList, actual.List())
 		}
+		if actual.Hash() != x.expectHash {
+			t.Fatalf("expected %s hash to be %t, got %t", x.uri, x.expectHash, actual.Hash())
+		}
 		if actual.DeprecatedRaw() != x.expectDeprecatedRaw {
 			t.Fatalf("expected %s deprecated raw to be %t, got %t", x.uri, x.expectDeprecatedRaw, actual.DeprecatedRaw())
 		}