mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
http: url handling improved
improved logs
This commit is contained in:
parent
9788ed5337
commit
7f50f8d54e
5 changed files with 61 additions and 47 deletions
41
bzz/api.go
41
bzz/api.go
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"regexp"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/resolver"
|
"github.com/ethereum/go-ethereum/common/resolver"
|
||||||
|
|
@ -14,6 +14,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}")
|
||||||
|
slashes = regexp.MustCompile("/+")
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Api implements webserver/file system related content storage and retrieval
|
Api implements webserver/file system related content storage and retrieval
|
||||||
on top of the dpa
|
on top of the dpa
|
||||||
|
|
@ -50,6 +55,7 @@ func (self *Api) Bzz() (p2p.Protocol, error) {
|
||||||
func (self *Api) Start(node *discover.Node, connectPeer func(string) error) {
|
func (self *Api) Start(node *discover.Node, connectPeer func(string) error) {
|
||||||
self.dpa.Start()
|
self.dpa.Start()
|
||||||
self.netStore.Start(node, connectPeer)
|
self.netStore.Start(node, connectPeer)
|
||||||
|
dpaLogger.Infof("Swarm started.")
|
||||||
go startHttpServer(self, self.port)
|
go startHttpServer(self, self.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,15 +95,20 @@ func (self *Api) resolveHost(hostport string) (contentHash Key, errR errResolve)
|
||||||
var host, port string
|
var host, port string
|
||||||
var err error
|
var err error
|
||||||
host, port, err = net.SplitHostPort(hostport)
|
host, port, err = net.SplitHostPort(hostport)
|
||||||
if err != nil && err.Error() != "missing port in address "+hostport {
|
if err != nil {
|
||||||
errR = errResolve(fmt.Errorf("invalid host '%s': %v", hostport, err))
|
if err.Error() == "missing port in address "+hostport {
|
||||||
return
|
host = hostport
|
||||||
|
} else {
|
||||||
|
errR = errResolve(fmt.Errorf("invalid host '%s': %v", hostport, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if hashMatcher.MatchString(host) {
|
if hashMatcher.MatchString(host) {
|
||||||
contentHash = Key(host)
|
contentHash = Key(common.Hex2Bytes(host))
|
||||||
|
dpaLogger.Debugf("Swarm host is a contentHash: '%064x'", contentHash)
|
||||||
} else {
|
} else {
|
||||||
if self.Resolver != nil {
|
if self.Resolver != nil {
|
||||||
hostHash := common.BytesToHash(crypto.Sha3([]byte(host)))
|
hostHash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(host)))
|
||||||
// TODO: should take port as block number versioning
|
// TODO: should take port as block number versioning
|
||||||
_ = port
|
_ = port
|
||||||
var hash common.Hash
|
var hash common.Hash
|
||||||
|
|
@ -106,6 +117,7 @@ func (self *Api) resolveHost(hostport string) (contentHash Key, errR errResolve)
|
||||||
err = errResolve(fmt.Errorf("unable to resolve '%s': %v", hostport, err))
|
err = errResolve(fmt.Errorf("unable to resolve '%s': %v", hostport, err))
|
||||||
}
|
}
|
||||||
contentHash = Key(hash.Bytes())
|
contentHash = Key(hash.Bytes())
|
||||||
|
dpaLogger.Debugf("Swarm resolve host to contentHash: '%064x'", contentHash)
|
||||||
} else {
|
} else {
|
||||||
err = errResolve(fmt.Errorf("no resolver '%s': %v", hostport, err))
|
err = errResolve(fmt.Errorf("no resolver '%s': %v", hostport, err))
|
||||||
}
|
}
|
||||||
|
|
@ -114,9 +126,12 @@ func (self *Api) resolveHost(hostport string) (contentHash Key, errR errResolve)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, status int, err error) {
|
func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, status int, err error) {
|
||||||
parts := strings.SplitAfterN(uri[1:], "/", 2)
|
parts := slashes.Split(uri, 3)
|
||||||
hostPort := parts[0]
|
hostPort := parts[1]
|
||||||
path := parts[1]
|
var path string
|
||||||
|
if len(parts) > 2 {
|
||||||
|
path = parts[2]
|
||||||
|
}
|
||||||
dpaLogger.Debugf("Swarm: host: '%s', path '%s' requested.", hostPort, path)
|
dpaLogger.Debugf("Swarm: host: '%s', path '%s' requested.", hostPort, path)
|
||||||
|
|
||||||
//resolving host and port
|
//resolving host and port
|
||||||
|
|
@ -129,6 +144,7 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
// retrieve content following path along manifests
|
// retrieve content following path along manifests
|
||||||
var pos int
|
var pos int
|
||||||
for {
|
for {
|
||||||
|
dpaLogger.Debugf("Swarm: manifest lookup key: '%064x'.", key)
|
||||||
// retrieve manifest via DPA
|
// retrieve manifest via DPA
|
||||||
manifestReader := self.dpa.Retrieve(key)
|
manifestReader := self.dpa.Retrieve(key)
|
||||||
// TODO check size for oversized manifests
|
// TODO check size for oversized manifests
|
||||||
|
|
@ -143,7 +159,7 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dpaLogger.Debugf("Swarm: Manifest for '%s' retrieved.", uri)
|
dpaLogger.Debugf("Swarm: Manifest for '%s' retrieved", uri)
|
||||||
man := manifest{}
|
man := manifest{}
|
||||||
err = json.Unmarshal(manifestData, &man)
|
err = json.Unmarshal(manifestData, &man)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -152,7 +168,7 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dpaLogger.Debugf("Swarm: Manifest for '%s' has %d entries.", uri, len(man.Entries))
|
dpaLogger.Debugf("Swarm: Manifest for '%s' has %d entries. Retrieving entry for '%s'", uri, len(man.Entries), path)
|
||||||
|
|
||||||
// retrieve entry that matches path from manifest entries
|
// retrieve entry that matches path from manifest entries
|
||||||
var entry *manifestEntry
|
var entry *manifestEntry
|
||||||
|
|
@ -172,13 +188,14 @@ func (self *Api) getPath(uri string) (reader SectionReader, mimeType string, sta
|
||||||
|
|
||||||
// get mime type of entry
|
// get mime type of entry
|
||||||
mimeType = entry.ContentType
|
mimeType = entry.ContentType
|
||||||
if mimeType != "" {
|
if mimeType == "" {
|
||||||
mimeType = manifestType
|
mimeType = manifestType
|
||||||
}
|
}
|
||||||
|
|
||||||
// if path matched on non-manifest content type, then retrieve reader
|
// if path matched on non-manifest content type, then retrieve reader
|
||||||
// and return
|
// and return
|
||||||
if mimeType != manifestType {
|
if mimeType != manifestType {
|
||||||
|
dpaLogger.Debugf("Swarm: content lookup key: '%064x' (%v)", key, mimeType)
|
||||||
reader = self.dpa.Retrieve(key)
|
reader = self.dpa.Retrieve(key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -309,13 +309,13 @@ func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
|
||||||
C: make(chan bool), // close channel to signal data delivery
|
C: make(chan bool), // close channel to signal data delivery
|
||||||
}
|
}
|
||||||
self.chunkC <- chunk // submit retrieval request, someone should be listening on the other side (or we will time out globally)
|
self.chunkC <- chunk // submit retrieval request, someone should be listening on the other side (or we will time out globally)
|
||||||
dpaLogger.Debugf("readAt %x into %d bytes at %d.", chunk.Key[:4], len(b), off)
|
dpaLogger.Debugf("readAt: reading %x into %d bytes at offset %d.", chunk.Key[:4], len(b), off)
|
||||||
|
|
||||||
// waiting for the chunk retrieval
|
// waiting for the chunk retrieval
|
||||||
select {
|
select {
|
||||||
case <-self.quitC:
|
case <-self.quitC:
|
||||||
// this is how we control process leakage (quitC is closed once join is finished (after timeout))
|
// this is how we control process leakage (quitC is closed once join is finished (after timeout))
|
||||||
dpaLogger.Debugf("quit")
|
// dpaLogger.Debugf("quit")
|
||||||
return
|
return
|
||||||
case <-chunk.C: // bells are ringing, data have been delivered
|
case <-chunk.C: // bells are ringing, data have been delivered
|
||||||
// dpaLogger.Debugf("chunk data received for %x", chunk.Key[:4])
|
// dpaLogger.Debugf("chunk data received for %x", chunk.Key[:4])
|
||||||
|
|
@ -355,9 +355,9 @@ func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
|
||||||
if off+int64(read) == self.size {
|
if off+int64(read) == self.size {
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
}
|
}
|
||||||
dpaLogger.Debugf("ReadAt returning with %d, %v", read, err)
|
dpaLogger.Debugf("ReadAt returning at %d: %v", read, err)
|
||||||
case <-self.quitC:
|
case <-self.quitC:
|
||||||
dpaLogger.Debugf("ReadAt aborted with %d, %v", read, err)
|
dpaLogger.Debugf("ReadAt aborted at %d: %v", read, err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -422,7 +422,7 @@ func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, tr
|
||||||
// this is how we control process leakage (quitC is closed once join is finished (after timeout))
|
// this is how we control process leakage (quitC is closed once join is finished (after timeout))
|
||||||
return
|
return
|
||||||
case <-ch.C: // bells are ringing, data have been delivered
|
case <-ch.C: // bells are ringing, data have been delivered
|
||||||
dpaLogger.Debugf("chunk data received")
|
// dpaLogger.Debugf("chunk data received")
|
||||||
}
|
}
|
||||||
if soff < off {
|
if soff < off {
|
||||||
soff = off
|
soff = off
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ SPLIT:
|
||||||
select {
|
select {
|
||||||
case err, ok := <-errC:
|
case err, ok := <-errC:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dpaLogger.Warnf("chunkner split error: %v", err)
|
dpaLogger.Warnf("chunker split error: %v", err)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
break SPLIT
|
break SPLIT
|
||||||
|
|
@ -108,7 +108,7 @@ func (self *DPA) Start() {
|
||||||
self.quitC = make(chan bool)
|
self.quitC = make(chan bool)
|
||||||
self.storeLoop()
|
self.storeLoop()
|
||||||
self.retrieveLoop()
|
self.retrieveLoop()
|
||||||
dpaLogger.Infof("Swarm started.")
|
dpaLogger.Debugf("Swarm DPA started.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *DPA) Stop() {
|
func (self *DPA) Stop() {
|
||||||
|
|
@ -129,6 +129,7 @@ func (self *DPA) retrieveLoop() {
|
||||||
for ch := range self.retrieveC {
|
for ch := range self.retrieveC {
|
||||||
|
|
||||||
go func(chunk *Chunk) {
|
go func(chunk *Chunk) {
|
||||||
|
dpaLogger.DebugDetailf("dpa: retrieve loop : chunk '%x'", chunk.Key)
|
||||||
storedChunk, err := self.ChunkStore.Get(chunk.Key)
|
storedChunk, err := self.ChunkStore.Get(chunk.Key)
|
||||||
if err == notFound {
|
if err == notFound {
|
||||||
dpaLogger.DebugDetailf("chunk '%x' not found", chunk.Key)
|
dpaLogger.DebugDetailf("chunk '%x' not found", chunk.Key)
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// protocolMatcher = regexp.MustCompile("^/bzz:")
|
rawUrl = regexp.MustCompile("^/+raw/*")
|
||||||
rawManifestMatcher = regexp.MustCompile("^/raw/")
|
trailingSlashes = regexp.MustCompile("/+$")
|
||||||
// rawManifestMatcher = regexp.MustCompile("^/raw/[0-9A-Fa-f]{64}(?:/[a-z]+/[-+0-9a-z]+)?$")
|
|
||||||
manifestMatcher = regexp.MustCompile("^/[0-9A-Fa-f]{64}")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type sequentialReader struct {
|
type sequentialReader struct {
|
||||||
|
|
@ -43,11 +41,17 @@ 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)
|
dpaLogger.Debugf("request URL: '%s' Host: '%s', Path: '%s'", r.RequestURI, r.URL.Host, r.URL.Path)
|
||||||
|
uri := r.URL.Path
|
||||||
|
var raw bool
|
||||||
|
path := rawUrl.ReplaceAllStringFunc(uri, func(string) string {
|
||||||
|
raw = true
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.Method == "POST":
|
case r.Method == "POST":
|
||||||
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, r.URL.Path)
|
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, r.URL.Path)
|
||||||
if r.URL.Path == "/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{
|
||||||
reader: r.Body,
|
reader: r.Body,
|
||||||
|
|
@ -61,20 +65,18 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, "No POST to "+r.URL.Path+" allowed.", http.StatusBadRequest)
|
http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case r.Method == "GET" || r.Method == "HEAD":
|
case r.Method == "GET" || r.Method == "HEAD":
|
||||||
uri := r.URL.Path
|
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, uri)
|
||||||
dpaLogger.Debugf("request URL Host: '%s', Path: '%s'", r.URL.Host, r.URL.Path)
|
path = trailingSlashes.ReplaceAllString(path, "")
|
||||||
// raw ,
|
if raw {
|
||||||
if rawManifestMatcher.MatchString(uri) {
|
|
||||||
dpaLogger.Debugf("Swarm: Raw GET request '%s' received", uri)
|
dpaLogger.Debugf("Swarm: Raw GET request '%s' received", uri)
|
||||||
|
|
||||||
// resolving host
|
// resolving host
|
||||||
name := uri[5:]
|
key, err := api.resolveHost(path)
|
||||||
key, err := api.resolveHost(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dpaLogger.Debugf("Swarm: %v", err)
|
dpaLogger.Debugf("Swarm: %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
|
@ -93,7 +95,7 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", mimeType)
|
w.Header().Set("Content-Type", mimeType)
|
||||||
http.ServeContent(w, r, name, time.Unix(0, 0), reader)
|
http.ServeContent(w, r, uri, time.Unix(0, 0), 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
|
||||||
|
|
@ -102,9 +104,8 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
dpaLogger.Debugf("Swarm: Structured GET request '%s' received.", uri)
|
dpaLogger.Debugf("Swarm: Structured GET request '%s' received.", uri)
|
||||||
|
|
||||||
// call to api.getPath on uri
|
// call to api.getPath on uri
|
||||||
reader, mimeType, status, err := api.getPath(uri[1:])
|
reader, mimeType, status, err := api.getPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var status int
|
|
||||||
if _, ok := err.(errResolve); ok {
|
if _, ok := err.(errResolve); ok {
|
||||||
dpaLogger.Debugf("Swarm: %v", err)
|
dpaLogger.Debugf("Swarm: %v", err)
|
||||||
status = http.StatusBadRequest
|
status = http.StatusBadRequest
|
||||||
|
|
@ -121,8 +122,8 @@ func handler(w http.ResponseWriter, r *http.Request, api *Api) {
|
||||||
if status > 0 {
|
if status > 0 {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
}
|
}
|
||||||
|
dpaLogger.Debugf("Swarm: Served '%s' (%d bytes) as '%s' (status code: %v)", uri, reader.Size(), mimeType, w.Header())
|
||||||
http.ServeContent(w, r, uri, time.Unix(0, 0), reader)
|
http.ServeContent(w, r, uri, time.Unix(0, 0), reader)
|
||||||
dpaLogger.Debugf("Swarm: Served '%s' (%d bytes) as '%s' (status code: %d)", uri, reader.Size(), mimeType, status)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,29 @@
|
||||||
package bzz
|
package bzz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "fmt"
|
// "fmt"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
manifestType = "application/bzz-manifest+json"
|
manifestType = "application/bzz-manifest+json"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
hashMatcher = regexp.MustCompile("^/[0-9A-Fa-f]{64}")
|
|
||||||
)
|
|
||||||
|
|
||||||
type manifest struct {
|
type manifest struct {
|
||||||
Entries []*manifestEntry `"json:entries"`
|
Entries []*manifestEntry `json:"entries"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type manifestEntry struct {
|
type manifestEntry struct {
|
||||||
Path string `"json:path"`
|
Path string `json:"path"`
|
||||||
Hash string `"json:hash"`
|
Hash string `json:"hash"`
|
||||||
ContentType string `"json:contentType"`
|
ContentType string `json:"contentType"`
|
||||||
Status int `"json:status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *manifest) getEntry(path string) (entry *manifestEntry, pos int) {
|
func (self *manifest) getEntry(path string) (entry *manifestEntry, pos int) {
|
||||||
for _, entry = range self.Entries {
|
for _, entry = range self.Entries {
|
||||||
pos = len(entry.Path)
|
pos = len(entry.Path)
|
||||||
if len(path) >= pos && path[:pos] == entry.Path {
|
if len(path) >= pos && path[:pos] == entry.Path {
|
||||||
dpaLogger.Debugf("Swarm: \"%s\" matches \"%s\".", path, entry.Path)
|
dpaLogger.Debugf("Swarm: '%s' matches '%s'.", path, entry.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue