forked from ipfs-cluster/ipfs-cluster
-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
229 lines (196 loc) · 5.62 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package client
import (
"context"
"errors"
"fmt"
"net/http"
"time"
shell "github.com/ipfs/go-ipfs-api"
logging "github.com/ipfs/go-log"
host "github.com/libp2p/go-libp2p-host"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
manet "github.com/multiformats/go-multiaddr-net"
)
// Configuration defaults
var (
DefaultTimeout = 120 * time.Second
DefaultAPIAddr = "/ip4/127.0.0.1/tcp/9094"
DefaultLogLevel = "info"
DefaultProxyPort = 9095
)
var loggingFacility = "apiclient"
var logger = logging.Logger(loggingFacility)
// Config allows to configure the parameters to connect
// to the ipfs-cluster REST API.
type Config struct {
// Enable SSL support. Only valid without PeerAddr.
SSL bool
// Skip certificate verification (insecure)
NoVerifyCert bool
// Username and password for basic authentication
Username string
Password string
// The ipfs-cluster REST API endpoint in multiaddress form
// (takes precedence over host:port). Only valid without PeerAddr.
APIAddr ma.Multiaddr
// REST API endpoint host and port. Only valid without
// APIAddr and PeerAddr
Host string
Port string
// The ipfs-cluster REST API peer address (usually
// the same as the cluster peer). This will use libp2p
// to tunnel HTTP requests, thus getting encryption for
// free. It overseeds APIAddr, Host/Port, and SSL configurations.
PeerAddr ma.Multiaddr
// If PeerAddr is provided, and the peer uses private networks
// (pnet), then we need to provide the key. If the peer is the
// cluster peer, this corresponds to the cluster secret.
ProtectorKey []byte
// ProxyAddr is used to obtain a go-ipfs-api Shell instance pointing
// to the ipfs proxy endpoint of ipfs-cluster. If empty, the location
// will be guessed from one of PeerAddr/APIAddr/Host,
// and the port used will be ipfs-cluster's proxy default port (9095)
ProxyAddr ma.Multiaddr
// Define timeout for network operations
Timeout time.Duration
// Specifies if we attempt to re-use connections to the same
// hosts.
DisableKeepAlives bool
// LogLevel defines the verbosity of the logging facility
LogLevel string
}
// Client provides methods to interact with the ipfs-cluster API. Use
// NewClient() to create one.
type Client struct {
ctx context.Context
cancel func()
config *Config
transport *http.Transport
net string
hostname string
client *http.Client
p2p host.Host
}
// NewClient initializes a client given a Config.
func NewClient(cfg *Config) (*Client, error) {
ctx := context.Background()
client := &Client{
ctx: ctx,
config: cfg,
}
if client.config.Timeout == 0 {
client.config.Timeout = DefaultTimeout
}
err := client.setupHTTPClient()
if err != nil {
return nil, err
}
err = client.setupHostname()
if err != nil {
return nil, err
}
err = client.setupProxy()
if err != nil {
return nil, err
}
if lvl := cfg.LogLevel; lvl != "" {
logging.SetLogLevel(loggingFacility, lvl)
} else {
logging.SetLogLevel(loggingFacility, DefaultLogLevel)
}
return client, nil
}
func (c *Client) setupHTTPClient() error {
var err error
switch {
case c.config.PeerAddr != nil:
err = c.enableLibp2p()
case c.config.SSL:
err = c.enableTLS()
default:
c.defaultTransport()
}
if err != nil {
return err
}
c.client = &http.Client{
Transport: c.transport,
Timeout: c.config.Timeout,
}
return nil
}
func (c *Client) setupHostname() error {
// When no host/port/multiaddress defined, we set the default
if c.config.APIAddr == nil && c.config.Host == "" && c.config.Port == "" {
var err error
c.config.APIAddr, err = ma.NewMultiaddr(DefaultAPIAddr)
if err != nil {
return err
}
}
// PeerAddr takes precedence over APIAddr. APIAddr takes precedence
// over Host/Port. APIAddr is resolved and dial args
// extracted.
switch {
case c.config.PeerAddr != nil:
// Taken care of in setupHTTPClient
case c.config.APIAddr != nil:
// Resolve multiaddress just in case and extract host:port
resolveCtx, cancel := context.WithTimeout(c.ctx, c.config.Timeout)
defer cancel()
resolved, err := madns.Resolve(resolveCtx, c.config.APIAddr)
if err != nil {
return err
}
c.config.APIAddr = resolved[0]
_, c.hostname, err = manet.DialArgs(c.config.APIAddr)
if err != nil {
return err
}
default:
c.hostname = fmt.Sprintf("%s:%s", c.config.Host, c.config.Port)
apiAddr, err := ma.NewMultiaddr(
fmt.Sprintf("/ip4/%s/tcp/%s", c.config.Host, c.config.Port),
)
if err != nil {
return err
}
c.config.APIAddr = apiAddr
}
return nil
}
func (c *Client) setupProxy() error {
if c.config.ProxyAddr != nil {
return nil
}
// Guess location from PeerAddr or APIAddr
port, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", DefaultProxyPort))
if err != nil {
return err
}
var paddr ma.Multiaddr
switch {
case c.config.PeerAddr != nil:
paddr = ma.Split(c.config.PeerAddr)[0].Encapsulate(port)
case c.config.APIAddr != nil: // Host/Port setupHostname sets APIAddr
paddr = ma.Split(c.config.APIAddr)[0].Encapsulate(port)
default:
return errors.New("cannot find proxy address")
}
ctx, cancel := context.WithTimeout(c.ctx, c.config.Timeout)
defer cancel()
resolved, err := madns.Resolve(ctx, paddr)
if err != nil {
return err
}
c.config.ProxyAddr = resolved[0]
return nil
}
// IPFS returns an instance of go-ipfs-api's Shell, pointing to the
// configured ProxyAddr (or to the default ipfs-cluster's IPFS proxy port).
// It re-uses this Client's HTTP client, thus will be constrained by
// the same configurations affecting it (timeouts...).
func (c *Client) IPFS() *shell.Shell {
return shell.NewShellWithClient(c.config.ProxyAddr.String(), c.client)
}