Skip to content

Commit 86d8e75

Browse files
committed
client/rpc: migrate go-ipfs-http-client
2 parents c10b804 + 7d82623 commit 86d8e75

31 files changed

+3195
-51
lines changed

.github/workflows/build.yml

-33
Original file line numberDiff line numberDiff line change
@@ -127,39 +127,6 @@ jobs:
127127
working-directory: go-ipfs-api
128128
- run: cmd/ipfs/ipfs shutdown
129129
if: always()
130-
go-ipfs-http-client:
131-
needs: [interop-prep]
132-
runs-on: ubuntu-latest
133-
timeout-minutes: 5
134-
env:
135-
TEST_DOCKER: 0
136-
TEST_FUSE: 0
137-
TEST_VERBOSE: 1
138-
TRAVIS: 1
139-
GIT_PAGER: cat
140-
IPFS_CHECK_RCMGR_DEFAULTS: 1
141-
defaults:
142-
run:
143-
shell: bash
144-
steps:
145-
- uses: actions/setup-go@v3
146-
with:
147-
go-version: ${{ env.GO_VERSION }}
148-
- uses: actions/download-artifact@v3
149-
with:
150-
name: kubo
151-
path: cmd/ipfs
152-
- run: chmod +x cmd/ipfs/ipfs
153-
- uses: actions/checkout@v3
154-
with:
155-
repository: ipfs/go-ipfs-http-client
156-
path: go-ipfs-http-client
157-
- uses: protocol/cache-go-action@v1
158-
with:
159-
name: ${{ github.job }}
160-
- run: echo '${{ github.workspace }}/cmd/ipfs' >> $GITHUB_PATH
161-
- run: go test -count=1 -v ./...
162-
working-directory: go-ipfs-http-client
163130
ipfs-webui:
164131
needs: [interop-prep]
165132
runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }}

.golangci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ linters:
44

55
linters-settings:
66
stylecheck:
7+
checks:
8+
- all
9+
- '-ST1003'
710
dot-import-whitelist:
811
- github.com/ipfs/kubo/test/cli/testutils

client/rpc/README.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# `httpapi`
2+
3+
> IPFS CoreAPI implementation using HTTP API
4+
5+
This packages implements [`coreiface.CoreAPI`](https://pkg.go.dev/github.com/ipfs/boxo/coreiface#CoreAPI) over the HTTP API.
6+
7+
## Documentation
8+
9+
https://pkg.go.dev/github.com/ipfs/kubo/client/rpc
10+
11+
### Example
12+
13+
Pin file on your local IPFS node based on its CID:
14+
15+
```go
16+
package main
17+
18+
import (
19+
"context"
20+
"fmt"
21+
22+
ipfsClient "github.com/ipfs/kubo/client/rpc"
23+
path "github.com/ipfs/boxo/coreiface/path"
24+
)
25+
26+
func main() {
27+
// "Connect" to local node
28+
node, err := ipfsClient.NewLocalApi()
29+
if err != nil {
30+
fmt.Printf(err)
31+
return
32+
}
33+
// Pin a given file by its CID
34+
ctx := context.Background()
35+
cid := "bafkreidtuosuw37f5xmn65b3ksdiikajy7pwjjslzj2lxxz2vc4wdy3zku"
36+
p := path.New(cid)
37+
err = node.Pin().Add(ctx, p)
38+
if err != nil {
39+
fmt.Printf(err)
40+
return
41+
}
42+
return
43+
}
44+
```

client/rpc/api.go

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package httpapi
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
11+
iface "github.com/ipfs/boxo/coreiface"
12+
caopts "github.com/ipfs/boxo/coreiface/options"
13+
"github.com/mitchellh/go-homedir"
14+
ma "github.com/multiformats/go-multiaddr"
15+
manet "github.com/multiformats/go-multiaddr/net"
16+
)
17+
18+
const (
19+
DefaultPathName = ".ipfs"
20+
DefaultPathRoot = "~/" + DefaultPathName
21+
DefaultApiFile = "api"
22+
EnvDir = "IPFS_PATH"
23+
)
24+
25+
// ErrApiNotFound if we fail to find a running daemon.
26+
var ErrApiNotFound = errors.New("ipfs api address could not be found")
27+
28+
// HttpApi implements github.com/ipfs/interface-go-ipfs-core/CoreAPI using
29+
// IPFS HTTP API.
30+
//
31+
// For interface docs see
32+
// https://godoc.org/github.com/ipfs/interface-go-ipfs-core#CoreAPI
33+
type HttpApi struct {
34+
url string
35+
httpcli http.Client
36+
Headers http.Header
37+
applyGlobal func(*requestBuilder)
38+
}
39+
40+
// NewLocalApi tries to construct new HttpApi instance communicating with local
41+
// IPFS daemon
42+
//
43+
// Daemon api address is pulled from the $IPFS_PATH/api file.
44+
// If $IPFS_PATH env var is not present, it defaults to ~/.ipfs
45+
func NewLocalApi() (*HttpApi, error) {
46+
baseDir := os.Getenv(EnvDir)
47+
if baseDir == "" {
48+
baseDir = DefaultPathRoot
49+
}
50+
51+
return NewPathApi(baseDir)
52+
}
53+
54+
// NewPathApi constructs new HttpApi by pulling api address from specified
55+
// ipfspath. Api file should be located at $ipfspath/api
56+
func NewPathApi(ipfspath string) (*HttpApi, error) {
57+
a, err := ApiAddr(ipfspath)
58+
if err != nil {
59+
if os.IsNotExist(err) {
60+
err = ErrApiNotFound
61+
}
62+
return nil, err
63+
}
64+
return NewApi(a)
65+
}
66+
67+
// ApiAddr reads api file in specified ipfs path
68+
func ApiAddr(ipfspath string) (ma.Multiaddr, error) {
69+
baseDir, err := homedir.Expand(ipfspath)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
apiFile := filepath.Join(baseDir, DefaultApiFile)
75+
76+
api, err := os.ReadFile(apiFile)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return ma.NewMultiaddr(strings.TrimSpace(string(api)))
82+
}
83+
84+
// NewApi constructs HttpApi with specified endpoint
85+
func NewApi(a ma.Multiaddr) (*HttpApi, error) {
86+
c := &http.Client{
87+
Transport: &http.Transport{
88+
Proxy: http.ProxyFromEnvironment,
89+
DisableKeepAlives: true,
90+
},
91+
}
92+
93+
return NewApiWithClient(a, c)
94+
}
95+
96+
// NewApiWithClient constructs HttpApi with specified endpoint and custom http client
97+
func NewApiWithClient(a ma.Multiaddr, c *http.Client) (*HttpApi, error) {
98+
_, url, err := manet.DialArgs(a)
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
if a, err := ma.NewMultiaddr(url); err == nil {
104+
_, host, err := manet.DialArgs(a)
105+
if err == nil {
106+
url = host
107+
}
108+
}
109+
110+
proto := "http://"
111+
112+
// By default, DialArgs is going to provide details suitable for connecting
113+
// a socket to, but not really suitable for making an informed choice of http
114+
// protocol. For multiaddresses specifying tls and/or https we want to make
115+
// a https request instead of a http request.
116+
protocols := a.Protocols()
117+
for _, p := range protocols {
118+
if p.Code == ma.P_HTTPS || p.Code == ma.P_TLS {
119+
proto = "https://"
120+
break
121+
}
122+
}
123+
124+
return NewURLApiWithClient(proto+url, c)
125+
}
126+
127+
func NewURLApiWithClient(url string, c *http.Client) (*HttpApi, error) {
128+
api := &HttpApi{
129+
url: url,
130+
httpcli: *c,
131+
Headers: make(map[string][]string),
132+
applyGlobal: func(*requestBuilder) {},
133+
}
134+
135+
// We don't support redirects.
136+
api.httpcli.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
137+
return fmt.Errorf("unexpected redirect")
138+
}
139+
return api, nil
140+
}
141+
142+
func (api *HttpApi) WithOptions(opts ...caopts.ApiOption) (iface.CoreAPI, error) {
143+
options, err := caopts.ApiOptions(opts...)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
subApi := *api
149+
subApi.applyGlobal = func(req *requestBuilder) {
150+
if options.Offline {
151+
req.Option("offline", options.Offline)
152+
}
153+
}
154+
155+
return &subApi, nil
156+
}
157+
158+
func (api *HttpApi) Request(command string, args ...string) RequestBuilder {
159+
headers := make(map[string]string)
160+
if api.Headers != nil {
161+
for k := range api.Headers {
162+
headers[k] = api.Headers.Get(k)
163+
}
164+
}
165+
return &requestBuilder{
166+
command: command,
167+
args: args,
168+
shell: api,
169+
headers: headers,
170+
}
171+
}
172+
173+
func (api *HttpApi) Unixfs() iface.UnixfsAPI {
174+
return (*UnixfsAPI)(api)
175+
}
176+
177+
func (api *HttpApi) Block() iface.BlockAPI {
178+
return (*BlockAPI)(api)
179+
}
180+
181+
func (api *HttpApi) Dag() iface.APIDagService {
182+
return (*HttpDagServ)(api)
183+
}
184+
185+
func (api *HttpApi) Name() iface.NameAPI {
186+
return (*NameAPI)(api)
187+
}
188+
189+
func (api *HttpApi) Key() iface.KeyAPI {
190+
return (*KeyAPI)(api)
191+
}
192+
193+
func (api *HttpApi) Pin() iface.PinAPI {
194+
return (*PinAPI)(api)
195+
}
196+
197+
func (api *HttpApi) Object() iface.ObjectAPI {
198+
return (*ObjectAPI)(api)
199+
}
200+
201+
func (api *HttpApi) Dht() iface.DhtAPI {
202+
return (*DhtAPI)(api)
203+
}
204+
205+
func (api *HttpApi) Swarm() iface.SwarmAPI {
206+
return (*SwarmAPI)(api)
207+
}
208+
209+
func (api *HttpApi) PubSub() iface.PubSubAPI {
210+
return (*PubsubAPI)(api)
211+
}
212+
213+
func (api *HttpApi) Routing() iface.RoutingAPI {
214+
return (*RoutingAPI)(api)
215+
}

0 commit comments

Comments
 (0)