mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
swarm/api: return hash of the content for bzz-hash:// requests
This commit is contained in:
parent
700ac3b03e
commit
77e5d7e8e2
3 changed files with 99 additions and 59 deletions
|
|
@ -130,6 +130,15 @@ func (self *Api) Put(content, contentType string) (storage.Key, error) {
|
||||||
// to resolve basePath to content using dpa retrieve
|
// to resolve basePath to content using dpa retrieve
|
||||||
// it returns a section reader, mimeType, status and an error
|
// it returns a section reader, mimeType, status and an error
|
||||||
func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
|
func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) {
|
||||||
|
reader, _, mimeType, status, err = self.GetHash(key, path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHash extends the Get method to return the hash of the content,
|
||||||
|
// it uses iterative manifest retrieval and prefix matching
|
||||||
|
// to resolve basePath to content using dpa retrieve
|
||||||
|
// it returns a section reader, hash, mimeType, status and an error
|
||||||
|
func (self *Api) GetHash(key storage.Key, path string) (reader storage.LazySectionReader, hash storage.Key, mimeType string, status int, err error) {
|
||||||
trie, err := loadManifest(self.dpa, key, nil)
|
trie, err := loadManifest(self.dpa, key, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
|
|
@ -142,14 +151,14 @@ func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionRe
|
||||||
entry, _ := trie.getEntry(path)
|
entry, _ := trie.getEntry(path)
|
||||||
|
|
||||||
if entry != nil {
|
if entry != nil {
|
||||||
key = common.Hex2Bytes(entry.Hash)
|
hash = common.Hex2Bytes(entry.Hash)
|
||||||
status = entry.Status
|
status = entry.Status
|
||||||
if status == http.StatusMultipleChoices {
|
if status == http.StatusMultipleChoices {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
mimeType = entry.ContentType
|
mimeType = entry.ContentType
|
||||||
log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
|
log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", hash, mimeType))
|
||||||
reader = self.dpa.Retrieve(key)
|
reader = self.dpa.Retrieve(hash)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
|
|
|
||||||
|
|
@ -290,12 +290,9 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
|
||||||
fmt.Fprint(w, newKey)
|
fmt.Fprint(w, newKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleGet handles a GET request to
|
// HandleGetRaw handles a GET request to bzzr://<key> and responds with
|
||||||
// - bzzr://<key> and responds with the raw content stored at the
|
// the raw content stored at the given storage key
|
||||||
// given storage key
|
func (s *Server) HandleGetRaw(w http.ResponseWriter, r *Request) {
|
||||||
// - 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)
|
key, err := s.api.Resolve(r.uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
|
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
|
||||||
|
|
@ -348,22 +345,15 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
// allow the request to overwrite the content type using a query
|
||||||
case r.uri.Raw():
|
// parameter
|
||||||
// allow the request to overwrite the content type using a query
|
contentType := "application/octet-stream"
|
||||||
// parameter
|
if typ := r.URL.Query().Get("content_type"); typ != "" {
|
||||||
contentType := "application/octet-stream"
|
contentType = typ
|
||||||
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)
|
|
||||||
case r.uri.Hash():
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprint(w, key)
|
|
||||||
}
|
}
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
http.ServeContent(w, &r.Request, "", time.Now(), reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
|
// HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
|
||||||
|
|
@ -532,8 +522,11 @@ func (s *Server) getManifestList(key storage.Key, prefix string) (list api.Manif
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
|
// HandleGetFile handles a GET request to
|
||||||
// with the content of the file at <path> from the given <manifest>
|
// - bzz://<manifest>/<path> and responds with the content of the file at
|
||||||
|
// <path> from the given <manifest>
|
||||||
|
// - bzz-hash://<key>/<path> and responds with the hash of the content stored
|
||||||
|
// at the given storage key as a text/plain response
|
||||||
func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
|
func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
|
||||||
// ensure the root path has a trailing slash so that relative URLs work
|
// ensure the root path has a trailing slash so that relative URLs work
|
||||||
if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
|
if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
|
@ -547,7 +540,7 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, contentType, status, err := s.api.Get(key, r.uri.Path)
|
reader, hash, contentType, status, err := s.api.GetHash(key, r.uri.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch status {
|
switch status {
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
|
|
@ -580,6 +573,13 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.uri.Hash() {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprint(w, hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
http.ServeContent(w, &r.Request, "", time.Now(), reader)
|
http.ServeContent(w, &r.Request, "", time.Now(), reader)
|
||||||
|
|
@ -626,8 +626,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.HandleDelete(w, req)
|
s.HandleDelete(w, req)
|
||||||
|
|
||||||
case "GET":
|
case "GET":
|
||||||
if uri.Raw() || uri.Hash() {
|
if uri.Raw() {
|
||||||
s.HandleGet(w, req)
|
s.HandleGetRaw(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBzzGetPath(t *testing.T) {
|
func TestBzzrGetPath(t *testing.T) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
@ -104,35 +104,6 @@ func TestBzzGetPath(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 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nonhashtests := []string{
|
nonhashtests := []string{
|
||||||
srv.URL + "/bzz:/name",
|
srv.URL + "/bzz:/name",
|
||||||
srv.URL + "/bzzi:/nonhash",
|
srv.URL + "/bzzi:/nonhash",
|
||||||
|
|
@ -225,3 +196,63 @@ func TestBzzRootRedirect(t *testing.T) {
|
||||||
t.Fatalf("expected response to equal %q, got %q", data, gotData)
|
t.Fatalf("expected response to equal %q, got %q", data, gotData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBzzHash tests if requests with bzz-hash:// scheme
|
||||||
|
// return the hash of the swarm content.
|
||||||
|
func TestBzzHash(t *testing.T) {
|
||||||
|
srv := testutil.NewTestSwarmServer(t)
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
client := swarm.NewClient(srv.URL)
|
||||||
|
|
||||||
|
for _, c := range []struct {
|
||||||
|
data string
|
||||||
|
path string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: "test root",
|
||||||
|
path: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "test /a",
|
||||||
|
path: "a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "test /a/b",
|
||||||
|
path: "a/b",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run("path "+c.path, func(t *testing.T) {
|
||||||
|
hash, err := client.Upload(&swarm.File{
|
||||||
|
ReadCloser: ioutil.NopCloser(strings.NewReader(c.data)),
|
||||||
|
ManifestEntry: api.ManifestEntry{
|
||||||
|
Path: "",
|
||||||
|
ContentType: "text/plain",
|
||||||
|
Size: int64(len(c.data)),
|
||||||
|
},
|
||||||
|
}, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest, err := client.DownloadManifest(hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := http.Get(srv.URL + "/bzz-hash:/" + hash + "/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(body) != manifest.Entries[0].Hash {
|
||||||
|
t.Fatalf("expected response to equal %q, got %q", manifest.Entries[0].Hash, string(body))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue