diff --git a/bzz/httpaccess.go b/bzz/httpaccess.go index 824667596f..35e84f29d6 100644 --- a/bzz/httpaccess.go +++ b/bzz/httpaccess.go @@ -4,6 +4,7 @@ A simple http server interface to Swarm package bzz import ( + "encoding/json" "fmt" "github.com/ethereum/go-ethereum/ethutil" "io" @@ -17,7 +18,9 @@ const ( ) var ( - uriMatcher = regexp.MustCompile("^/raw/[0-9A-Fa-f]{64}$") + uriMatcher = regexp.MustCompile("^/raw/[0-9A-Fa-f]{64}$") + manifestMatcher = regexp.MustCompile("^/[0-9A-Fa-f]{64}") + hashMatcher = regexp.MustCompile("^[0-9A-Fa-f]{64}$") ) type sequentialReader struct { @@ -26,6 +29,13 @@ type sequentialReader struct { ahead map[int64](chan bool) } +type manifestEntry struct { + Path string + Hash string + Content_type string + Status int16 +} + func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) { if self.pos != off { dpaLogger.Debugf("Swarm: deferred read in POST at position %d, offset %d.", @@ -80,9 +90,68 @@ func handler(w http.ResponseWriter, r *http.Request, dpa *DPA) { } case r.Method == "GET": if uriMatcher.MatchString(uri) { + dpaLogger.Debugf("Swarm: Raw GET request %s received", uri) name := uri[5:] key := ethutil.Hex2Bytes(name) http.ServeContent(w, r, name+".bin", time.Unix(0, 0), dpa.Retrieve(key)) + dpaLogger.Debugf("Swarm: Object %s returned.", name) + } else if manifestMatcher.MatchString(uri) { + dpaLogger.Debugf("Swarm: Structured GET request %s received.", uri) + name := uri[1:65] + path := uri[65:] // typically begins with a / + key := ethutil.Hex2Bytes(name) + manifestReader := dpa.Retrieve(key) + // TODO check size for oversized manifests + manifest := make([]byte, manifestReader.Size()) + n, err := manifestReader.Read(manifest) + if err != nil { + dpaLogger.Debugf("Swarm: Manifest %s not found.", name) + http.Error(w, err.Error(), http.StatusNotFound) + return + } + dpaLogger.Debugf("Swarm: Manifest %s retrieved.") + manifestEntries := make([]manifestEntry, 0) + err = json.Unmarshal(manifest, &manifestEntries) + if err != nil { + dpaLogger.Debugf("Swarm: Manifest %s is malformed.", name) + http.Error(w, err.Error(), http.StatusNotFound) + return + } else { + dpaLogger.Debugf("Swarm: Manifest %s has %d entries.", name, len(manifestEntries)) + } + var mimeType string + key = nil + prefix := 0 + status := int16(404) + for i, entry := range manifestEntries { + if !hashMatcher.MatchString(entry.Hash) { + // hash is mandatory + break + } + if entry.Content_type == "" { + // content type defaults to manifest + entry.Content_type = "application/bzz-manifest+json" + } + if entry.Status == 0 { + // status defaults to 200 + entry.Status = 200 + } + pathLen := len(entry.Path) + if len(path) >= pathLen && path[:pathLen] == entry.Path && prefix < pathLen { + prefix = pathLen + key = ethutil.Hex2Bytes(entry.Hash) + mimeType = entry.Content_type + status = entry.Status + } + } + if key == nil { + http.Error(w, "Object "+uri+" not found.", http.StatusNotFound) + } else { + w.Header().Set("Content-Type", mimeType) + w.WriteHeader(int(status)) + http.ServeContent(w, r, "", time.Unix(0, 0), dpa.Retrieve(key)) + dpaLogger.Debugf("Swarm: Served %s as %s.", mimeType, uri) + } } else { http.Error(w, "Object "+uri+" not found.", http.StatusNotFound) } diff --git a/eth/backend.go b/eth/backend.go index 367ca7ec68..0e51f3a277 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -148,7 +148,7 @@ func New(config *Config) (*Ethereum, error) { ChunkStore: netStore, } dpa.Start() - bzz.StartHttpServer(dpa) + go bzz.StartHttpServer(dpa) nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway) if err != nil {