From cfd842017741a1aee9e3d831d49431fa67f2b5b4 Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Wed, 13 Dec 2017 12:58:18 +0100 Subject: [PATCH] swarm/api: url scheme bzz-list for getting list of files from manifest Replace query parameter list=true for listing all files contained in a swarm manifest with a new URL scheme bzz-list. --- swarm/api/client/client.go | 2 +- swarm/api/http/server.go | 23 +++++---- swarm/api/http/server_test.go | 90 +++++++++++++++++++++++++++++++++++ swarm/api/http/templates.go | 2 +- swarm/api/uri.go | 9 +++- swarm/api/uri_test.go | 14 ++++++ 6 files changed, 126 insertions(+), 14 deletions(-) diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go index 7952d3fb68..1a6327cb51 100644 --- a/swarm/api/client/client.go +++ b/swarm/api/client/client.go @@ -269,7 +269,7 @@ func (c *Client) DownloadManifest(hash string) (*api.Manifest, error) { // // where entries ending with "/" are common prefixes. func (c *Client) List(hash, prefix string) (*api.ManifestList, error) { - res, err := http.DefaultClient.Get(c.Gateway + "/bzz:/" + hash + "/" + prefix + "?list=true") + res, err := http.DefaultClient.Get(c.Gateway + "/bzz-list:/" + hash + "/" + prefix) if err != nil { return nil, err } diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go index 65f6afab72..e5fefc6eec 100644 --- a/swarm/api/http/server.go +++ b/swarm/api/http/server.go @@ -424,14 +424,13 @@ func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) { } } -// HandleGetList handles a GET request to bzz:// which has -// the "list" query parameter set to "true" and returns a list of all files -// contained in under grouped into common prefixes using -// "/" as a delimiter +// HandleGetList handles a GET request to bzz-list:// and returns +// a list of all files contained in under grouped into +// common prefixes using "/" as a delimiter func (s *Server) HandleGetList(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+"/?list=true", http.StatusMovedPermanently) + http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently) return } @@ -453,7 +452,11 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { if strings.Contains(r.Header.Get("Accept"), "text/html") { w.Header().Set("Content-Type", "text/html") err := htmlListTemplate.Execute(w, &htmlListData{ - URI: r.uri, + URI: &api.URI{ + Scheme: "bzz", + Addr: r.uri.Addr, + Path: r.uri.Path, + }, List: &list, }) if err != nil { @@ -621,13 +624,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - if r.Header.Get("Accept") == "application/x-tar" { - s.HandleGetFiles(w, req) + if uri.List() { + s.HandleGetList(w, req) return } - if r.URL.Query().Get("list") == "true" { - s.HandleGetList(w, req) + if r.Header.Get("Accept") == "application/x-tar" { + s.HandleGetFiles(w, req) return } diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go index ffeaf6e0d8..62626586bb 100644 --- a/swarm/api/http/server_test.go +++ b/swarm/api/http/server_test.go @@ -104,16 +104,106 @@ func TestBzzrGetPath(t *testing.T) { } } + for _, c := range []struct { + path string + json string + html string + }{ + { + path: "/", + json: `{"common_prefixes":["a/"]}`, + html: "\n\n\n \n \n Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/\n\n\n\n

Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/

\n
\n \n \n \n\t\n\t\n\t\n \n \n\n \n \n\t\n\t \n\t \n\t \n\t\n \n\n \n
PathTypeSize
a/DIR-
\n
\n\n", + }, + { + path: "/a/", + json: `{"common_prefixes":["a/b/"],"entries":[{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/a","mod_time":"0001-01-01T00:00:00Z"}]}`, + html: "\n\n\n \n \n Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/a/\n\n\n\n

Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/a/

\n
\n \n \n \n\t\n\t\n\t\n \n \n\n \n \n\t\n\t \n\t \n\t \n\t\n \n\n \n\t\n\t \n\t \n\t \n\t\n \n
PathTypeSize
b/DIR-
a0
\n
\n\n", + }, + { + path: "/a/b/", + json: `{"entries":[{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/b/b","mod_time":"0001-01-01T00:00:00Z"},{"hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","path":"a/b/c","mod_time":"0001-01-01T00:00:00Z"}]}`, + html: "\n\n\n \n \n Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/a/b/\n\n\n\n

Swarm index of bzz:/262e5c08c03c2789b6daef487dfa14b4d132f5340d781a3ecb1d5122ab65640c/a/b/

\n
\n \n \n \n\t\n\t\n\t\n \n \n\n \n \n\n \n\t\n\t \n\t \n\t \n\t\n \n\t\n\t \n\t \n\t \n\t\n \n
PathTypeSize
b0
c0
\n
\n\n", + }, + { + path: "/x", + }, + { + path: "", + }, + } { + k := c.path + url := srv.URL + "/bzz-list:/" + if k[:] != "" { + url += common.ToHex(key[0])[2:] + "/" + k[1:] + } + t.Run("json list "+c.path, func(t *testing.T) { + resp, err := http.Get(url) + if err != nil { + t.Fatalf("HTTP request: %v", err) + } + defer resp.Body.Close() + respbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Read response body: %v", err) + } + + body := strings.TrimSpace(string(respbody)) + if body != c.json { + isexpectedfailrequest := false + + for _, r := range expectedfailrequests { + if k[:] == r { + isexpectedfailrequest = true + } + } + if !isexpectedfailrequest { + t.Errorf("Response list body %q does not match, expected: %v, got %v", k, c.json, body) + } + } + }) + t.Run("html list "+c.path, func(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + t.Fatalf("New request: %v", err) + } + req.Header.Set("Accept", "text/html") + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("HTTP request: %v", err) + } + defer resp.Body.Close() + respbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Read response body: %v", err) + } + + if string(respbody) != c.html { + isexpectedfailrequest := false + + for _, r := range expectedfailrequests { + if k[:] == r { + isexpectedfailrequest = true + } + } + if !isexpectedfailrequest { + t.Errorf("Response list body %q does not match, expected: %q, got %q", k, c.html, string(respbody)) + } + } + }) + } + nonhashtests := []string{ srv.URL + "/bzz:/name", srv.URL + "/bzzi:/nonhash", srv.URL + "/bzzr:/nonhash", + srv.URL + "/bzz-list:/nonhash", } nonhashresponses := []string{ "error resolving name: no DNS to resolve name: "name"", "error resolving nonhash: immutable address not a content hash: "nonhash"", "error resolving nonhash: no DNS to resolve name: "nonhash"", + "error resolving nonhash: no DNS to resolve name: "nonhash"", } for i, url := range nonhashtests { diff --git a/swarm/api/http/templates.go b/swarm/api/http/templates.go index 9ae434a7ed..53ce7b5a22 100644 --- a/swarm/api/http/templates.go +++ b/swarm/api/http/templates.go @@ -52,7 +52,7 @@ var htmlListTemplate = template.Must(template.New("html-list").Funcs(template.Fu {{ range .List.CommonPrefixes }} - {{ basename . }}/ + {{ basename . }}/ DIR - diff --git a/swarm/api/uri.go b/swarm/api/uri.go index caed4212d5..79196c7327 100644 --- a/swarm/api/uri.go +++ b/swarm/api/uri.go @@ -30,6 +30,7 @@ type URI struct { // * bzzr - raw swarm content // * bzzi - immutable URI of an entry in a swarm manifest // (address is not resolved) + // * bzz-list - list of all files contained in a swarm manifest Scheme string // Addr is either a hexadecimal storage key or it an address which @@ -50,7 +51,7 @@ type URI struct { // * :// // * :/// // -// with scheme one of bzz, bzzr or bzzi +// with scheme one of bzz, bzzr, bzzi or bzz-list func Parse(rawuri string) (*URI, error) { u, err := url.Parse(rawuri) if err != nil { @@ -60,7 +61,7 @@ func Parse(rawuri string) (*URI, error) { // check the scheme is valid switch uri.Scheme { - case "bzz", "bzzi", "bzzr": + case "bzz", "bzzi", "bzzr", "bzz-list": default: return nil, fmt.Errorf("unknown scheme %q", u.Scheme) } @@ -91,6 +92,10 @@ func (u *URI) Immutable() bool { return u.Scheme == "bzzi" } +func (u *URI) List() bool { + return u.Scheme == "bzz-list" +} + func (u *URI) String() string { return u.Scheme + ":/" + u.Addr + "/" + u.Path } diff --git a/swarm/api/uri_test.go b/swarm/api/uri_test.go index 7d4160601d..aa150cf3d7 100644 --- a/swarm/api/uri_test.go +++ b/swarm/api/uri_test.go @@ -28,6 +28,7 @@ func TestParseURI(t *testing.T) { expectErr bool expectRaw bool expectImmutable bool + expectList bool } tests := []test{ { @@ -95,6 +96,16 @@ func TestParseURI(t *testing.T) { uri: "bzz://abc123/path/to/entry", expectURI: &URI{Scheme: "bzz", Addr: "abc123", Path: "path/to/entry"}, }, + { + uri: "bzz-list:", + expectURI: &URI{Scheme: "bzz-list"}, + expectList: true, + }, + { + uri: "bzz-list:/", + expectURI: &URI{Scheme: "bzz-list"}, + expectList: true, + }, } for _, x := range tests { actual, err := Parse(x.uri) @@ -116,5 +127,8 @@ func TestParseURI(t *testing.T) { if actual.Immutable() != x.expectImmutable { t.Fatalf("expected %s immutable to be %t, got %t", x.uri, x.expectImmutable, actual.Immutable()) } + if actual.List() != x.expectList { + t.Fatalf("expected %s list to be %t, got %t", x.uri, x.expectList, actual.List()) + } } }