Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URL store #4896

Merged
merged 13 commits into from
Jul 13, 2018
2 changes: 1 addition & 1 deletion core/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func setupNode(ctx context.Context, n *IpfsNode, cfg *BuildCfg) error {
n.GCLocker = bstore.NewGCLocker()
n.Blockstore = bstore.NewGCBlockstore(cbs, n.GCLocker)

if conf.Experimental.FilestoreEnabled {
if conf.Experimental.FilestoreEnabled || conf.Experimental.UrlstoreEnabled {
// hash security
n.Filestore = filestore.NewFilestore(cbs, n.Repo.FileManager())
n.Blockstore = bstore.NewGCBlockstore(n.Filestore, n.GCLocker)
Expand Down
4 changes: 2 additions & 2 deletions core/commands/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
blockservice "github.com/ipfs/go-ipfs/blockservice"
core "github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/core/coreunix"
filestore "github.com/ipfs/go-ipfs/filestore"
dag "github.com/ipfs/go-ipfs/merkledag"
dagtest "github.com/ipfs/go-ipfs/merkledag/test"
mfs "github.com/ipfs/go-ipfs/mfs"
Expand Down Expand Up @@ -183,8 +184,7 @@ You can now check what blocks have been created by:

// nocopy -> filestoreEnabled
if nocopy && !cfg.Experimental.FilestoreEnabled {
res.SetError(errors.New("filestore is not enabled, see https://git.io/vNItf"),
cmdkit.ErrClient)
res.SetError(filestore.ErrFilestoreNotEnabled, cmdkit.ErrClient)
return
}

Expand Down
2 changes: 2 additions & 0 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ func TestCommands(t *testing.T) {
"/tar/add",
"/tar/cat",
"/update",
"/urlstore",
"/urlstore/add",
"/version",
}

Expand Down
2 changes: 1 addition & 1 deletion core/commands/filestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func getFilestore(env interface{}) (*core.IpfsNode, *filestore.Filestore, error)
}
fs := n.Filestore
if fs == nil {
return n, nil, fmt.Errorf("filestore not enabled")
return n, nil, filestore.ErrFilestoreNotEnabled
}
return n, fs, err
}
Expand Down
3 changes: 2 additions & 1 deletion core/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"strings"

oldcmds "github.com/ipfs/go-ipfs/commands"
lgc "github.com/ipfs/go-ipfs/commands/legacy"
dag "github.com/ipfs/go-ipfs/core/commands/dag"
e "github.com/ipfs/go-ipfs/core/commands/e"
ocmd "github.com/ipfs/go-ipfs/core/commands/object"
unixfs "github.com/ipfs/go-ipfs/core/commands/unixfs"

lgc "github.com/ipfs/go-ipfs/commands/legacy"
"gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
"gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
Expand Down Expand Up @@ -136,6 +136,7 @@ var rootSubcommands = map[string]*cmds.Command{
"tar": lgc.NewCommand(TarCmd),
"file": lgc.NewCommand(unixfs.UnixFSCmd),
"update": lgc.NewCommand(ExternalBinary()),
"urlstore": lgc.NewCommand(urlStoreCmd),
"version": lgc.NewCommand(VersionCmd),
"shutdown": lgc.NewCommand(daemonShutdownCmd),
}
Expand Down
122 changes: 122 additions & 0 deletions core/commands/urlstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package commands

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

cmds "github.com/ipfs/go-ipfs/commands"
filestore "github.com/ipfs/go-ipfs/filestore"
balanced "github.com/ipfs/go-ipfs/importer/balanced"
ihelper "github.com/ipfs/go-ipfs/importer/helpers"

mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
chunk "gx/ipfs/QmXnzH7wowyLZy8XJxxaQCVTgLMcDXdMBznmsrmQWCyiQV/go-ipfs-chunker"
cid "gx/ipfs/QmapdYm1b22Frv3k17fqrBYTFRxwiaVJkB299Mfn33edeB/go-cid"
cmdkit "gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
)

var urlStoreCmd = &cmds.Command{

Subcommands: map[string]*cmds.Command{
"add": urlAdd,
},
}

var urlAdd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Add URL via urlstore.",
LongDescription: `
Add URLs to ipfs without storing the data locally.

The URL provided must be stable and ideally on a web server under your
control.

The file is added using raw-leaves but otherwise using the default
settings for 'ipfs add'.

The file is not pinned, so this command should be followed by an 'ipfs
pin add'.

This command is considered temporary until a better solution can be
found. It may disappear or the semantics can change at any
time.
`,
},
Arguments: []cmdkit.Argument{
cmdkit.StringArg("url", true, false, "URL to add to IPFS"),
},
Type: BlockStat{},

Run: func(req cmds.Request, res cmds.Response) {
url := req.Arguments()[0]
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

if !filestore.IsURL(url) {
res.SetError(fmt.Errorf("unsupported url syntax: %s", url), cmdkit.ErrNormal)
return
}

cfg, err := n.Repo.Config()
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

if !cfg.Experimental.UrlstoreEnabled {
res.SetError(filestore.ErrUrlstoreNotEnabled, cmdkit.ErrNormal)
return
}

hreq, err := http.NewRequest("GET", url, nil)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

hres, err := http.DefaultClient.Do(hreq)
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}
if hres.StatusCode != http.StatusOK {
res.SetError(fmt.Errorf("expected code 200, got: %d", hres.StatusCode), cmdkit.ErrNormal)
return
}

chk := chunk.NewSizeSplitter(hres.Body, chunk.DefaultBlockSize)
prefix := cid.NewPrefixV1(cid.DagProtobuf, mh.SHA2_256)
dbp := &ihelper.DagBuilderParams{
Dagserv: n.DAG,
RawLeaves: true,
Maxlinks: ihelper.DefaultLinksPerBlock,
NoCopy: true,
Prefix: &prefix,
URL: url,
}

blc, err := balanced.Layout(dbp.New(chk))
if err != nil {
res.SetError(err, cmdkit.ErrNormal)
return
}

res.SetOutput(BlockStat{
Key: blc.Cid().String(),
Size: int(hres.ContentLength),
})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
ch := res.Output().(<-chan interface{})
bs0 := <-ch
bs := bs0.(*BlockStat)
return strings.NewReader(bs.Key + "\n"), nil
},
},
}
21 changes: 21 additions & 0 deletions docs/experimental-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ the above issue.
- [go-multiplex stream muxer](#go-multiplex-stream-muxer)
- [Raw leaves for unixfs files](#raw-leaves-for-unixfs-files)
- [ipfs filestore](#ipfs-filestore)
- [ipfs urlstore](#ipfs-urlstore)
- [BadgerDB datastore](#badger-datastore)
- [Private Networks](#private-networks)
- [ipfs p2p](#ipfs-p2p)
Expand Down Expand Up @@ -164,6 +165,26 @@ And then pass the `--nocopy` flag when running `ipfs add`

---

## ipfs urlstore
Allows ipfs to retrieve blocks contents via a url instead of storing it in the datastore

### State
experimental.

### In Version
???.

### How to enable
Modify your ipfs config:
```
ipfs config --json Experimental.UrlstoreEnabled true
```

### Road to being a real feature
???.

---

## Private Networks

Allows ipfs to only connect to other peers who have a shared secret key.
Expand Down
4 changes: 4 additions & 0 deletions filestore/filestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package filestore

import (
"context"
"errors"

blocks "gx/ipfs/QmTRCUvZLiir12Qr6MV3HKfKMHX8Nf1Vddn6t2g5nsQSb9/go-block-format"
posinfo "gx/ipfs/QmUWsXLvYYDAaoAt9TPZpFX4ffHHMg46AHrz1ZLTN5ABbe/go-ipfs-posinfo"
Expand All @@ -20,6 +21,9 @@ import (

var log = logging.Logger("filestore")

var ErrFilestoreNotEnabled = errors.New("filestore is not enabled, see https://git.io/vNItf")
var ErrUrlstoreNotEnabled = errors.New("urlstore is not enabled")

// Filestore implements a Blockstore by combining a standard Blockstore
// to store regular blocks and a special Blockstore called
// FileManager to store blocks which data exists in an external file.
Expand Down
13 changes: 13 additions & 0 deletions filestore/filestore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func newTestFilestore(t *testing.T) (string, *Filestore) {
t.Fatal(err)
}
fm := NewFileManager(mds, testdir)
fm.AllowFiles = true

bs := blockstore.NewBlockstore(mds)
fstore := NewFilestore(bs, fm)
Expand Down Expand Up @@ -162,3 +163,15 @@ func TestDeletes(t *testing.T) {
}
}
}

func TestIsURL(t *testing.T) {
if !IsURL("http://www.example.com") {
t.Fatal("IsURL failed: http://www.example.com")
}
if !IsURL("https://www.example.com") {
t.Fatal("IsURL failed: https://www.example.com")
}
if IsURL("adir/afile") || IsURL("http:/ /afile") || IsURL("http:/a/file") {
t.Fatal("IsURL recognized non-url")
}
}