Skip to content

Commit

Permalink
ipfs, http: support downloading directory files
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Oct 9, 2023
1 parent 7a1c164 commit 26f3da7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 6 deletions.
2 changes: 1 addition & 1 deletion http/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (as *apiServer) handlePin(jc jape.Context) {
return
}

r, err := as.node.DownloadCID(ctx, cid)
r, err := as.node.DownloadCID(ctx, cid, nil)
if err != nil {
jc.Error(err, http.StatusInternalServerError)
return
Expand Down
22 changes: 19 additions & 3 deletions http/ipfs.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package http

import (
"errors"
"io"
"net/http"
"strings"

"github.com/ipfs/go-cid"
format "github.com/ipfs/go-ipld-format"
Expand All @@ -24,9 +26,23 @@ type ipfsServer struct {
func (is *ipfsServer) handleIPFS(jc jape.Context) {
ctx := jc.Request.Context()

var pathStr string
if err := jc.DecodeParam("path", &pathStr); err != nil {
return
}

var cidStr string
if err := jc.DecodeParam("cid", &cidStr); err != nil {
is.log.Debug("downloading file", zap.String("path", pathStr))
pathStr = strings.TrimPrefix(pathStr, "/") // remove leading slash
path := strings.Split(pathStr, "/")
if len(path) == 0 || path[0] == "" {
jc.Error(errors.New("bad path"), http.StatusBadRequest)
return
} else if len(path) == 1 {
cidStr = path[0]
} else {
cidStr = path[0]
path = path[1:]
}

cid, err := cid.Parse(cidStr)
Expand All @@ -38,7 +54,7 @@ func (is *ipfsServer) handleIPFS(jc jape.Context) {
block, err := is.store.GetBlock(ctx, cid)
if format.IsNotFound(err) && is.ipfs.FetchRemote {
is.log.Info("downloading from ipfs", zap.String("cid", cid.Hash().B58String()))
r, err := is.node.DownloadCID(ctx, cid)
r, err := is.node.DownloadCID(ctx, cid, path)
if err != nil {
jc.Error(err, http.StatusInternalServerError)
is.log.Error("failed to download cid", zap.Error(err))
Expand Down Expand Up @@ -81,6 +97,6 @@ func NewIPFSHandler(node *ipfs.Node, store Store, cfg config.Config, log *zap.Lo
}

return jape.Mux(map[string]jape.Handler{
"GET /ipfs/:cid": s.handleIPFS,
"GET /ipfs/*path": s.handleIPFS,
})
}
33 changes: 31 additions & 2 deletions ipfs/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"strings"

"github.com/ipfs/boxo/bitswap"
bnetwork "github.com/ipfs/boxo/bitswap/network"
Expand Down Expand Up @@ -55,14 +56,42 @@ func (n *Node) Close() error {
}

// DownloadCID downloads the CID
func (n *Node) DownloadCID(ctx context.Context, c cid.Cid) (io.ReadSeekCloser, error) {
func (n *Node) DownloadCID(ctx context.Context, c cid.Cid, path []string) (io.ReadSeekCloser, error) {
dagSess := merkledag.NewSession(ctx, n.dagService)
rootNode, err := dagSess.Get(ctx, c)
if err != nil {
return nil, fmt.Errorf("failed to get root node: %w", err)
}

dr, err := fsio.NewDagReader(ctx, rootNode, dagSess)
var traverse func(context.Context, format.Node, []string) (format.Node, error)
traverse = func(ctx context.Context, parent format.Node, path []string) (format.Node, error) {
if len(path) == 0 {
return parent, nil
}

childLink, rem, err := parent.Resolve(path)
if err != nil {
return nil, fmt.Errorf("failed to resolve path %q: %w", strings.Join(path, "/"), err)
}

switch v := childLink.(type) {
case *format.Link:
childNode, err := dagSess.Get(ctx, v.Cid)
if err != nil {
return nil, fmt.Errorf("failed to get child node %q: %w", v.Cid, err)
}
return traverse(ctx, childNode, rem)
default:
return nil, fmt.Errorf("expected link node, got %T", childLink)
}
}

node, err := traverse(ctx, rootNode, path)
if err != nil {
return nil, fmt.Errorf("failed to traverse path: %w", err)
}

dr, err := fsio.NewDagReader(ctx, node, dagSess)
return dr, err
}

Expand Down

0 comments on commit 26f3da7

Please sign in to comment.