mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
http access: fix regression and improve
- correct slash handling and path matching in manifest - fix subpath match and use deepest path instead of longest for matching - http POST: give key as http response instead of fmt.Printf - improved logging
This commit is contained in:
parent
38d1bdf688
commit
021182f822
3 changed files with 53 additions and 31 deletions
18
bzz/api.go
18
bzz/api.go
|
|
@ -300,35 +300,35 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
var size int
|
var size int
|
||||||
size, err = manifestReader.Read(manifestData)
|
size, err = manifestReader.Read(manifestData)
|
||||||
if int64(size) < manifestReader.Size() {
|
if int64(size) < manifestReader.Size() {
|
||||||
dpaLogger.Debugf("Swarm: Manifest for '%s' not found.", uri)
|
dpaLogger.Debugf("Swarm: Manifest for '%s' not found (uri: '%s')", path[:pos], uri)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = fmt.Errorf("Manifest retrieval cut short: %v < %v", size, manifestReader.Size())
|
err = fmt.Errorf("Manifest retrieval cut short: %v < %v", size, manifestReader.Size())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dpaLogger.Debugf("Swarm: Manifest for '%s' retrieved", uri)
|
dpaLogger.Debugf("Swarm: Manifest for '%s' retrieved", path[:pos])
|
||||||
man := manifest{}
|
man := manifest{}
|
||||||
err = json.Unmarshal(manifestData, &man)
|
err = json.Unmarshal(manifestData, &man)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Manifest for '%s' is malformed: %v", uri, err)
|
err = fmt.Errorf("Manifest for '%s' is malformed: %v", path[:pos], err)
|
||||||
dpaLogger.Debugf("Swarm: %v", err)
|
dpaLogger.Debugf("Swarm: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dpaLogger.Debugf("Swarm: Manifest for '%s' has %d entries. Retrieving entry for '%s'", uri, len(man.Entries), path)
|
dpaLogger.Debugf("Swarm: Manifest for '%s' has %d entries. Match path '%s'", path[:pos], len(man.Entries), path[pos:])
|
||||||
|
|
||||||
// retrieve entry that matches path from manifest entries
|
// retrieve entry that matches path from manifest entries
|
||||||
var entry *manifestEntry
|
entry, hop := man.getEntry(path[pos:])
|
||||||
entry, pos = man.getEntry(path)
|
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
err = fmt.Errorf("Content for '%s' not found.", uri)
|
err = fmt.Errorf("Path '%s' not found on manifest '%s'", path[:pos], path[pos:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check hash of entry
|
// check hash of entry
|
||||||
if !hashMatcher.MatchString(entry.Hash) {
|
if !hashMatcher.MatchString(entry.Hash) {
|
||||||
err = fmt.Errorf("Incorrect hash '%064x' for '%s'", entry.Hash, uri)
|
err = fmt.Errorf("Incorrect hash '%064x' for path '%s' on manifest for '%s')", entry.Hash, path[pos:], path[:pos])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
key = common.Hex2Bytes(entry.Hash)
|
key = common.Hex2Bytes(entry.Hash)
|
||||||
|
|
@ -349,7 +349,7 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise continue along the path with manifest resolution
|
// otherwise continue along the path with manifest resolution
|
||||||
path = path[pos:]
|
pos += hop
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,24 @@ A simple http server interface to Swarm
|
||||||
package bzz
|
package bzz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
notFoundStatus = 404
|
|
||||||
rawType = "application/octet-stream"
|
rawType = "application/octet-stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rawUrl = regexp.MustCompile("^/+raw/*")
|
rawUrl = regexp.MustCompile("^/+raw/*")
|
||||||
trailingSlashes = regexp.MustCompile("/+$")
|
trailingSlashes = regexp.MustCompile("/+$")
|
||||||
|
forever = time.Unix(0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
type sequentialReader struct {
|
type sequentialReader struct {
|
||||||
|
|
@ -40,8 +42,9 @@ func startHttpServer(api *Api, port string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
dpaLogger.Debugf("request URL: '%s' Host: '%s', Path: '%s'", r.RequestURI, r.URL.Host, r.URL.Path)
|
requestURL := r.URL
|
||||||
uri := r.URL.Path
|
dpaLogger.Debugf("Swarm: HTTP request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))
|
||||||
|
uri := requestURL.Path
|
||||||
var raw bool
|
var raw bool
|
||||||
path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
|
path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
|
||||||
raw = true
|
raw = true
|
||||||
|
|
@ -50,7 +53,6 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.Method == "POST":
|
case r.Method == "POST":
|
||||||
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, r.URL.Path)
|
|
||||||
if raw {
|
if raw {
|
||||||
dpaLogger.Debugf("Swarm: POST request received.")
|
dpaLogger.Debugf("Swarm: POST request received.")
|
||||||
key, err := api.dpa.Store(io.NewSectionReader(&sequentialReader{
|
key, err := api.dpa.Store(io.NewSectionReader(&sequentialReader{
|
||||||
|
|
@ -58,7 +60,8 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
ahead: make(map[int64]chan bool),
|
ahead: make(map[int64]chan bool),
|
||||||
}, 0, r.ContentLength), nil)
|
}, 0, r.ContentLength), nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Fprintf(w, "%064x", key)
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key))))
|
||||||
dpaLogger.Debugf("Swarm: Content for '%064x' stored", key)
|
dpaLogger.Debugf("Swarm: Content for '%064x' stored", key)
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
|
@ -70,7 +73,6 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case r.Method == "GET" || r.Method == "HEAD":
|
case r.Method == "GET" || r.Method == "HEAD":
|
||||||
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, uri)
|
|
||||||
path = trailingSlashes.ReplaceAllString(path, "")
|
path = trailingSlashes.ReplaceAllString(path, "")
|
||||||
if raw {
|
if raw {
|
||||||
dpaLogger.Debugf("Swarm: Raw GET request '%s' received", uri)
|
dpaLogger.Debugf("Swarm: Raw GET request '%s' received", uri)
|
||||||
|
|
@ -88,14 +90,14 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
dpaLogger.Debugf("Swarm: Reading %d bytes.", reader.Size())
|
dpaLogger.Debugf("Swarm: Reading %d bytes.", reader.Size())
|
||||||
|
|
||||||
// setting mime type
|
// setting mime type
|
||||||
qv := r.URL.Query()
|
qv := requestURL.Query()
|
||||||
mimeType := qv.Get("content_type")
|
mimeType := qv.Get("content_type")
|
||||||
if mimeType == "" {
|
if mimeType == "" {
|
||||||
mimeType = rawType
|
mimeType = rawType
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", mimeType)
|
w.Header().Set("Content-Type", mimeType)
|
||||||
http.ServeContent(w, r, uri, time.Unix(0, 0), reader)
|
http.ServeContent(w, r, uri, forever, reader)
|
||||||
dpaLogger.Debugf("Swarm: Serve raw content '%s' (%d bytes) as '%s'", uri, reader.Size(), mimeType)
|
dpaLogger.Debugf("Swarm: Serve raw content '%s' (%d bytes) as '%s'", uri, reader.Size(), mimeType)
|
||||||
|
|
||||||
// retrieve path via manifest
|
// retrieve path via manifest
|
||||||
|
|
@ -121,9 +123,12 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
w.Header().Set("Content-Type", mimeType)
|
w.Header().Set("Content-Type", mimeType)
|
||||||
if status > 0 {
|
if status > 0 {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
|
} else {
|
||||||
|
status = 200
|
||||||
}
|
}
|
||||||
dpaLogger.Debugf("Swarm: Served '%s' (%d bytes) as '%s' (status code: %v)", uri, reader.Size(), mimeType, w.Header())
|
dpaLogger.Debugf("Swarm: Served '%s' (%d bytes) as '%s' (status code: %v)", uri, reader.Size(), mimeType, status)
|
||||||
http.ServeContent(w, r, uri, time.Unix(0, 0), reader)
|
|
||||||
|
http.ServeContent(w, r, path, forever, reader)
|
||||||
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -24,24 +24,41 @@ type manifestEntry struct {
|
||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *manifest) getEntry(path string) (entry *manifestEntry, pos int) {
|
// path must not have any leading slashes
|
||||||
|
func (self *manifest) getEntry(path string) (matchingEntry *manifestEntry, matchlength int) {
|
||||||
|
var pos, depth, maxdepth int
|
||||||
|
var entry *manifestEntry
|
||||||
|
// path := leadingSlashes.ReplaceAllString(fullpath, "")
|
||||||
|
// iterate over entries matching paths to the target
|
||||||
|
// due to redundant slashes, it is NOT the longest match but the match with
|
||||||
|
// the highest depth is chosen
|
||||||
|
// this gives thse matches in case of trailing slashes:
|
||||||
|
// "a/" -> "a/" not "a" and "a" matches "a" not "a/" if both exist
|
||||||
|
// "a" never matches "a/" but "a/" matches a
|
||||||
for _, entry = range self.Entries {
|
for _, entry = range self.Entries {
|
||||||
entryPath := leadingSlashes.ReplaceAllString(entry.Path, "")
|
entryPath := leadingSlashes.ReplaceAllString(entry.Path, "")
|
||||||
pos = len(entryPath)
|
pos = len(entryPath)
|
||||||
if len(path) >= pos && path[:pos] == entryPath {
|
depth = len(slashes.Split(entryPath, -1))
|
||||||
var n int
|
if len(path) >= pos && path[:pos] == entryPath && (depth > maxdepth || depth == maxdepth && pos > matchlength) {
|
||||||
|
var hop int
|
||||||
|
// hop and chop leading hashes of the continuation
|
||||||
if len(path) > pos {
|
if len(path) > pos {
|
||||||
chopped := leadingSlashes.ReplaceAllString(path[pos:], "")
|
chopped := leadingSlashes.ReplaceAllString(path[pos:], "")
|
||||||
n = len(path) - pos - len(chopped)
|
hop = len(path) - pos - len(chopped)
|
||||||
}
|
}
|
||||||
if n > 0 || pos == 0 || path[pos-1] == '/' {
|
// check if pos actually ends a subpath "ab" matches on "" not on "a"
|
||||||
pos += n
|
if hop > 0 || pos == len(path) || pos == 0 || path[pos-1] == '/' {
|
||||||
dpaLogger.Debugf("Swarm: '%s' matches '%s'.", path, entry.Path)
|
matchlength = pos + hop
|
||||||
return
|
maxdepth = depth
|
||||||
|
matchingEntry = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry = nil
|
if matchingEntry != nil {
|
||||||
dpaLogger.Debugf("Path '%s' on manifest not found.", path)
|
dpaLogger.Debugf("Swarm: '%s' matches '%s'.", path, matchingEntry.Path)
|
||||||
|
} else {
|
||||||
|
dpaLogger.Debugf("Path '%s' not found on manifest ", path)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue