aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/api/http/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'swarm/api/http/server.go')
-rw-r--r--swarm/api/http/server.go164
1 files changed, 70 insertions, 94 deletions
diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go
index 199c02d1d..76a57625b 100644
--- a/swarm/api/http/server.go
+++ b/swarm/api/http/server.go
@@ -38,7 +38,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/log"
@@ -518,9 +517,8 @@ func resourcePostMode(path string) (isRaw bool, frequency uint64, err error) {
// If the latter is used, a subsequent bzz:// GET call to the manifest of the resource will return
// the page that the multihash is pointing to, as if it held a normal swarm content manifest
//
-// The resource name will be verbatim what is passed as the address part of the url.
-// For example, if a POST is made to /bzz-resource:/foo.eth/raw/13 a new resource with frequency 13
-// and name "foo.eth" will be created
+// The POST request admits a JSON structure as defined in the mru package: `mru.updateRequestJSON`
+// The requests can be to a) create a resource, b) update a resource or c) both a+b: create a resource and set the initial content
func (s *Server) HandlePostResource(w http.ResponseWriter, r *Request) {
log.Debug("handle.post.resource", "ruid", r.ruid)
@@ -532,33 +530,54 @@ func (s *Server) HandlePostResource(w http.ResponseWriter, r *Request) {
defer sp.Finish()
var err error
- var addr storage.Address
- var name string
- var outdata []byte
- isRaw, frequency, err := resourcePostMode(r.uri.Path)
+
+ // Creation and update must send mru.updateRequestJSON JSON structure
+ body, err := ioutil.ReadAll(r.Body)
if err != nil {
- Respond(w, r, err.Error(), http.StatusBadRequest)
+ Respond(w, r, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ var updateRequest mru.Request
+ if err := updateRequest.UnmarshalJSON(body); err != nil { // decodes request JSON
+ Respond(w, r, err.Error(), http.StatusBadRequest) //TODO: send different status response depending on error
return
}
- // new mutable resource creation will always have a frequency field larger than 0
- if frequency > 0 {
-
- name = r.uri.Addr
+ if updateRequest.IsUpdate() {
+ // Verify that the signature is intact and that the signer is authorized
+ // to update this resource
+ // Check this early, to avoid creating a resource and then not being able to set its first update.
+ if err = updateRequest.Verify(); err != nil {
+ Respond(w, r, err.Error(), http.StatusForbidden)
+ return
+ }
+ }
- // the key is the content addressed root chunk holding mutable resource metadata information
- addr, err = s.api.ResourceCreate(ctx, name, frequency)
+ if updateRequest.IsNew() {
+ err = s.api.ResourceCreate(r.Context(), &updateRequest)
if err != nil {
code, err2 := s.translateResourceError(w, r, "resource creation fail", err)
-
Respond(w, r, err2.Error(), code)
return
}
+ }
+
+ if updateRequest.IsUpdate() {
+ _, err = s.api.ResourceUpdate(r.Context(), &updateRequest.SignedResourceUpdate)
+ if err != nil {
+ Respond(w, r, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+ // at this point both possible operations (create, update or both) were successful
+ // so in case it was a new resource, then create a manifest and send it over.
+
+ if updateRequest.IsNew() {
// we create a manifest so we can retrieve the resource with bzz:// later
// this manifest has a special "resource type" manifest, and its hash is the key of the mutable resource
- // root chunk
- m, err := s.api.NewResourceManifest(r.Context(), addr.Hex())
+ // metadata chunk (rootAddr)
+ m, err := s.api.NewResourceManifest(r.Context(), updateRequest.RootAddr().Hex())
if err != nil {
Respond(w, r, fmt.Sprintf("failed to create resource manifest: %v", err), http.StatusInternalServerError)
return
@@ -568,85 +587,21 @@ func (s *Server) HandlePostResource(w http.ResponseWriter, r *Request) {
// the client can access the root chunk key directly through its Hash member
// the manifest key should be set as content in the resolver of the ENS name
// \TODO update manifest key automatically in ENS
- outdata, err = json.Marshal(m)
+ outdata, err := json.Marshal(m)
if err != nil {
Respond(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError)
return
}
- } else {
- // to update the resource through http we need to retrieve the key for the mutable resource root chunk
- // that means that we retrieve the manifest and inspect its Hash member.
- manifestAddr := r.uri.Address()
- if manifestAddr == nil {
- manifestAddr, err = s.api.Resolve(r.Context(), r.uri)
- if err != nil {
- getFail.Inc(1)
- Respond(w, r, fmt.Sprintf("cannot resolve %s: %s", r.uri.Addr, err), http.StatusNotFound)
- return
- }
- } else {
- w.Header().Set("Cache-Control", "max-age=2147483648")
- }
-
- // get the root chunk key from the manifest
- addr, err = s.api.ResolveResourceManifest(r.Context(), manifestAddr)
- if err != nil {
- getFail.Inc(1)
- Respond(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", r.uri.Addr, err), http.StatusNotFound)
- return
- }
-
- log.Debug("handle.post.resource: resolved", "ruid", r.ruid, "manifestkey", manifestAddr, "rootchunkkey", addr)
-
- name, _, err = s.api.ResourceLookup(ctx, addr, 0, 0, &mru.LookupParams{})
- if err != nil {
- Respond(w, r, err.Error(), http.StatusNotFound)
- return
- }
- }
-
- // Creation and update must send data aswell. This data constitutes the update data itself.
- data, err := ioutil.ReadAll(r.Body)
- if err != nil {
- Respond(w, r, err.Error(), http.StatusInternalServerError)
- return
- }
-
- // Multihash will be passed as hex-encoded data, so we need to parse this to bytes
- if isRaw {
- _, _, _, err = s.api.ResourceUpdate(ctx, name, data)
- if err != nil {
- Respond(w, r, err.Error(), http.StatusBadRequest)
- return
- }
- } else {
- bytesdata, err := hexutil.Decode(string(data))
- if err != nil {
- Respond(w, r, err.Error(), http.StatusBadRequest)
- return
- }
- _, _, _, err = s.api.ResourceUpdateMultihash(ctx, name, bytesdata)
- if err != nil {
- Respond(w, r, err.Error(), http.StatusBadRequest)
- return
- }
- }
-
- // If we have data to return, write this now
- // \TODO there should always be data to return here
- if len(outdata) > 0 {
- w.Header().Add("Content-type", "text/plain")
- w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(outdata))
- return
}
- w.WriteHeader(http.StatusOK)
+ w.Header().Add("Content-type", "application/json")
}
// Retrieve mutable resource updates:
// bzz-resource://<id> - get latest update
// bzz-resource://<id>/<n> - get latest update on period n
// bzz-resource://<id>/<n>/<m> - get update version m of period n
+// bzz-resource://<id>/meta - get metadata and next version information
// <id> = ens name or hash
// TODO: Enable pass maxPeriod parameter
func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
@@ -666,31 +621,51 @@ func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
w.Header().Set("Cache-Control", "max-age=2147483648")
}
- // get the root chunk key from the manifest
- key, err := s.api.ResolveResourceManifest(r.Context(), manifestAddr)
+ // get the root chunk rootAddr from the manifest
+ rootAddr, err := s.api.ResolveResourceManifest(r.Context(), manifestAddr)
if err != nil {
getFail.Inc(1)
Respond(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", r.uri.Addr, err), http.StatusNotFound)
return
}
- log.Debug("handle.get.resource: resolved", "ruid", r.ruid, "manifestkey", manifestAddr, "rootchunk key", key)
+ log.Debug("handle.get.resource: resolved", "ruid", r.ruid, "manifestkey", manifestAddr, "rootchunk addr", rootAddr)
- // determine if the query specifies period and version
+ // determine if the query specifies period and version or it is a metadata query
var params []string
if len(r.uri.Path) > 0 {
+ if r.uri.Path == "meta" {
+ unsignedUpdateRequest, err := s.api.ResourceNewRequest(r.Context(), rootAddr)
+ if err != nil {
+ getFail.Inc(1)
+ Respond(w, r, fmt.Sprintf("cannot retrieve resource metadata for rootAddr=%s: %s", rootAddr.Hex(), err), http.StatusNotFound)
+ return
+ }
+ rawResponse, err := unsignedUpdateRequest.MarshalJSON()
+ if err != nil {
+ Respond(w, r, fmt.Sprintf("cannot encode unsigned UpdateRequest: %v", err), http.StatusInternalServerError)
+ return
+ }
+ w.Header().Add("Content-type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprint(w, string(rawResponse))
+ return
+
+ }
+
params = strings.Split(r.uri.Path, "/")
+
}
var name string
- var period uint64
- var version uint64
var data []byte
now := time.Now()
switch len(params) {
case 0: // latest only
- name, data, err = s.api.ResourceLookup(r.Context(), key, 0, 0, nil)
+ name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatest(rootAddr))
case 2: // specific period and version
+ var version uint64
+ var period uint64
version, err = strconv.ParseUint(params[1], 10, 32)
if err != nil {
break
@@ -699,13 +674,14 @@ func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
if err != nil {
break
}
- name, data, err = s.api.ResourceLookup(r.Context(), key, uint32(period), uint32(version), nil)
+ name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupVersion(rootAddr, uint32(period), uint32(version)))
case 1: // last version of specific period
+ var period uint64
period, err = strconv.ParseUint(params[0], 10, 32)
if err != nil {
break
}
- name, data, err = s.api.ResourceLookup(r.Context(), key, uint32(period), uint32(version), nil)
+ name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatestVersionInPeriod(rootAddr, uint32(period)))
default: // bogus
err = mru.NewError(storage.ErrInvalidValue, "invalid mutable resource request")
}