@cryptotaxi247 / kubo / commits / d221d55d8

HTTP: add handlers to allow object creation and modification

Mildred Ki'Lya committed Jan 21, 2015 at 15:51 UTC d221d55d85629512711e7ffbe248c5d56f247d39
6 files changed +280 -28
core/corehttp/gateway_handler.go
+198 -8
@@ -1,6 +1,7 @@
1 package corehttp
2
3 import (
4 + "fmt"
5 "html/template"
6 "io"
7 "net/http"
@@ -17,6 +18,7 @@ import (
18 dag "github.com/jbenet/go-ipfs/merkledag"
19 path "github.com/jbenet/go-ipfs/path"
20 "github.com/jbenet/go-ipfs/routing"
21 + ufs "github.com/jbenet/go-ipfs/unixfs"
22 uio "github.com/jbenet/go-ipfs/unixfs/io"
23 u "github.com/jbenet/go-ipfs/util"
24 )
@@ -101,6 +103,10 @@ func (i *gatewayHandler) NewDagFromReader(r io.Reader) (*dag.Node, error) {
103 r, i.node.DAG, i.node.Pinning.GetManual(), chunk.DefaultSplitter)
104 }
105
106 +func NewDagEmptyDir() *dag.Node {
107 + return &dag.Node{Data: ufs.FolderPBData()}
108 +}
109 +
110 func (i *gatewayHandler) AddNodeToDAG(nd *dag.Node) (u.Key, error) {
111 return i.node.DAG.Add(nd)
112 }
@@ -110,6 +116,33 @@ func (i *gatewayHandler) NewDagReader(nd *dag.Node) (uio.ReadSeekCloser, error)
116 }
117
118 func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
119 + if r.Method == "POST" {
120 + i.postHandler(w, r)
121 + return
122 + }
123 +
124 + if r.Method == "PUT" {
125 + i.putHandler(w, r)
126 + return
127 + }
128 +
129 + if r.Method == "DELETE" {
130 + i.deleteHandler(w, r)
131 + return
132 + }
133 +
134 + if r.Method == "GET" {
135 + i.getHandler(w, r)
136 + return
137 + }
138 +
139 + errmsg := "Method " + r.Method + " not allowed: " + "bad request for " + r.URL.Path
140 + w.WriteHeader(http.StatusBadRequest)
141 + w.Write([]byte(errmsg))
142 + log.Error(errmsg)
143 +}
144 +
145 +func (i *gatewayHandler) getHandler(w http.ResponseWriter, r *http.Request) {
146 ctx, cancel := context.WithCancel(i.node.Context())
147 defer cancel()
148
@@ -209,23 +242,180 @@ func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
242 func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) {
243 nd, err := i.NewDagFromReader(r.Body)
244 if err != nil {
212 - w.WriteHeader(http.StatusInternalServerError)
213 - log.Error(err)
214 - w.Write([]byte(err.Error()))
245 + internalWebError(w, err)
246 return
247 }
248
249 k, err := i.AddNodeToDAG(nd)
250 if err != nil {
220 - w.WriteHeader(http.StatusInternalServerError)
221 - log.Error(err)
251 + internalWebError(w, err)
252 + return
253 + }
254 +
255 + h := mh.Multihash(k).B58String()
256 + w.Header().Set("IPFS-Hash", h)
257 + http.Redirect(w, r, IpfsPathPrefix+h, http.StatusCreated)
258 +}
259 +
260 +func (i *gatewayHandler) putEmptyDirHandler(w http.ResponseWriter, r *http.Request) {
261 + newnode := NewDagEmptyDir()
262 +
263 + key, err := i.node.DAG.Add(newnode)
264 + if err != nil {
265 + webError(w, "Could not recursively add new node", err, http.StatusInternalServerError)
266 + return
267 + }
268 +
269 + w.Header().Set("IPFS-Hash", key.String())
270 + http.Redirect(w, r, IpfsPathPrefix+key.String()+"/", http.StatusCreated)
271 +}
272 +
273 +func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {
274 + urlPath := r.URL.Path
275 + pathext := urlPath[5:]
276 + var err error
277 + if urlPath == IpfsPathPrefix + "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn/" {
278 + i.putEmptyDirHandler(w, r)
279 + return
280 + }
281 +
282 + var newnode *dag.Node
283 + if pathext[len(pathext)-1] == '/' {
284 + newnode = NewDagEmptyDir()
285 + } else {
286 + newnode, err = i.NewDagFromReader(r.Body)
287 + if err != nil {
288 + webError(w, "Could not create DAG from request", err, http.StatusInternalServerError)
289 + return
290 + }
291 + }
292 +
293 + h, components, err := path.SplitAbsPath(path.Path(urlPath))
294 + if err != nil {
295 + webError(w, "Could not split path", err, http.StatusInternalServerError)
296 + return
297 + }
298 +
299 + if len(components) < 1 {
300 + err = fmt.Errorf("Cannot override existing object")
301 + w.WriteHeader(http.StatusBadRequest)
302 w.Write([]byte(err.Error()))
303 + log.Error("%s", err)
304 + return
305 + }
306 +
307 + rootnd, err := i.node.Resolver.DAG.Get(u.Key(h))
308 + if err != nil {
309 + webError(w, "Could not resolve root object", err, http.StatusBadRequest)
310 + return
311 + }
312 +
313 + // resolving path components into merkledag nodes. if a component does not
314 + // resolve, create empty directories (which will be linked and populated below.)
315 + path_nodes, err := i.node.Resolver.ResolveLinks(rootnd, components[:len(components)-1])
316 + if _, ok := err.(path.ErrNoLink); ok {
317 + // Create empty directories, links will be made further down the code
318 + for len(path_nodes) < len(components) {
319 + path_nodes = append(path_nodes, NewDagEmptyDir())
320 + }
321 + } else if err != nil {
322 + webError(w, "Could not resolve parent object", err, http.StatusBadRequest)
323 + return
324 + }
325 +
326 + for i := len(path_nodes) - 1; i >= 0; i-- {
327 + newnode, err = path_nodes[i].UpdateNodeLink(components[i], newnode)
328 + if err != nil {
329 + webError(w, "Could not update node links", err, http.StatusInternalServerError)
330 + return
331 + }
332 + }
333 +
334 + err = i.node.DAG.AddRecursive(newnode)
335 + if err != nil {
336 + webError(w, "Could not add recursively new node", err, http.StatusInternalServerError)
337 + return
338 + }
339 +
340 + // Redirect to new path
341 + key, err := newnode.Key()
342 + if err != nil {
343 + webError(w, "Could not get key of new node", err, http.StatusInternalServerError)
344 + return
345 + }
346 +
347 + w.Header().Set("IPFS-Hash", key.String())
348 + http.Redirect(w, r, IpfsPathPrefix+key.String()+"/"+strings.Join(components, "/"), http.StatusCreated)
349 +}
350 +
351 +func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
352 + urlPath := r.URL.Path
353 + h, components, err := path.SplitAbsPath(path.Path(urlPath))
354 + if err != nil {
355 + webError(w, "Could not split path", err, http.StatusInternalServerError)
356 + return
357 + }
358 +
359 + rootnd, err := i.node.Resolver.DAG.Get(u.Key(h))
360 + if err != nil {
361 + webError(w, "Could not resolve root object", err, http.StatusBadRequest)
362 return
363 }
364
226 - //TODO: return json representation of list instead
227 - w.WriteHeader(http.StatusCreated)
228 - w.Write([]byte(mh.Multihash(k).B58String()))
365 + path_nodes, err := i.node.Resolver.ResolveLinks(rootnd, components[:len(components)-1])
366 + if err != nil {
367 + webError(w, "Could not resolve parent object", err, http.StatusBadRequest)
368 + return
369 + }
370 +
371 + err = path_nodes[len(path_nodes)-1].RemoveNodeLink(components[len(components)-1])
372 + if err != nil {
373 + webError(w, "Could not delete link", err, http.StatusBadRequest)
374 + return
375 + }
376 +
377 + newnode := path_nodes[len(path_nodes)-1]
378 + for i := len(path_nodes) - 2; i >= 0; i-- {
379 + newnode, err = path_nodes[i].UpdateNodeLink(components[i], newnode)
380 + if err != nil {
381 + webError(w, "Could not update node links", err, http.StatusInternalServerError)
382 + return
383 + }
384 + }
385 +
386 + err = i.node.DAG.AddRecursive(newnode)
387 + if err != nil {
388 + webError(w, "Could not add recursively new node", err, http.StatusInternalServerError)
389 + return
390 + }
391 +
392 + // Redirect to new path
393 + key, err := newnode.Key()
394 + if err != nil {
395 + webError(w, "Could not get key of new node", err, http.StatusInternalServerError)
396 + return
397 + }
398 +
399 + w.Header().Set("IPFS-Hash", key.String())
400 + http.Redirect(w, r, IpfsPathPrefix+key.String()+"/"+strings.Join(components[:len(components)-1], "/"), http.StatusCreated)
401 +}
402 +
403 +func webError(w http.ResponseWriter, message string, err error, defaultCode int) {
404 + if _, ok := err.(path.ErrNoLink); ok {
405 + webErrorWithCode(w, message, err, http.StatusNotFound)
406 + } else if err == routing.ErrNotFound {
407 + webErrorWithCode(w, message, err, http.StatusNotFound)
408 + } else if err == context.DeadlineExceeded {
409 + webErrorWithCode(w, message, err, http.StatusRequestTimeout)
410 + } else {
411 + webErrorWithCode(w, message, err, defaultCode)
412 + }
413 +}
414 +
415 +func webErrorWithCode(w http.ResponseWriter, message string, err error, code int) {
416 + w.WriteHeader(code)
417 + log.Errorf("%s: %s", message, err)
418 + w.Write([]byte(message + ": " + err.Error()))
419 }
420
421 // return a 500 error and log
fuse/ipns/ipns_unix.go
+4 -5
@@ -292,13 +292,13 @@ func (s *Node) Attr() fuse.Attr {
292 // Lookup performs a lookup under this node.
293 func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
294 log.Debugf("ipns: node[%s] Lookup '%s'", s.name, name)
295 - nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
295 + nodes, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
296 if err != nil {
297 // todo: make this error more versatile.
298 return nil, fuse.ENOENT
299 }
300
301 - return s.makeChild(name, nd), nil
301 + return s.makeChild(name, nodes[len(nodes)-1]), nil
302 }
303
304 func (n *Node) makeChild(name string, node *mdag.Node) *Node {
@@ -650,12 +650,11 @@ func (n *Node) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fus
650 // Updates the child of this node, specified by name to the given newnode
651 func (n *Node) update(name string, newnode *mdag.Node) error {
652 log.Debugf("update '%s' in '%s'", name, n.name)
653 - nnode := n.Nd.Copy()
654 - err := nnode.RemoveNodeLink(name)
653 +
654 + nnode, err := n.Nd.UpdateNodeLink(name, newnode)
655 if err != nil {
656 return err
657 }
658 - nnode.AddNodeLink(name, newnode)
658
659 if n.parent != nil {
660 err := n.parent.update(n.name, nnode)
fuse/readonly/readonly_unix.go
+2 -2
@@ -118,13 +118,13 @@ func (s *Node) Attr() fuse.Attr {
118 // Lookup performs a lookup under this node.
119 func (s *Node) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
120 log.Debugf("Lookup '%s'", name)
121 - nd, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
121 + nodes, err := s.Ipfs.Resolver.ResolveLinks(s.Nd, []string{name})
122 if err != nil {
123 // todo: make this error more versatile.
124 return nil, fuse.ENOENT
125 }
126
127 - return &Node{Ipfs: s.Ipfs, Nd: nd}, nil
127 + return &Node{Ipfs: s.Ipfs, Nd: nodes[len(nodes)-1]}, nil
128 }
129
130 // ReadDir reads the link structure as directory entries
merkledag/node.go
+10
@@ -134,6 +134,16 @@ func (n *Node) Copy() *Node {
134 return nnode
135 }
136
137 +// UpdateNodeLink return a copy of the node with the link name set to point to
138 +// that. If a link of the same name existed, it is removed.
139 +func (n *Node) UpdateNodeLink(name string, that *Node) (*Node, error) {
140 + newnode := n.Copy()
141 + err := newnode.RemoveNodeLink(name)
142 + err = nil // ignore error
143 + err = newnode.AddNodeLink(name, that)
144 + return newnode, err
145 +}
146 +
147 // Size returns the total size of the data addressed by node,
148 // including the total sizes of references.
149 func (n *Node) Size() (uint64, error) {
path/resolver.go
+50 -13
@@ -11,16 +11,26 @@ import (
11
12 var log = u.Logger("path")
13
14 +// ErrNoLink is returned when a link is not found in a path
15 +type ErrNoLink struct {
16 + name string
17 + node mh.Multihash
18 +}
19 +
20 +func (e ErrNoLink) Error() string {
21 + return fmt.Sprintf("no link named %q under %s", e.name, e.node.B58String())
22 +}
23 +
24 // Resolver provides path resolution to IPFS
25 // It has a pointer to a DAGService, which is uses to resolve nodes.
26 type Resolver struct {
27 DAG merkledag.DAGService
28 }
29
20 -// ResolvePath fetches the node for given path. It uses the first
21 -// path component as a hash (key) of the first node, then resolves
22 -// all other components walking the links, with ResolveLinks.
23 -func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) {
30 +// SplitAbsPath clean up and split fpath. It extracts the first component (which
31 +// must be a Multihash) and return it separately.
32 +func SplitAbsPath(fpath Path) (mh.Multihash, []string, error) {
33 +
34 log.Debugf("Resolve: '%s'", fpath)
35
36 parts := fpath.Segments()
@@ -30,13 +40,36 @@ func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) {
40
41 // if nothing, bail.
42 if len(parts) == 0 {
33 - return nil, fmt.Errorf("ipfs path must contain at least one component")
43 + return nil, nil, fmt.Errorf("ipfs path must contain at least one component")
44 }
45
46 // first element in the path is a b58 hash (for now)
47 h, err := mh.FromB58String(parts[0])
48 if err != nil {
49 log.Debug("given path element is not a base58 string.\n")
50 + return nil, nil, err
51 + }
52 +
53 + return h, parts[1:], nil
54 +}
55 +
56 +// ResolvePath fetches the node for given path. It returns the last item
57 +// returned by ResolvePathComponents.
58 +func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) {
59 + nodes, err := s.ResolvePathComponents(fpath)
60 + if err != nil || nodes == nil {
61 + return nil, err
62 + } else {
63 + return nodes[len(nodes)-1], err
64 + }
65 +}
66 +
67 +// ResolvePathComponents fetches the nodes for each segment of the given path.
68 +// It uses the first path component as a hash (key) of the first node, then
69 +// resolves all other components walking the links, with ResolveLinks.
70 +func (s *Resolver) ResolvePathComponents(fpath Path) ([]*merkledag.Node, error) {
71 + h, parts, err := SplitAbsPath(fpath)
72 + if err != nil {
73 return nil, err
74 }
75
@@ -46,19 +79,22 @@ func (s *Resolver) ResolvePath(fpath Path) (*merkledag.Node, error) {
79 return nil, err
80 }
81
49 - return s.ResolveLinks(nd, parts[1:])
82 + return s.ResolveLinks(nd, parts)
83 }
84
85 // ResolveLinks iteratively resolves names by walking the link hierarchy.
86 // Every node is fetched from the DAGService, resolving the next name.
54 -// Returns the last node found.
87 +// Returns the list of nodes forming the path, starting with ndd. This list is
88 +// guaranteed never to be empty.
89 //
90 // ResolveLinks(nd, []string{"foo", "bar", "baz"})
91 // would retrieve "baz" in ("bar" in ("foo" in nd.Links).Links).Links
92 func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) (
59 - nd *merkledag.Node, err error) {
93 + result []*merkledag.Node, err error) {
94
61 - nd = ndd // dup arg workaround
95 + result = make([]*merkledag.Node, 0, len(names)+1)
96 + result = append(result, ndd)
97 + nd := ndd // dup arg workaround
98
99 // for each of the path components
100 for _, name := range names {
@@ -75,21 +111,22 @@ func (s *Resolver) ResolveLinks(ndd *merkledag.Node, names []string) (
111 }
112
113 if next == "" {
78 - h1, _ := nd.Multihash()
79 - h2 := h1.B58String()
80 - return nil, fmt.Errorf("no link named %q under %s", name, h2)
114 + n, _ := nd.Multihash()
115 + return result, ErrNoLink{name: name, node: n}
116 }
117
118 if nlink.Node == nil {
119 // fetch object for link and assign to nd
120 nd, err = s.DAG.Get(next)
121 if err != nil {
87 - return nd, err
122 + return append(result, nd), err
123 }
124 nlink.Node = nd
125 } else {
126 nd = nlink.Node
127 }
128 +
129 + result = append(result, nlink.Node)
130 }
131 return
132 }
util/util.go
+16
@@ -144,3 +144,19 @@ func (m MultiErr) Error() string {
144 }
145 return s
146 }
147 +
148 +func Partition(subject string, sep string) (string, string, string) {
149 + if i := strings.Index(subject, sep); i != -1 {
150 + return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):]
151 + } else {
152 + return subject, "", ""
153 + }
154 +}
155 +
156 +func RPartition(subject string, sep string) (string, string, string) {
157 + if i := strings.LastIndex(subject, sep); i != -1 {
158 + return subject[:i], subject[i : i+len(sep)], subject[i+len(sep):]
159 + } else {
160 + return subject, "", ""
161 + }
162 +}