swarm/api: fixed 404 handling on missing default entry (#15139)
This commit is contained in:
@ -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.
|
||||
|
File diff suppressed because one or more lines are too long
@ -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))
|
||||
|
Reference in New Issue
Block a user