From e260d2fd066158dfd7ae8e5ae7e25ac202cdaa90 Mon Sep 17 00:00:00 2001
From: Lars Gierth <larsg@systemli.org>
Date: Wed, 16 Nov 2016 06:21:15 +0100
Subject: [PATCH 1/2] coreapi: smarter way of dealing with the different APIs

License: MIT
Signed-off-by: Lars Gierth <larsg@systemli.org>
---
 core/coreapi/coreapi.go             | 13 +++++++++++++
 core/coreapi/interface/interface.go |  9 ++++-----
 core/coreapi/unixfs.go              | 10 +---------
 core/coreapi/unixfs_test.go         |  2 +-
 core/corehttp/gateway.go            |  2 +-
 core/corehttp/gateway_handler.go    | 12 ++++++------
 6 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go
index f86b0e648a3..a44af4876e9 100644
--- a/core/coreapi/coreapi.go
+++ b/core/coreapi/coreapi.go
@@ -10,6 +10,19 @@ import (
 	ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
 )
 
+type CoreAPI struct {
+	node *core.IpfsNode
+}
+
+func NewCoreAPI(n *core.IpfsNode) coreiface.CoreAPI {
+	api := &CoreAPI{n}
+	return api
+}
+
+func (api *CoreAPI) Unixfs() coreiface.UnixfsAPI {
+	return (*UnixfsAPI)(api)
+}
+
 func resolve(ctx context.Context, n *core.IpfsNode, p string) (ipld.Node, error) {
 	pp, err := path.ParsePath(p)
 	if err != nil {
diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go
index b506e65096a..7bf9d5c0ac6 100644
--- a/core/coreapi/interface/interface.go
+++ b/core/coreapi/interface/interface.go
@@ -9,11 +9,6 @@ import (
 	ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
 )
 
-// type CoreAPI interface {
-// 	ID() CoreID
-// 	Version() CoreVersion
-// }
-
 type Link ipld.Link
 
 type Reader interface {
@@ -21,6 +16,10 @@ type Reader interface {
 	io.Closer
 }
 
+type CoreAPI interface {
+	Unixfs() UnixfsAPI
+}
+
 type UnixfsAPI interface {
 	Add(context.Context, io.Reader) (*cid.Cid, error)
 	Cat(context.Context, string) (Reader, error)
diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go
index db97b2594cc..0ec8cf8519e 100644
--- a/core/coreapi/unixfs.go
+++ b/core/coreapi/unixfs.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"io"
 
-	core "github.com/ipfs/go-ipfs/core"
 	coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
 	coreunix "github.com/ipfs/go-ipfs/core/coreunix"
 	uio "github.com/ipfs/go-ipfs/unixfs/io"
@@ -12,14 +11,7 @@ import (
 	cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
 )
 
-type UnixfsAPI struct {
-	node *core.IpfsNode
-}
-
-func NewUnixfsAPI(n *core.IpfsNode) coreiface.UnixfsAPI {
-	api := &UnixfsAPI{n}
-	return api
-}
+type UnixfsAPI CoreAPI
 
 func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (*cid.Cid, error) {
 	k, err := coreunix.AddWithContext(ctx, api.node, r)
diff --git a/core/coreapi/unixfs_test.go b/core/coreapi/unixfs_test.go
index 61270551045..3cda629afd6 100644
--- a/core/coreapi/unixfs_test.go
+++ b/core/coreapi/unixfs_test.go
@@ -41,7 +41,7 @@ func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.UnixfsAPI, error) {
 	if err != nil {
 		return nil, nil, err
 	}
-	api := coreapi.NewUnixfsAPI(node)
+	api := coreapi.NewCoreAPI(node).Unixfs()
 	return node, api, nil
 }
 
diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go
index e212c183b3a..84b9b246745 100644
--- a/core/corehttp/gateway.go
+++ b/core/corehttp/gateway.go
@@ -28,7 +28,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
 			Headers:      cfg.Gateway.HTTPHeaders,
 			Writable:     writable,
 			PathPrefixes: cfg.Gateway.PathPrefixes,
-		}, coreapi.NewUnixfsAPI(n))
+		}, coreapi.NewCoreAPI(n))
 
 		for _, p := range paths {
 			mux.Handle(p+"/", gateway)
diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go
index 67dae923b79..03d5ca18f05 100644
--- a/core/corehttp/gateway_handler.go
+++ b/core/corehttp/gateway_handler.go
@@ -37,10 +37,10 @@ const (
 type gatewayHandler struct {
 	node   *core.IpfsNode
 	config GatewayConfig
-	api    coreiface.UnixfsAPI
+	api    coreiface.CoreAPI
 }
 
-func newGatewayHandler(n *core.IpfsNode, c GatewayConfig, api coreiface.UnixfsAPI) *gatewayHandler {
+func newGatewayHandler(n *core.IpfsNode, c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
 	i := &gatewayHandler{
 		node:   n,
 		config: c,
@@ -158,7 +158,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 		ipnsHostname = true
 	}
 
-	dr, err := i.api.Cat(ctx, urlPath)
+	dr, err := i.api.Unixfs().Cat(ctx, urlPath)
 	dir := false
 	switch err {
 	case nil:
@@ -218,7 +218,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 		return
 	}
 
-	links, err := i.api.Ls(ctx, urlPath)
+	links, err := i.api.Unixfs().Ls(ctx, urlPath)
 	if err != nil {
 		internalWebError(w, err)
 		return
@@ -247,7 +247,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 			}
 
 			// return index page instead.
-			dr, err := i.api.Cat(ctx, p.String())
+			dr, err := i.api.Unixfs().Cat(ctx, p.String())
 			if err != nil {
 				internalWebError(w, err)
 				return
@@ -314,7 +314,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 }
 
 func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
-	k, err := i.api.Add(ctx, r.Body)
+	k, err := i.api.Unixfs().Add(ctx, r.Body)
 	if err != nil {
 		internalWebError(w, err)
 		return

From ee45b8d32f90313901429e7536c143e20dd5ff79 Mon Sep 17 00:00:00 2001
From: Lars Gierth <larsg@systemli.org>
Date: Fri, 17 Mar 2017 03:47:59 +0100
Subject: [PATCH 2/2] coreapi: make the interfaces path centric

The new coreiface.Path maps a path to the cid.Cid
resulting from a full path resolution.

The path is internally represented as a go-ipfs/path.Path,
but that doesn't matter to the outside.

Apart from the path-to-CID mapping, it also aims to hold all
resolved segment CIDs of the path. Right now it only exposes
Root(), and only for flat paths a la /ipfs/Qmfoo. In other cases,
the root is nil.

In the future, resolution will internally use
go-ipfs/path.Resolver.ResolvePathComponents and thus always return
the proper resolved segments, via Root(), or a future Segments() func.

- Add coreiface.Path with Cid() and Root().
- Add CoreAPI.ResolvePath() for getting a coreiface.Path.
- All functions now expect and return coreiface.Path.
- Add ParsePath() and ParseCid() for constructing a coreiface.Path.
- Add coreiface.Node and Link which are simply go-ipld-node.Node and Link.
- Add CoreAPI.ResolveNode() for getting a Node from a Path.

License: MIT
Signed-off-by: Lars Gierth <larsg@systemli.org>
---
 core/coreapi/coreapi.go             | 60 ++++++++++++++++++++++++++---
 core/coreapi/interface/interface.go | 19 +++++++--
 core/coreapi/unixfs.go              | 20 +++++++---
 core/coreapi/unixfs_test.go         | 58 +++++++++++++++-------------
 core/corehttp/gateway_handler.go    | 26 ++++++-------
 5 files changed, 128 insertions(+), 55 deletions(-)

diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go
index a44af4876e9..980dca7c0f3 100644
--- a/core/coreapi/coreapi.go
+++ b/core/coreapi/coreapi.go
@@ -5,9 +5,9 @@ import (
 
 	core "github.com/ipfs/go-ipfs/core"
 	coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
-	path "github.com/ipfs/go-ipfs/path"
+	ipfspath "github.com/ipfs/go-ipfs/path"
 
-	ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
+	cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
 )
 
 type CoreAPI struct {
@@ -23,17 +23,65 @@ func (api *CoreAPI) Unixfs() coreiface.UnixfsAPI {
 	return (*UnixfsAPI)(api)
 }
 
-func resolve(ctx context.Context, n *core.IpfsNode, p string) (ipld.Node, error) {
-	pp, err := path.ParsePath(p)
+func (api *CoreAPI) ResolveNode(ctx context.Context, p coreiface.Path) (coreiface.Node, error) {
+	p, err := api.ResolvePath(ctx, p)
 	if err != nil {
 		return nil, err
 	}
 
-	dagnode, err := core.Resolve(ctx, n.Namesys, n.Resolver, pp)
+	node, err := api.node.DAG.Get(ctx, p.Cid())
+	if err != nil {
+		return nil, err
+	}
+	return node, nil
+}
+
+// TODO: store all of ipfspath.Resolver.ResolvePathComponents() in Path
+func (api *CoreAPI) ResolvePath(ctx context.Context, p coreiface.Path) (coreiface.Path, error) {
+	if p.Resolved() {
+		return p, nil
+	}
+
+	p2 := ipfspath.FromString(p.String())
+	node, err := core.Resolve(ctx, api.node.Namesys, api.node.Resolver, p2)
 	if err == core.ErrNoNamesys {
 		return nil, coreiface.ErrOffline
 	} else if err != nil {
 		return nil, err
 	}
-	return dagnode, nil
+
+	var root *cid.Cid
+	if p2.IsJustAKey() {
+		root = node.Cid()
+	}
+
+	return ResolvedPath(p.String(), node.Cid(), root), nil
+}
+
+// Implements coreiface.Path
+type path struct {
+	path ipfspath.Path
+	cid  *cid.Cid
+	root *cid.Cid
+}
+
+func ParsePath(p string) (coreiface.Path, error) {
+	pp, err := ipfspath.ParsePath(p)
+	if err != nil {
+		return nil, err
+	}
+	return &path{path: pp}, nil
 }
+
+func ParseCid(c *cid.Cid) coreiface.Path {
+	return &path{path: ipfspath.FromCid(c), cid: c, root: c}
+}
+
+func ResolvedPath(p string, c *cid.Cid, r *cid.Cid) coreiface.Path {
+	return &path{path: ipfspath.FromString(p), cid: c, root: r}
+}
+
+func (p *path) String() string { return p.path.String() }
+func (p *path) Cid() *cid.Cid  { return p.cid }
+func (p *path) Root() *cid.Cid { return p.root }
+func (p *path) Resolved() bool { return p.cid != nil }
diff --git a/core/coreapi/interface/interface.go b/core/coreapi/interface/interface.go
index 7bf9d5c0ac6..d72fc8a3b0b 100644
--- a/core/coreapi/interface/interface.go
+++ b/core/coreapi/interface/interface.go
@@ -9,6 +9,16 @@ import (
 	ipld "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
 )
 
+type Path interface {
+	String() string
+	Cid() *cid.Cid
+	Root() *cid.Cid
+	Resolved() bool
+}
+
+// TODO: should we really copy these?
+//       if we didn't, godoc would generate nice links straight to go-ipld-node
+type Node ipld.Node
 type Link ipld.Link
 
 type Reader interface {
@@ -18,12 +28,14 @@ type Reader interface {
 
 type CoreAPI interface {
 	Unixfs() UnixfsAPI
+	ResolvePath(context.Context, Path) (Path, error)
+	ResolveNode(context.Context, Path) (Node, error)
 }
 
 type UnixfsAPI interface {
-	Add(context.Context, io.Reader) (*cid.Cid, error)
-	Cat(context.Context, string) (Reader, error)
-	Ls(context.Context, string) ([]*Link, error)
+	Add(context.Context, io.Reader) (Path, error)
+	Cat(context.Context, Path) (Reader, error)
+	Ls(context.Context, Path) ([]*Link, error)
 }
 
 // type ObjectAPI interface {
@@ -49,5 +61,4 @@ type UnixfsAPI interface {
 // }
 
 var ErrIsDir = errors.New("object is a directory")
-var ErrIsNonDag = errors.New("not a merkledag object")
 var ErrOffline = errors.New("can't resolve, ipfs node is offline")
diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go
index 0ec8cf8519e..80e93b4e970 100644
--- a/core/coreapi/unixfs.go
+++ b/core/coreapi/unixfs.go
@@ -13,16 +13,20 @@ import (
 
 type UnixfsAPI CoreAPI
 
-func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (*cid.Cid, error) {
+func (api *UnixfsAPI) Add(ctx context.Context, r io.Reader) (coreiface.Path, error) {
 	k, err := coreunix.AddWithContext(ctx, api.node, r)
 	if err != nil {
 		return nil, err
 	}
-	return cid.Decode(k)
+	c, err := cid.Decode(k)
+	if err != nil {
+		return nil, err
+	}
+	return ParseCid(c), nil
 }
 
-func (api *UnixfsAPI) Cat(ctx context.Context, p string) (coreiface.Reader, error) {
-	dagnode, err := resolve(ctx, api.node, p)
+func (api *UnixfsAPI) Cat(ctx context.Context, p coreiface.Path) (coreiface.Reader, error) {
+	dagnode, err := api.core().ResolveNode(ctx, p)
 	if err != nil {
 		return nil, err
 	}
@@ -36,8 +40,8 @@ func (api *UnixfsAPI) Cat(ctx context.Context, p string) (coreiface.Reader, erro
 	return r, nil
 }
 
-func (api *UnixfsAPI) Ls(ctx context.Context, p string) ([]*coreiface.Link, error) {
-	dagnode, err := resolve(ctx, api.node, p)
+func (api *UnixfsAPI) Ls(ctx context.Context, p coreiface.Path) ([]*coreiface.Link, error) {
+	dagnode, err := api.core().ResolveNode(ctx, p)
 	if err != nil {
 		return nil, err
 	}
@@ -49,3 +53,7 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p string) ([]*coreiface.Link, erro
 	}
 	return links, nil
 }
+
+func (api *UnixfsAPI) core() coreiface.CoreAPI {
+	return (*CoreAPI)(api)
+}
diff --git a/core/coreapi/unixfs_test.go b/core/coreapi/unixfs_test.go
index 3cda629afd6..7c2a64967e6 100644
--- a/core/coreapi/unixfs_test.go
+++ b/core/coreapi/unixfs_test.go
@@ -19,14 +19,14 @@ import (
 )
 
 // `echo -n 'hello, world!' | ipfs add`
-var hello = "QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk"
+var hello = coreapi.ResolvedPath("/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk", nil, nil)
 var helloStr = "hello, world!"
 
 // `ipfs object new unixfs-dir`
-var emptyUnixfsDir = "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
+var emptyDir = coreapi.ResolvedPath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", nil, nil)
 
 // `echo -n | ipfs add`
-var emptyUnixfsFile = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
+var emptyFile = coreapi.ResolvedPath("/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH", nil, nil)
 
 func makeAPI(ctx context.Context) (*core.IpfsNode, coreiface.UnixfsAPI, error) {
 	r := &repo.Mock{
@@ -53,13 +53,13 @@ func TestAdd(t *testing.T) {
 	}
 
 	str := strings.NewReader(helloStr)
-	c, err := api.Add(ctx, str)
+	p, err := api.Add(ctx, str)
 	if err != nil {
 		t.Error(err)
 	}
 
-	if c.String() != hello {
-		t.Fatalf("expected CID %s, got: %s", hello, c)
+	if p.String() != hello.String() {
+		t.Fatalf("expected path %s, got: %s", hello, p)
 	}
 
 	r, err := api.Cat(ctx, hello)
@@ -85,13 +85,13 @@ func TestAddEmptyFile(t *testing.T) {
 	}
 
 	str := strings.NewReader("")
-	c, err := api.Add(ctx, str)
+	p, err := api.Add(ctx, str)
 	if err != nil {
 		t.Error(err)
 	}
 
-	if c.String() != emptyUnixfsFile {
-		t.Fatalf("expected CID %s, got: %s", hello, c)
+	if p.String() != emptyFile.String() {
+		t.Fatalf("expected path %s, got: %s", hello, p)
 	}
 }
 
@@ -103,16 +103,17 @@ func TestCatBasic(t *testing.T) {
 	}
 
 	hr := strings.NewReader(helloStr)
-	k, err := coreunix.Add(node, hr)
+	p, err := coreunix.Add(node, hr)
 	if err != nil {
 		t.Fatal(err)
 	}
+	p = "/ipfs/" + p
 
-	if k != hello {
-		t.Fatalf("expected CID %s, got: %s", hello, k)
+	if p != hello.String() {
+		t.Fatalf("expected CID %s, got: %s", hello, p)
 	}
 
-	r, err := api.Cat(ctx, k)
+	r, err := api.Cat(ctx, hello)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -139,7 +140,7 @@ func TestCatEmptyFile(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	r, err := api.Cat(ctx, emptyUnixfsFile)
+	r, err := api.Cat(ctx, emptyFile)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -165,8 +166,13 @@ func TestCatDir(t *testing.T) {
 	if err != nil {
 		t.Error(err)
 	}
+	p := coreapi.ParseCid(c)
+
+	if p.String() != emptyDir.String() {
+		t.Fatalf("expected path %s, got: %s", emptyDir, p)
+	}
 
-	_, err = api.Cat(ctx, c.String())
+	_, err = api.Cat(ctx, emptyDir)
 	if err != coreiface.ErrIsDir {
 		t.Fatalf("expected ErrIsDir, got: %s", err)
 	}
@@ -184,7 +190,7 @@ func TestCatNonUnixfs(t *testing.T) {
 		t.Error(err)
 	}
 
-	_, err = api.Cat(ctx, c.String())
+	_, err = api.Cat(ctx, coreapi.ParseCid(c))
 	if !strings.Contains(err.Error(), "proto: required field") {
 		t.Fatalf("expected protobuf error, got: %s", err)
 	}
@@ -197,7 +203,7 @@ func TestCatOffline(t *testing.T) {
 		t.Error(err)
 	}
 
-	_, err = api.Cat(ctx, "/ipns/Qmfoobar")
+	_, err = api.Cat(ctx, coreapi.ResolvedPath("/ipns/Qmfoobar", nil, nil))
 	if err != coreiface.ErrOffline {
 		t.Fatalf("expected ErrOffline, got: %", err)
 	}
@@ -211,17 +217,17 @@ func TestLs(t *testing.T) {
 	}
 
 	r := strings.NewReader("content-of-file")
-	p, _, err := coreunix.AddWrapped(node, r, "name-of-file")
+	k, _, err := coreunix.AddWrapped(node, r, "name-of-file")
 	if err != nil {
 		t.Error(err)
 	}
-	parts := strings.Split(p, "/")
+	parts := strings.Split(k, "/")
 	if len(parts) != 2 {
-		t.Errorf("unexpected path:", p)
+		t.Errorf("unexpected path:", k)
 	}
-	k := parts[0]
+	p := coreapi.ResolvedPath("/ipfs/"+parts[0], nil, nil)
 
-	links, err := api.Ls(ctx, k)
+	links, err := api.Ls(ctx, p)
 	if err != nil {
 		t.Error(err)
 	}
@@ -236,7 +242,7 @@ func TestLs(t *testing.T) {
 		t.Fatalf("expected name = name-of-file, got %s", links[0].Name)
 	}
 	if links[0].Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" {
-		t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid.String())
+		t.Fatalf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", links[0].Cid)
 	}
 }
 
@@ -247,12 +253,12 @@ func TestLsEmptyDir(t *testing.T) {
 		t.Error(err)
 	}
 
-	c, err := node.DAG.Add(unixfs.EmptyDirNode())
+	_, err = node.DAG.Add(unixfs.EmptyDirNode())
 	if err != nil {
 		t.Error(err)
 	}
 
-	links, err := api.Ls(ctx, c.String())
+	links, err := api.Ls(ctx, emptyDir)
 	if err != nil {
 		t.Error(err)
 	}
@@ -275,7 +281,7 @@ func TestLsNonUnixfs(t *testing.T) {
 		t.Error(err)
 	}
 
-	links, err := api.Ls(ctx, c.String())
+	links, err := api.Ls(ctx, coreapi.ParseCid(c))
 	if err != nil {
 		t.Error(err)
 	}
diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go
index 03d5ca18f05..7c1c4ead737 100644
--- a/core/corehttp/gateway_handler.go
+++ b/core/corehttp/gateway_handler.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	core "github.com/ipfs/go-ipfs/core"
+	coreapi "github.com/ipfs/go-ipfs/core/coreapi"
 	coreiface "github.com/ipfs/go-ipfs/core/coreapi/interface"
 	"github.com/ipfs/go-ipfs/importer"
 	chunk "github.com/ipfs/go-ipfs/importer/chunk"
@@ -158,7 +159,13 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 		ipnsHostname = true
 	}
 
-	dr, err := i.api.Unixfs().Cat(ctx, urlPath)
+	parsedPath, err := coreapi.ParsePath(urlPath)
+	if err != nil {
+		webError(w, "invalid ipfs path", err, http.StatusBadRequest)
+		return
+	}
+
+	dr, err := i.api.Unixfs().Cat(ctx, parsedPath)
 	dir := false
 	switch err {
 	case nil:
@@ -218,7 +225,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 		return
 	}
 
-	links, err := i.api.Unixfs().Ls(ctx, urlPath)
+	links, err := i.api.Unixfs().Ls(ctx, parsedPath)
 	if err != nil {
 		internalWebError(w, err)
 		return
@@ -240,14 +247,7 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 				return
 			}
 
-			p, err := path.ParsePath(urlPath + "/index.html")
-			if err != nil {
-				internalWebError(w, err)
-				return
-			}
-
-			// return index page instead.
-			dr, err := i.api.Unixfs().Cat(ctx, p.String())
+			dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(link.Cid))
 			if err != nil {
 				internalWebError(w, err)
 				return
@@ -314,15 +314,15 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
 }
 
 func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
-	k, err := i.api.Unixfs().Add(ctx, r.Body)
+	p, err := i.api.Unixfs().Add(ctx, r.Body)
 	if err != nil {
 		internalWebError(w, err)
 		return
 	}
 
 	i.addUserHeaders(w) // ok, _now_ write user's headers.
-	w.Header().Set("IPFS-Hash", k.String())
-	http.Redirect(w, r, ipfsPathPrefix+k.String(), http.StatusCreated)
+	w.Header().Set("IPFS-Hash", p.Cid().String())
+	http.Redirect(w, r, p.String(), http.StatusCreated)
 }
 
 func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {