From b8b4e4566517e6c4647e25b1cf0bcbc196ff7ddb Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 22 Oct 2015 23:32:57 +0200 Subject: [PATCH] fixing putHandler for --writable http gateway I disabled this a long time ago and never refactored it. About time. License: MIT Signed-off-by: Henry --- core/corehttp/gateway_handler.go | 136 +++++++----------- path/resolver.go | 8 +- ...writable.sh => t0111-gateway-writeable.sh} | 9 +- 3 files changed, 61 insertions(+), 92 deletions(-) rename test/sharness/{x0111-gateway-writable.sh => t0111-gateway-writeable.sh} (92%) diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index aa8dc32d3..b95c7482f 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -6,6 +6,7 @@ import ( "io" "net/http" gopath "path" + "path/filepath" "strings" "time" @@ -17,6 +18,7 @@ import ( "github.com/ipfs/go-ipfs/importer" chunk "github.com/ipfs/go-ipfs/importer/chunk" dag "github.com/ipfs/go-ipfs/merkledag" + dagutils "github.com/ipfs/go-ipfs/merkledag/utils" path "github.com/ipfs/go-ipfs/path" "github.com/ipfs/go-ipfs/routing" uio "github.com/ipfs/go-ipfs/unixfs/io" @@ -273,116 +275,82 @@ func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, ipfsPathPrefix+k.String(), http.StatusCreated) } -func (i *gatewayHandler) putEmptyDirHandler(w http.ResponseWriter, r *http.Request) { - newnode := uio.NewEmptyDirectory() - - key, err := i.node.DAG.Add(newnode) +func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { + rootPath, err := path.ParsePath(r.URL.Path) if err != nil { - webError(w, "Could not recursively add new node", err, http.StatusInternalServerError) + webError(w, "putHandler: ipfs path not valid", err, http.StatusBadRequest) return } - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", key.String()) - http.Redirect(w, r, ipfsPathPrefix+key.String()+"/", http.StatusCreated) -} - -func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { - // TODO(cryptix): either ask mildred about the flow of this or rewrite it - webErrorWithCode(w, "Sorry, PUT is bugged right now, closing request", errors.New("handler disabled"), http.StatusInternalServerError) - return - urlPath := r.URL.Path - pathext := urlPath[5:] - var err error - if urlPath == ipfsPathPrefix+"QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn/" { - i.putEmptyDirHandler(w, r) + rsegs := rootPath.Segments() + if rsegs[0] == ipnsPathPrefix { + webError(w, "putHandler: updating named entries not supported", errors.New("WritableGateway: ipns put not supported"), http.StatusBadRequest) return } var newnode *dag.Node - if pathext[len(pathext)-1] == '/' { + if rsegs[len(rsegs)-1] == "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" { newnode = uio.NewEmptyDirectory() } else { - newnode, err = i.newDagFromReader(r.Body) + putNode, err := i.newDagFromReader(r.Body) if err != nil { - webError(w, "Could not create DAG from request", err, http.StatusInternalServerError) + webError(w, "putHandler: Could not create DAG from request", err, http.StatusInternalServerError) return } + newnode = putNode } - ctx, cancel := context.WithCancel(i.node.Context()) - defer cancel() - - ipfsNode, err := core.Resolve(ctx, i.node, path.Path(urlPath)) - if err != nil { - // FIXME HTTP error code - webError(w, "Could not resolve name", err, http.StatusInternalServerError) - return + var newPath string + if len(rsegs) > 1 { + newPath = strings.Join(rsegs[2:], "/") } - k, err := ipfsNode.Key() - if err != nil { - webError(w, "Could not get key from resolved node", err, http.StatusInternalServerError) - return - } - - h, components, err := path.SplitAbsPath(path.FromKey(k)) - if err != nil { - webError(w, "Could not split path", err, http.StatusInternalServerError) - return - } - - if len(components) < 1 { - err = fmt.Errorf("Cannot override existing object") - webError(w, "http gateway", err, http.StatusBadRequest) - return - } - - tctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - // TODO(cryptix): could this be core.Resolve() too? - rootnd, err := i.node.Resolver.DAG.Get(tctx, key.Key(h)) - if err != nil { - webError(w, "Could not resolve root object", err, http.StatusBadRequest) - return - } - - // resolving path components into merkledag nodes. if a component does not - // resolve, create empty directories (which will be linked and populated below.) - pathNodes, err := i.node.Resolver.ResolveLinks(tctx, rootnd, components[:len(components)-1]) - if _, ok := err.(path.ErrNoLink); ok { - // Create empty directories, links will be made further down the code - for len(pathNodes) < len(components) { - pathNodes = append(pathNodes, uio.NewDirectory(i.node.DAG).GetNode()) - } - } else if err != nil { - webError(w, "Could not resolve parent object", err, http.StatusBadRequest) - return - } - - for i := len(pathNodes) - 1; i >= 0; i-- { - newnode, err = pathNodes[i].UpdateNodeLink(components[i], newnode) + var newkey key.Key + rnode, err := core.Resolve(i.node.Context(), i.node, rootPath) + switch ev := err.(type) { + case path.ErrNoLink: + // ev.Node < node where resolve failed + // ev.Name < new link + // but we need to patch from the root + rnode, err := i.node.DAG.Get(i.node.Context(), key.B58KeyDecode(rsegs[1])) if err != nil { - webError(w, "Could not update node links", err, http.StatusInternalServerError) + webError(w, "putHandler: Could not create DAG from request", err, http.StatusInternalServerError) return } - } - if err := i.node.DAG.AddRecursive(newnode); err != nil { - webError(w, "Could not add recursively new node", err, http.StatusInternalServerError) - return - } + e := dagutils.NewDagEditor(i.node.DAG, rnode) + err = e.InsertNodeAtPath(i.node.Context(), newPath, newnode, uio.NewEmptyDirectory) + if err != nil { + webError(w, "putHandler: InsertNodeAtPath failed", err, http.StatusInternalServerError) + return + } - // Redirect to new path - key, err := newnode.Key() - if err != nil { - webError(w, "Could not get key of new node", err, http.StatusInternalServerError) + newkey, err = e.GetNode().Key() + if err != nil { + webError(w, "putHandler: could not get key of edited node", err, http.StatusInternalServerError) + return + } + + case nil: + // object set-data case + rnode.Data = newnode.Data + + newkey, err = i.node.DAG.Add(rnode) + if err != nil { + nnk, _ := newnode.Key() + rk, _ := rnode.Key() + webError(w, fmt.Sprintf("putHandler: Could not add newnode(%q) to root(%q)", nnk.B58String(), rk.B58String()), err, http.StatusInternalServerError) + return + } + default: + log.Warningf("putHandler: unhandled resolve error %T", ev) + webError(w, "could not resolve root DAG", ev, http.StatusInternalServerError) return } i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", key.String()) - http.Redirect(w, r, ipfsPathPrefix+key.String()+"/"+strings.Join(components, "/"), http.StatusCreated) + w.Header().Set("IPFS-Hash", newkey.String()) + http.Redirect(w, r, filepath.Join(ipfsPathPrefix, newkey.String(), newPath), http.StatusCreated) } func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { diff --git a/path/resolver.go b/path/resolver.go index 4f4db6dce..6390bdeca 100644 --- a/path/resolver.go +++ b/path/resolver.go @@ -22,12 +22,12 @@ var ErrNoComponents = errors.New( // ErrNoLink is returned when a link is not found in a path type ErrNoLink struct { - name string - node mh.Multihash + Name string + Node mh.Multihash } func (e ErrNoLink) Error() string { - return fmt.Sprintf("no link named %q under %s", e.name, e.node.B58String()) + return fmt.Sprintf("no link named %q under %s", e.Name, e.Node.B58String()) } // Resolver provides path resolution to IPFS @@ -124,7 +124,7 @@ func (s *Resolver) ResolveLinks(ctx context.Context, ndd *merkledag.Node, names if next == "" { n, _ := nd.Multihash() - return result, ErrNoLink{name: name, node: n} + return result, ErrNoLink{Name: name, Node: n} } if nlink.Node == nil { diff --git a/test/sharness/x0111-gateway-writable.sh b/test/sharness/t0111-gateway-writeable.sh similarity index 92% rename from test/sharness/x0111-gateway-writable.sh rename to test/sharness/t0111-gateway-writeable.sh index d20bd7d08..13c9db7c1 100755 --- a/test/sharness/x0111-gateway-writable.sh +++ b/test/sharness/t0111-gateway-writeable.sh @@ -14,9 +14,10 @@ test_launch_ipfs_daemon port=$PORT_GWAY -test_expect_success "ipfs daemon listening to TCP port $port" ' - test_wait_open_tcp_port_10_sec "$PORT_GWAY" -' +# TODO(cryptix): netstat not avail on testing system? +#test_expect_success "ipfs daemon listening to TCP port $port" ' +# test_wait_open_tcp_port_10_sec "$PORT_GWAY" +#' test_expect_success "HTTP gateway gives access to sample file" ' curl -s -o welcome "http://localhost:$PORT_GWAY/ipfs/$HASH_WELCOME_DOCS/readme" && @@ -44,7 +45,7 @@ test_expect_success "HTTP PUT empty directory" ' curl -svX PUT "$URL" 2>curl_putEmpty.out && cat curl_putEmpty.out && grep "Ipfs-Hash: $HASH_EMPTY_DIR" curl_putEmpty.out && - grep "Location: /ipfs/$HASH_EMPTY_DIR/" curl_putEmpty.out && + grep "Location: /ipfs/$HASH_EMPTY_DIR" curl_putEmpty.out && grep "HTTP/1.1 201 Created" curl_putEmpty.out '