Skip to content
This repository was archived by the owner on Oct 5, 2023. It is now read-only.

Commit 93943f7

Browse files
committed
Initial structure, path stuff
1 parent 4e7edce commit 93943f7

File tree

6 files changed

+476
-0
lines changed

6 files changed

+476
-0
lines changed

api.go

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package httpapi
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"io/ioutil"
6+
gohttp "net/http"
7+
"os"
8+
"path"
9+
"strings"
10+
11+
"github.com/ipfs/go-ipfs/core/coreapi/interface"
12+
homedir "github.com/mitchellh/go-homedir"
13+
ma "github.com/multiformats/go-multiaddr"
14+
manet "github.com/multiformats/go-multiaddr-net"
15+
)
16+
17+
const (
18+
DefaultPathName = ".ipfs"
19+
DefaultPathRoot = "~/" + DefaultPathName
20+
DefaultApiFile = "api"
21+
EnvDir = "IPFS_PATH"
22+
)
23+
24+
var ErrNotImplemented = errors.New("not implemented")
25+
26+
type HttpApi struct {
27+
url string
28+
httpcli *gohttp.Client
29+
}
30+
31+
func NewLocalApi() iface.CoreAPI {
32+
baseDir := os.Getenv(EnvDir)
33+
if baseDir == "" {
34+
baseDir = DefaultPathRoot
35+
}
36+
37+
baseDir, err := homedir.Expand(baseDir)
38+
if err != nil {
39+
return nil
40+
}
41+
42+
apiFile := path.Join(baseDir, DefaultApiFile)
43+
44+
if _, err := os.Stat(apiFile); err != nil {
45+
return nil
46+
}
47+
48+
api, err := ioutil.ReadFile(apiFile)
49+
if err != nil {
50+
return nil
51+
}
52+
53+
return NewApi(strings.TrimSpace(string(api)))
54+
}
55+
56+
func NewApi(url string) *HttpApi {
57+
c := &gohttp.Client{
58+
Transport: &gohttp.Transport{
59+
Proxy: gohttp.ProxyFromEnvironment,
60+
DisableKeepAlives: true,
61+
},
62+
}
63+
64+
return NewApiWithClient(url, c)
65+
}
66+
67+
func NewApiWithClient(url string, c *gohttp.Client) *HttpApi {
68+
if a, err := ma.NewMultiaddr(url); err == nil {
69+
_, host, err := manet.DialArgs(a)
70+
if err == nil {
71+
url = host
72+
}
73+
}
74+
75+
return &HttpApi{
76+
url: url,
77+
httpcli: c,
78+
}
79+
}
80+
81+
func (api *HttpApi) request(command string, args ...string) *RequestBuilder {
82+
return &RequestBuilder{
83+
command: command,
84+
args: args,
85+
shell: api,
86+
}
87+
}
88+
89+
func (api *HttpApi) Unixfs() iface.UnixfsAPI {
90+
return nil
91+
}
92+
93+
func (api *HttpApi) Block() iface.BlockAPI {
94+
return nil
95+
}
96+
97+
func (api *HttpApi) Dag() iface.DagAPI {
98+
return nil
99+
}
100+
101+
func (api *HttpApi) Name() iface.NameAPI {
102+
return (*NameAPI)(api)
103+
}
104+
105+
func (api *HttpApi) Key() iface.KeyAPI {
106+
return nil
107+
}
108+
109+
func (api *HttpApi) Pin() iface.PinAPI {
110+
return nil
111+
}
112+
113+
func (api *HttpApi) Object() iface.ObjectAPI {
114+
return nil
115+
}
116+
117+
func (api *HttpApi) Dht() iface.DhtAPI {
118+
return nil
119+
}
120+
121+
func (api *HttpApi) Swarm() iface.SwarmAPI {
122+
return nil
123+
}
124+
125+
func (api *HttpApi) PubSub() iface.PubSubAPI {
126+
return nil
127+
}

name.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package httpapi
2+
3+
import (
4+
"context"
5+
"github.com/ipfs/go-ipfs/core/coreapi/interface"
6+
"github.com/ipfs/go-ipfs/core/coreapi/interface/options"
7+
)
8+
9+
type NameAPI HttpApi
10+
11+
func (api *NameAPI) Publish(ctx context.Context, p iface.Path, opts ...options.NamePublishOption) (iface.IpnsEntry, error) {
12+
return nil, ErrNotImplemented
13+
}
14+
15+
func (api *NameAPI) Search(ctx context.Context, name string, opts ...options.NameResolveOption) (<-chan iface.IpnsResult, error) {
16+
return nil, ErrNotImplemented
17+
}
18+
19+
func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (iface.Path, error) {
20+
// TODO: options!
21+
22+
req := api.core().request("name/resolve")
23+
req.Arguments(name)
24+
25+
var out struct{ Path string }
26+
if err := req.Exec(ctx, &out); err != nil {
27+
return nil, err
28+
}
29+
30+
return iface.ParsePath(out.Path)
31+
}
32+
33+
func (api *NameAPI) core() *HttpApi {
34+
return (*HttpApi)(api)
35+
}

path.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package httpapi
2+
3+
import (
4+
"context"
5+
6+
"github.com/ipfs/go-ipfs/core/coreapi/interface"
7+
8+
cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
9+
ipfspath "gx/ipfs/QmRG3XuGwT7GYuAqgWDJBKTzdaHMwAnc1x7J2KHEXNHxzG/go-path"
10+
ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
11+
)
12+
13+
func (api *HttpApi) ResolvePath(ctx context.Context, path iface.Path) (iface.ResolvedPath, error) {
14+
var out struct {
15+
Cid cid.Cid
16+
RemPath string
17+
}
18+
19+
//TODO: this is hacky, fixing https://github.com/ipfs/go-ipfs/issues/5703 would help
20+
21+
var err error
22+
if path.Namespace() == "ipns" {
23+
if path, err = api.Name().Resolve(ctx, path.String()); err != nil {
24+
return nil, err
25+
}
26+
}
27+
28+
if err := api.request("dag/resolve", path.String()).Exec(ctx, &out); err != nil {
29+
return nil, err
30+
}
31+
32+
// TODO:
33+
ipath, err := ipfspath.FromSegments("/" +path.Namespace() + "/", out.Cid.String(), out.RemPath)
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
root, err := cid.Parse(ipfspath.Path(path.String()).Segments()[1])
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return iface.NewResolvedPath(ipath, out.Cid, root, out.RemPath), nil
44+
}
45+
46+
func (api *HttpApi) ResolveNode(context.Context, iface.Path) (ipld.Node, error) {
47+
return nil, ErrNotImplemented
48+
}

request.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package httpapi
2+
3+
import (
4+
"context"
5+
"io"
6+
"strings"
7+
)
8+
9+
type Request struct {
10+
ApiBase string
11+
Command string
12+
Args []string
13+
Opts map[string]string
14+
Body io.Reader
15+
Headers map[string]string
16+
}
17+
18+
func NewRequest(ctx context.Context, url, command string, args ...string) *Request {
19+
if !strings.HasPrefix(url, "http") {
20+
url = "http://" + url
21+
}
22+
23+
opts := map[string]string{
24+
"encoding": "json",
25+
"stream-channels": "true",
26+
}
27+
return &Request{
28+
ApiBase: url + "/api/v0",
29+
Command: command,
30+
Args: args,
31+
Opts: opts,
32+
Headers: make(map[string]string),
33+
}
34+
}

requestbuilder.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package httpapi
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"io"
8+
"strconv"
9+
"strings"
10+
)
11+
12+
// RequestBuilder is an IPFS commands request builder.
13+
type RequestBuilder struct {
14+
command string
15+
args []string
16+
opts map[string]string
17+
headers map[string]string
18+
body io.Reader
19+
20+
shell *HttpApi
21+
}
22+
23+
// Arguments adds the arguments to the args.
24+
func (r *RequestBuilder) Arguments(args ...string) *RequestBuilder {
25+
r.args = append(r.args, args...)
26+
return r
27+
}
28+
29+
// BodyString sets the request body to the given string.
30+
func (r *RequestBuilder) BodyString(body string) *RequestBuilder {
31+
return r.Body(strings.NewReader(body))
32+
}
33+
34+
// BodyBytes sets the request body to the given buffer.
35+
func (r *RequestBuilder) BodyBytes(body []byte) *RequestBuilder {
36+
return r.Body(bytes.NewReader(body))
37+
}
38+
39+
// Body sets the request body to the given reader.
40+
func (r *RequestBuilder) Body(body io.Reader) *RequestBuilder {
41+
r.body = body
42+
return r
43+
}
44+
45+
// Option sets the given option.
46+
func (r *RequestBuilder) Option(key string, value interface{}) *RequestBuilder {
47+
var s string
48+
switch v := value.(type) {
49+
case bool:
50+
s = strconv.FormatBool(v)
51+
case string:
52+
s = v
53+
case []byte:
54+
s = string(v)
55+
default:
56+
// slow case.
57+
s = fmt.Sprint(value)
58+
}
59+
if r.opts == nil {
60+
r.opts = make(map[string]string, 1)
61+
}
62+
r.opts[key] = s
63+
return r
64+
}
65+
66+
// Header sets the given header.
67+
func (r *RequestBuilder) Header(name, value string) *RequestBuilder {
68+
if r.headers == nil {
69+
r.headers = make(map[string]string, 1)
70+
}
71+
r.headers[name] = value
72+
return r
73+
}
74+
75+
// Send sends the request and return the response.
76+
func (r *RequestBuilder) Send(ctx context.Context) (*Response, error) {
77+
req := NewRequest(ctx, r.shell.url, r.command, r.args...)
78+
req.Opts = r.opts
79+
req.Headers = r.headers
80+
req.Body = r.body
81+
return req.Send(r.shell.httpcli)
82+
}
83+
84+
// Exec sends the request a request and decodes the response.
85+
func (r *RequestBuilder) Exec(ctx context.Context, res interface{}) error {
86+
httpRes, err := r.Send(ctx)
87+
if err != nil {
88+
return err
89+
}
90+
91+
if res == nil {
92+
httpRes.Close()
93+
if httpRes.Error != nil {
94+
return httpRes.Error
95+
}
96+
return nil
97+
}
98+
99+
return httpRes.Decode(res)
100+
}

0 commit comments

Comments
 (0)