aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/api
diff options
context:
space:
mode:
Diffstat (limited to 'swarm/api')
-rw-r--r--swarm/api/api.go10
-rw-r--r--swarm/api/http/error.go46
-rw-r--r--swarm/api/http/error_templates.go181
-rw-r--r--swarm/api/http/server.go66
-rw-r--r--swarm/api/manifest.go32
-rw-r--r--swarm/api/manifest_test.go15
6 files changed, 311 insertions, 39 deletions
diff --git a/swarm/api/api.go b/swarm/api/api.go
index a5941fb5c..79de29a1c 100644
--- a/swarm/api/api.go
+++ b/swarm/api/api.go
@@ -144,9 +144,13 @@ func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionRe
if entry != nil {
key = common.Hex2Bytes(entry.Hash)
status = entry.Status
- mimeType = entry.ContentType
- log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
- reader = self.dpa.Retrieve(key)
+ if status == http.StatusMultipleChoices {
+ return
+ } else {
+ mimeType = entry.ContentType
+ log.Trace(fmt.Sprintf("content lookup key: '%v' (%v)", key, mimeType))
+ reader = self.dpa.Retrieve(key)
+ }
} else {
status = http.StatusNotFound
err = fmt.Errorf("manifest entry for '%s' not found", path)
diff --git a/swarm/api/http/error.go b/swarm/api/http/error.go
index ebb5e3ebe..b4d46b3c4 100644
--- a/swarm/api/http/error.go
+++ b/swarm/api/http/error.go
@@ -25,9 +25,11 @@ import (
"fmt"
"html/template"
"net/http"
+ "strings"
"time"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/swarm/api"
)
//templateMap holds a mapping of an HTTP error code to a template
@@ -51,12 +53,14 @@ func initErrHandling() {
//pages are saved as strings - get these strings
genErrPage := GetGenericErrorPage()
notFoundPage := GetNotFoundErrorPage()
+ multipleChoicesPage := GetMultipleChoicesErrorPage()
//map the codes to the available pages
tnames := map[int]string{
- 0: genErrPage, //default
- 400: genErrPage,
- 404: notFoundPage,
- 500: genErrPage,
+ 0: genErrPage, //default
+ http.StatusBadRequest: genErrPage,
+ http.StatusNotFound: notFoundPage,
+ http.StatusMultipleChoices: multipleChoicesPage,
+ http.StatusInternalServerError: genErrPage,
}
templateMap = make(map[int]*template.Template)
for code, tname := range tnames {
@@ -65,6 +69,40 @@ func initErrHandling() {
}
}
+//ShowMultipeChoices is used when a user requests a resource in a manifest which results
+//in ambiguous results. It returns a HTML page with clickable links of each of the entry
+//in the manifest which fits the request URI ambiguity.
+//For example, if the user requests bzz:/<hash>/read and that manifest containes entries
+//"readme.md" and "readinglist.txt", a HTML page is returned with this two links.
+//This only applies if the manifest has no default entry
+func ShowMultipleChoices(w http.ResponseWriter, r *http.Request, list api.ManifestList) {
+ msg := ""
+ if list.Entries == nil {
+ ShowError(w, r, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
+ //make links relative
+ //requestURI comes with the prefix of the ambiguous path, e.g. "read" for "readme.md" and "readinglist.txt"
+ //to get clickable links, need to remove the ambiguous path, i.e. "read"
+ idx := strings.LastIndex(r.RequestURI, "/")
+ if idx == -1 {
+ ShowError(w, r, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
+ //remove ambiguous part
+ base := r.RequestURI[:idx+1]
+ for _, e := range list.Entries {
+ //create clickable link for each entry
+ msg += "<a href='" + base + e.Path + "'>" + e.Path + "</a><br/>"
+ }
+ respond(w, r, &ErrorParams{
+ Code: http.StatusMultipleChoices,
+ Details: template.HTML(msg),
+ Timestamp: time.Now().Format(time.RFC1123),
+ template: getTemplate(http.StatusMultipleChoices),
+ })
+}
+
//ShowError is used to show an HTML error page to a client.
//If there is an `Accept` header of `application/json`, JSON will be returned instead
//The function just takes a string message which will be displayed in the error page.
diff --git a/swarm/api/http/error_templates.go b/swarm/api/http/error_templates.go
index 29bd3bfbb..2c20ba8f9 100644
--- a/swarm/api/http/error_templates.go
+++ b/swarm/api/http/error_templates.go
@@ -165,7 +165,7 @@ func GetGenericErrorPage() string {
</tr>
<tr>
<td class="value">
- {{.Msg}}
+ {{.Msg}}
</td>
</tr>
@@ -339,7 +339,184 @@ func GetNotFoundErrorPage() string {
</tr>
<tr>
<td class="value">
- {{.Msg}}
+ {{.Msg}}
+ </td>
+ </tr>
+
+ <tr>
+ <td class="key">
+ Error code:
+ </td>
+ </tr>
+ <tr>
+ <td class="value">
+ {{.Code}}
+ </td>
+ </tr>
+
+ </tbody>
+ </table>
+ </section>
+ </content-body>
+
+ <footer>
+ <p>
+ Swarm: Serverless Hosting Incentivised Peer-To-Peer Storage And Content Distribution<br/>
+ <a href="http://swarm-gateways.net/bzz:/theswarm.eth">Swarm</a>
+ </p>
+ </footer>
+
+
+ </div>
+ </body>
+
+</html>
+`
+ return page
+}
+
+//This returns the HTML for a page listing disambiguation options
+//i.e. if user requested bzz:/<hash>/read and the manifest contains "readme.md" and "readinglist.txt",
+//this page is returned with a clickable list the existing disambiguation links in the manifest
+func GetMultipleChoicesErrorPage() string {
+ page := `
+<html>
+
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+ <meta http-equiv="X-UA-Compatible" ww="chrome=1">
+ <meta name="description" content="Ethereum/Swarm multiple options page">
+ <meta property="og:url" content="https://swarm-gateways.net/bzz:/theswarm.eth">
+
+ <style>
+
+ body, div, header, footer {
+ margin: 0;
+ padding: 0;
+ }
+
+ body {
+ overflow: hidden;
+ }
+
+ .container {
+ min-width: 100%;
+ min-height: 100%;
+ max-height: 100%;
+ }
+
+ header {
+ display: flex;
+ align-items: center;
+ background-color: #ffa500;
+ /* height: 20vh; */
+ padding: 5px;
+ }
+
+ .header-left, .header-right {
+ width: 20%;
+ }
+
+ .header-left {
+ padding-left: 40px;
+ float: left;
+ }
+
+ .header-right {
+ padding-right: 40px;
+ float: right;
+ }
+
+ .page-title {
+ /* margin-top: 4.5vh; */
+ text-align: center;
+ float: left;
+ width: 60%;
+ color: white;
+ }
+
+ content-body {
+ display: block;
+ margin: 0 auto;
+ /* width: 50%; */
+ min-height: 60vh;
+ max-height: 60vh;
+ padding: 50px 20px;
+ opacity: 0.6;
+ background-color: #A9F5BF;
+ }
+
+ table {
+ font-size: 1.2em;
+ margin: 0 auto;
+ }
+
+ tr {
+ height: 60px;
+ }
+
+ td {
+ text-align: center;
+ }
+
+ .key {
+ color: #111;
+ font-weight: bold;
+ width: 200px;
+ }
+
+ .value {
+ color: red;
+ font-weight: bold
+ }
+
+ footer {
+ height: 20vh;
+ background-color: #ffa500;
+ font-size: 1em;
+ text-align: center;
+ padding: 20px;
+ }
+
+ </style>
+
+ <title>Swarm::HTTP Disambiguation Page</title>
+ </head>
+
+
+ <body>
+ <div class="container">
+
+ <header>
+ <div class="header-left">
+ <img style="height:18vh;margin-left:40px" src="data:image/png;base64,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"/>
+ </div>
+ <div class="page-title">
+ <h1>Swarm: disambiguation</h1>
+ </div>
+ <div class="header-right">
+ <div id="timestamp">{{.Timestamp}}</div>
+ </div>
+ </header>
+
+ <content-body>
+ <section>
+ <table>
+ <thead>
+ <td style="height: 150px; font-size: 1.3em; color: black; font-weight: bold">
+ Your request yields ambiguous results!
+ </td>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="key">
+ Your request may refer to:
+ </td>
+ </tr>
+ <tr>
+ <td class="value">
+ {{ .Details}}
</td>
</tr>
diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go
index a637b8735..65f6afab7 100644
--- a/swarm/api/http/server.go
+++ b/swarm/api/http/server.go
@@ -441,14 +441,37 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
return
}
- walker, err := s.api.NewManifestWalker(key, nil)
+ list, err := s.getManifestList(key, r.uri.Path)
+
if err != nil {
s.Error(w, r, err)
return
}
- var list api.ManifestList
- prefix := r.uri.Path
+ // if the client wants HTML (e.g. a browser) then render the list as a
+ // HTML index with relative URLs
+ if strings.Contains(r.Header.Get("Accept"), "text/html") {
+ w.Header().Set("Content-Type", "text/html")
+ err := htmlListTemplate.Execute(w, &htmlListData{
+ URI: r.uri,
+ List: &list,
+ })
+ if err != nil {
+ s.logError("error rendering list HTML: %s", err)
+ }
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(&list)
+}
+
+func (s *Server) getManifestList(key storage.Key, prefix string) (list api.ManifestList, err error) {
+ walker, err := s.api.NewManifestWalker(key, nil)
+ if err != nil {
+ return
+ }
+
err = walker.Walk(func(entry *api.ManifestEntry) error {
// handle non-manifest files
if entry.ContentType != api.ManifestType {
@@ -495,27 +518,8 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
// so just skip it
return api.SkipManifest
})
- if err != nil {
- s.Error(w, r, err)
- return
- }
- // if the client wants HTML (e.g. a browser) then render the list as a
- // HTML index with relative URLs
- if strings.Contains(r.Header.Get("Accept"), "text/html") {
- w.Header().Set("Content-Type", "text/html")
- err := htmlListTemplate.Execute(w, &htmlListData{
- URI: r.uri,
- List: &list,
- })
- if err != nil {
- s.logError("error rendering list HTML: %s", err)
- }
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(&list)
+ return list, nil
}
// HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
@@ -544,6 +548,22 @@ func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
return
}
+ //the request results in ambiguous files
+ //e.g. /read with readme.md and readinglist.txt available in manifest
+ if status == http.StatusMultipleChoices {
+ list, err := s.getManifestList(key, r.uri.Path)
+
+ if err != nil {
+ s.Error(w, r, err)
+ return
+ }
+
+ s.logDebug(fmt.Sprintf("Multiple choices! --> %v", list))
+ //show a nice page links to available entries
+ ShowMultipleChoices(w, &r.Request, list)
+ return
+ }
+
// check the root chunk exists by retrieving the file's size
if _, err := reader.Size(nil); err != nil {
s.NotFound(w, r, fmt.Errorf("File not found %s: %s", r.uri, err))
diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go
index 90f287677..d3eced198 100644
--- a/swarm/api/manifest.go
+++ b/swarm/api/manifest.go
@@ -22,6 +22,8 @@ import (
"errors"
"fmt"
"io"
+ "net/http"
+ "strings"
"sync"
"time"
@@ -422,25 +424,47 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man
return self.entries[256], 0
}
+ //see if first char is in manifest entries
b := byte(path[0])
entry = self.entries[b]
if entry == nil {
return self.entries[256], 0
}
+
epl := len(entry.Path)
log.Trace(fmt.Sprintf("path = %v entry.Path = %v epl = %v", path, entry.Path, epl))
- if (len(path) >= epl) && (path[:epl] == entry.Path) {
+ if len(path) <= epl {
+ if entry.Path[:len(path)] == path {
+ if entry.ContentType == ManifestType {
+ entry.Status = http.StatusMultipleChoices
+ }
+ pos = len(path)
+ return
+ }
+ return nil, 0
+ }
+ if path[:epl] == entry.Path {
log.Trace(fmt.Sprintf("entry.ContentType = %v", entry.ContentType))
- if entry.ContentType == ManifestType {
+ //the subentry is a manifest, load subtrie
+ if entry.ContentType == ManifestType && (strings.Contains(entry.Path, path) || strings.Contains(path, entry.Path)) {
err := self.loadSubTrie(entry, quitC)
if err != nil {
return nil, 0
}
- entry, pos = entry.subtrie.findPrefixOf(path[epl:], quitC)
- if entry != nil {
+ sub, pos := entry.subtrie.findPrefixOf(path[epl:], quitC)
+ if sub != nil {
+ entry = sub
pos += epl
+ return sub, pos
+ } else if path == entry.Path {
+ entry.Status = http.StatusMultipleChoices
}
+
} else {
+ //entry is not a manifest, return it
+ if path != entry.Path {
+ return nil, 0
+ }
pos = epl
}
}
diff --git a/swarm/api/manifest_test.go b/swarm/api/manifest_test.go
index 0208848a3..f048627c5 100644
--- a/swarm/api/manifest_test.go
+++ b/swarm/api/manifest_test.go
@@ -17,7 +17,6 @@
package api
import (
- // "encoding/json"
"bytes"
"encoding/json"
"fmt"
@@ -72,11 +71,21 @@ func TestGetEntry(t *testing.T) {
testGetEntry(t, "/a", "", "")
testGetEntry(t, "/a/b", "a/b", "a/b")
// longest/deepest math
- testGetEntry(t, "a/b", "-", "a", "a/ba", "a/b/c")
+ testGetEntry(t, "read", "read", "readme.md", "readit.md")
+ testGetEntry(t, "rf", "-", "readme.md", "readit.md")
+ testGetEntry(t, "readme", "readme", "readme.md")
+ testGetEntry(t, "readme", "-", "readit.md")
+ testGetEntry(t, "readme.md", "readme.md", "readme.md")
+ testGetEntry(t, "readme.md", "-", "readit.md")
+ testGetEntry(t, "readmeAmd", "-", "readit.md")
+ testGetEntry(t, "readme.mdffff", "-", "readme.md")
+ testGetEntry(t, "ab", "ab", "ab/cefg", "ab/cedh", "ab/kkkkkk")
+ testGetEntry(t, "ab/ce", "ab/ce", "ab/cefg", "ab/cedh", "ab/ceuuuuuuuuuu")
+ testGetEntry(t, "abc", "abc", "abcd", "abczzzzef", "abc/def", "abc/e/g")
+ testGetEntry(t, "a/b", "a/b", "a", "a/bc", "a/ba", "a/b/c")
testGetEntry(t, "a/b", "a/b", "a", "a/b", "a/bb", "a/b/c")
testGetEntry(t, "//a//b//", "a/b", "a", "a/b", "a/bb", "a/b/c")
}
-
func TestDeleteEntry(t *testing.T) {
}