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
+}