@@ -2,7 +2,6 @@ package corehttp
2
2
3
3
import (
4
4
"context"
5
- "errors"
6
5
"fmt"
7
6
"io"
8
7
"net/http"
@@ -12,20 +11,13 @@ import (
12
11
"strings"
13
12
"time"
14
13
15
- "github.com/ipfs/go-ipfs/core"
16
- "github.com/ipfs/go-ipfs/dagutils"
17
- "github.com/ipfs/go-ipfs/namesys/resolve"
18
-
19
14
"github.com/dustin/go-humanize"
20
15
"github.com/ipfs/go-cid"
21
- chunker "github.com/ipfs/go-ipfs-chunker"
22
16
files "github.com/ipfs/go-ipfs-files"
23
- ipld "github.com/ipfs/go-ipld-format"
24
17
dag "github.com/ipfs/go-merkledag"
18
+ "github.com/ipfs/go-mfs"
25
19
"github.com/ipfs/go-path"
26
20
"github.com/ipfs/go-path/resolver"
27
- ft "github.com/ipfs/go-unixfs"
28
- "github.com/ipfs/go-unixfs/importer"
29
21
coreiface "github.com/ipfs/interface-go-ipfs-core"
30
22
ipath "github.com/ipfs/interface-go-ipfs-core/path"
31
23
routing "github.com/libp2p/go-libp2p-core/routing"
@@ -40,27 +32,36 @@ const (
40
32
// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
41
33
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
42
34
type gatewayHandler struct {
43
- node * core.IpfsNode
44
35
config GatewayConfig
45
36
api coreiface.CoreAPI
46
37
}
47
38
48
- func newGatewayHandler (n * core. IpfsNode , c GatewayConfig , api coreiface.CoreAPI ) * gatewayHandler {
39
+ func newGatewayHandler (c GatewayConfig , api coreiface.CoreAPI ) * gatewayHandler {
49
40
i := & gatewayHandler {
50
- node : n ,
51
41
config : c ,
52
42
api : api ,
53
43
}
54
44
return i
55
45
}
56
46
57
- // TODO(cryptix): find these helpers somewhere else
58
- func (i * gatewayHandler ) newDagFromReader (r io.Reader ) (ipld.Node , error ) {
59
- // TODO(cryptix): change and remove this helper once PR1136 is merged
60
- // return ufs.AddFromReader(i.node, r.Body)
61
- return importer .BuildDagFromReader (
62
- i .node .DAG ,
63
- chunker .DefaultSplitter (r ))
47
+ func parseIpfsPath (p string ) (cid.Cid , string , error ) {
48
+ rootPath , err := path .ParsePath (p )
49
+ if err != nil {
50
+ return cid.Cid {}, "" , err
51
+ }
52
+
53
+ // Check the path.
54
+ rsegs := rootPath .Segments ()
55
+ if rsegs [0 ] != "ipfs" {
56
+ return cid.Cid {}, "" , fmt .Errorf ("WritableGateway: only ipfs paths supported" , rsegs [0 ])
57
+ }
58
+
59
+ rootCid , err := cid .Decode (rsegs [1 ])
60
+ if err != nil {
61
+ return cid.Cid {}, "" , err
62
+ }
63
+
64
+ return rootCid , path .Join (rsegs [2 :]), nil
64
65
}
65
66
66
67
func (i * gatewayHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
@@ -160,10 +161,12 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
160
161
161
162
// Resolve path to the final DAG node for the ETag
162
163
resolvedPath , err := i .api .ResolvePath (r .Context (), parsedPath )
163
- if err == coreiface .ErrOffline && ! i .node .IsOnline {
164
+ switch err {
165
+ case nil :
166
+ case coreiface .ErrOffline :
164
167
webError (w , "ipfs resolve -r " + escapedURLPath , err , http .StatusServiceUnavailable )
165
168
return
166
- } else if err != nil {
169
+ default :
167
170
webError (w , "ipfs resolve -r " + escapedURLPath , err , http .StatusNotFound )
168
171
return
169
172
}
@@ -395,194 +398,146 @@ func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) {
395
398
}
396
399
397
400
func (i * gatewayHandler ) putHandler (w http.ResponseWriter , r * http.Request ) {
398
- rootPath , err := path .ParsePath (r .URL .Path )
401
+ ctx := r .Context ()
402
+ ds := i .api .Dag ()
403
+
404
+ // Parse the path
405
+ rootCid , newPath , err := parseIpfsPath (r .URL .Path )
399
406
if err != nil {
400
- webError (w , "putHandler: IPFS path not valid " , err , http .StatusBadRequest )
407
+ webError (w , "WritableGateway: failed to parse the path " , err , http .StatusBadRequest )
401
408
return
402
409
}
403
-
404
- rsegs := rootPath .Segments ()
405
- if rsegs [0 ] == ipnsPathPrefix {
406
- webError (w , "putHandler: updating named entries not supported" , errors .New ("WritableGateway: ipns put not supported" ), http .StatusBadRequest )
410
+ if newPath == "" || newPath == "/" {
411
+ http .Error (w , "WritableGateway: empty path" , http .StatusBadRequest )
407
412
return
408
413
}
414
+ newDirectory , _ := gopath .Split (newPath )
409
415
410
- var newnode ipld.Node
411
- if rsegs [len (rsegs )- 1 ] == "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" {
412
- newnode = ft .EmptyDirNode ()
413
- } else {
414
- putNode , err := i .newDagFromReader (r .Body )
415
- if err != nil {
416
- webError (w , "putHandler: Could not create DAG from request" , err , http .StatusInternalServerError )
417
- return
418
- }
419
- newnode = putNode
420
- }
416
+ // Resolve the old root.
421
417
422
- var newPath string
423
- if len (rsegs ) > 1 {
424
- newPath = path .Join (rsegs [2 :])
418
+ rnode , err := ds .Get (ctx , rootCid )
419
+ if err != nil {
420
+ webError (w , "WritableGateway: Could not create DAG from request" , err , http .StatusInternalServerError )
421
+ return
425
422
}
426
423
427
- var newcid cid.Cid
428
- rnode , err := resolve .Resolve (r .Context (), i .node .Namesys , i .node .Resolver , rootPath )
429
- switch ev := err .(type ) {
430
- case resolver.ErrNoLink :
431
- // ev.Node < node where resolve failed
432
- // ev.Name < new link
433
- // but we need to patch from the root
434
- c , err := cid .Decode (rsegs [1 ])
435
- if err != nil {
436
- webError (w , "putHandler: bad input path" , err , http .StatusBadRequest )
437
- return
438
- }
439
-
440
- rnode , err := i .node .DAG .Get (r .Context (), c )
441
- if err != nil {
442
- webError (w , "putHandler: Could not create DAG from request" , err , http .StatusInternalServerError )
443
- return
444
- }
445
-
446
- pbnd , ok := rnode .(* dag.ProtoNode )
447
- if ! ok {
448
- webError (w , "Cannot read non protobuf nodes through gateway" , dag .ErrNotProtobuf , http .StatusBadRequest )
449
- return
450
- }
451
-
452
- e := dagutils .NewDagEditor (pbnd , i .node .DAG )
453
- err = e .InsertNodeAtPath (r .Context (), newPath , newnode , ft .EmptyDirNode )
454
- if err != nil {
455
- webError (w , "putHandler: InsertNodeAtPath failed" , err , http .StatusInternalServerError )
456
- return
457
- }
458
-
459
- nnode , err := e .Finalize (r .Context (), i .node .DAG )
460
- if err != nil {
461
- webError (w , "putHandler: could not get node" , err , http .StatusInternalServerError )
462
- return
463
- }
424
+ pbnd , ok := rnode .(* dag.ProtoNode )
425
+ if ! ok {
426
+ webError (w , "Cannot read non protobuf nodes through gateway" , dag .ErrNotProtobuf , http .StatusBadRequest )
427
+ return
428
+ }
464
429
465
- newcid = nnode .Cid ()
430
+ // Create the new file.
431
+ newFilePath , err := i .api .Unixfs ().Add (ctx , files .NewReaderFile (r .Body ))
432
+ if err != nil {
433
+ webError (w , "WritableGateway: could not create DAG from request" , err , http .StatusInternalServerError )
434
+ return
435
+ }
466
436
467
- case nil :
468
- pbnd , ok := rnode .(* dag.ProtoNode )
469
- if ! ok {
470
- webError (w , "Cannot read non protobuf nodes through gateway" , dag .ErrNotProtobuf , http .StatusBadRequest )
471
- return
472
- }
437
+ newFile , err := ds .Get (ctx , newFilePath .Cid ())
438
+ if err != nil {
439
+ webError (w , "WritableGateway: failed to resolve new file" , err , http .StatusInternalServerError )
440
+ return
441
+ }
473
442
474
- pbnewnode , ok := newnode .(* dag.ProtoNode )
475
- if ! ok {
476
- webError (w , "Cannot read non protobuf nodes through gateway" , dag .ErrNotProtobuf , http .StatusBadRequest )
477
- return
478
- }
443
+ // Patch the new file into the old root.
479
444
480
- // object set-data case
481
- pbnd .SetData (pbnewnode .Data ())
445
+ root , err := mfs .NewRoot (ctx , ds , pbnd , nil )
446
+ if err != nil {
447
+ webError (w , "WritableGateway: failed to create MFS root" , err , http .StatusBadRequest )
448
+ return
449
+ }
482
450
483
- newcid = pbnd . Cid ()
484
- err = i . node . DAG . Add ( r . Context (), pbnd )
451
+ if newDirectory != "" {
452
+ err := mfs . Mkdir ( root , newDirectory , mfs. MkdirOpts { Mkparents : true , Flush : false } )
485
453
if err != nil {
486
- nnk := newnode .Cid ()
487
- webError (w , fmt .Sprintf ("putHandler: Could not add newnode(%q) to root(%q)" , nnk .String (), newcid .String ()), err , http .StatusInternalServerError )
454
+ webError (w , "WritableGateway: failed to create MFS directory" , err , http .StatusInternalServerError )
488
455
return
489
456
}
490
- default :
491
- webError (w , "could not resolve root DAG" , ev , http .StatusInternalServerError )
492
- return
493
457
}
458
+ err = mfs .PutNode (root , newPath , newFile )
459
+ if err != nil {
460
+ webError (w , "WritableGateway: failed to link file into directory" , err , http .StatusInternalServerError )
461
+ }
462
+ nnode , err := root .GetDirectory ().GetNode ()
463
+ if err != nil {
464
+ webError (w , "WritableGateway: failed to finalize" , err , http .StatusInternalServerError )
465
+ }
466
+ newcid := nnode .Cid ()
494
467
495
468
i .addUserHeaders (w ) // ok, _now_ write user's headers.
496
469
w .Header ().Set ("IPFS-Hash" , newcid .String ())
497
470
http .Redirect (w , r , gopath .Join (ipfsPathPrefix , newcid .String (), newPath ), http .StatusCreated )
498
471
}
499
472
500
473
func (i * gatewayHandler ) deleteHandler (w http.ResponseWriter , r * http.Request ) {
501
- urlPath := r .URL .Path
474
+ ctx := r .Context ()
475
+
476
+ // parse the path
502
477
503
- p , err := path . ParsePath ( urlPath )
478
+ rootCid , newPath , err := parseIpfsPath ( r . URL . Path )
504
479
if err != nil {
505
- webError (w , "failed to parse path" , err , http .StatusBadRequest )
480
+ webError (w , "WritableGateway: failed to parse the path" , err , http .StatusBadRequest )
506
481
return
507
482
}
508
-
509
- c , components , err := path .SplitAbsPath (p )
510
- if err != nil {
511
- webError (w , "Could not split path" , err , http .StatusInternalServerError )
483
+ if newPath == "" || newPath == "/" {
484
+ http .Error (w , "WritableGateway: empty path" , http .StatusBadRequest )
512
485
return
513
486
}
487
+ directory , filename := gopath .Split (newPath )
488
+
489
+ // lookup the root
514
490
515
- pathNodes , err := i .resolvePathComponents ( r . Context (), c , components )
491
+ rootNodeIPLD , err := i .api . Dag (). Get ( ctx , rootCid )
516
492
if err != nil {
517
- webError (w , "Could not resolve path components " , err , http .StatusBadRequest )
493
+ webError (w , "WritableGateway: failed to resolve root CID " , err , http .StatusInternalServerError )
518
494
return
519
495
}
520
-
521
- pbnd , ok := pathNodes [len (pathNodes )- 1 ].(* dag.ProtoNode )
496
+ rootNode , ok := rootNodeIPLD .(* dag.ProtoNode )
522
497
if ! ok {
523
- webError (w , "Cannot read non protobuf nodes through gateway " , dag . ErrNotProtobuf , http .StatusBadRequest )
498
+ http . Error (w , "WritableGateway: empty path " , http .StatusInternalServerError )
524
499
return
525
500
}
526
501
527
- // TODO(cyrptix): assumes len(pathNodes) > 1 - not found is an error above?
528
- err = pbnd .RemoveNodeLink (components [len (components )- 1 ])
502
+ // construct the mfs root
503
+
504
+ root , err := mfs .NewRoot (ctx , i .api .Dag (), rootNode , nil )
529
505
if err != nil {
530
- webError (w , "Could not delete link " , err , http .StatusBadRequest )
506
+ webError (w , "WritableGateway: failed to construct the MFS root " , err , http .StatusBadRequest )
531
507
return
532
508
}
533
509
534
- var newnode * dag.ProtoNode = pbnd
535
- for j := len (pathNodes ) - 2 ; j >= 0 ; j -- {
536
- if err := i .node .DAG .Add (r .Context (), newnode ); err != nil {
537
- webError (w , "Could not add node" , err , http .StatusInternalServerError )
538
- return
539
- }
540
-
541
- pathpb , ok := pathNodes [j ].(* dag.ProtoNode )
542
- if ! ok {
543
- webError (w , "Cannot read non protobuf nodes through gateway" , dag .ErrNotProtobuf , http .StatusBadRequest )
544
- return
545
- }
510
+ // lookup the parent directory
546
511
547
- newnode , err = pathpb .UpdateNodeLink (components [j ], newnode )
548
- if err != nil {
549
- webError (w , "Could not update node links" , err , http .StatusInternalServerError )
550
- return
551
- }
512
+ parentNode , err := mfs .Lookup (root , directory )
513
+ if err != nil {
514
+ webError (w , "WritableGateway: failed to look up parent" , err , http .StatusInternalServerError )
515
+ return
552
516
}
553
517
554
- if err := i .node .DAG .Add (r .Context (), newnode ); err != nil {
555
- webError (w , "Could not add root node" , err , http .StatusInternalServerError )
518
+ parent , ok := parentNode .(* mfs.Directory )
519
+ if ! ok {
520
+ http .Error (w , "WritableGateway: parent is not a directory" , http .StatusInternalServerError )
556
521
return
557
522
}
558
523
559
- // Redirect to new path
560
- ncid := newnode .Cid ()
561
-
562
- i .addUserHeaders (w ) // ok, _now_ write user's headers.
563
- w .Header ().Set ("IPFS-Hash" , ncid .String ())
564
- http .Redirect (w , r , gopath .Join (ipfsPathPrefix + ncid .String (), path .Join (components [:len (components )- 1 ])), http .StatusCreated )
565
- }
566
-
567
- func (i * gatewayHandler ) resolvePathComponents (
568
- ctx context.Context ,
569
- c cid.Cid ,
570
- components []string ,
571
- ) ([]ipld.Node , error ) {
572
- tctx , cancel := context .WithTimeout (ctx , time .Minute )
573
- defer cancel ()
524
+ // delete the file
574
525
575
- rootnd , err := i . node . Resolver . DAG . Get ( tctx , c )
526
+ err = parent . Unlink ( filename )
576
527
if err != nil {
577
- return nil , fmt .Errorf ("Could not resolve root object: %s" , err )
528
+ webError (w , "WritableGateway: failed to remove file" , err , http .StatusInternalServerError )
529
+ return
578
530
}
579
531
580
- pathNodes , err := i . node . Resolver . ResolveLinks ( tctx , rootnd , components [: len ( components ) - 1 ] )
532
+ nnode , err := root . GetDirectory (). GetNode ( )
581
533
if err != nil {
582
- return nil , fmt . Errorf ( "Could not resolve parent object: %s " , err )
534
+ webError ( w , "WritableGateway: failed to finalize " , err , http . StatusInternalServerError )
583
535
}
536
+ ncid := nnode .Cid ()
584
537
585
- return pathNodes , nil
538
+ i .addUserHeaders (w ) // ok, _now_ write user's headers.
539
+ w .Header ().Set ("IPFS-Hash" , ncid .String ())
540
+ http .Redirect (w , r , gopath .Join (ipfsPathPrefix + ncid .String (), directory ), http .StatusCreated )
586
541
}
587
542
588
543
func (i * gatewayHandler ) addUserHeaders (w http.ResponseWriter ) {
0 commit comments