mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 06:57:40 +08:00
149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/blang/semver/v4"
|
|
"github.com/ipfs/boxo/files"
|
|
)
|
|
|
|
type RequestBuilder interface {
|
|
Arguments(args ...string) RequestBuilder
|
|
BodyString(body string) RequestBuilder
|
|
BodyBytes(body []byte) RequestBuilder
|
|
Body(body io.Reader) RequestBuilder
|
|
FileBody(body io.Reader) RequestBuilder
|
|
Option(key string, value interface{}) RequestBuilder
|
|
Header(name, value string) RequestBuilder
|
|
Send(ctx context.Context) (*Response, error)
|
|
Exec(ctx context.Context, res interface{}) error
|
|
}
|
|
|
|
// encodedAbsolutePathVersion is the version from which the absolute path header in
|
|
// multipart requests is %-encoded. Before this version, its sent raw.
|
|
var encodedAbsolutePathVersion = semver.MustParse("0.23.0-dev")
|
|
|
|
// requestBuilder is an IPFS commands request builder.
|
|
type requestBuilder struct {
|
|
command string
|
|
args []string
|
|
opts map[string]string
|
|
headers map[string]string
|
|
body io.Reader
|
|
buildError error
|
|
|
|
shell *HttpApi
|
|
}
|
|
|
|
// Arguments adds the arguments to the args.
|
|
func (r *requestBuilder) Arguments(args ...string) RequestBuilder {
|
|
r.args = append(r.args, args...)
|
|
return r
|
|
}
|
|
|
|
// BodyString sets the request body to the given string.
|
|
func (r *requestBuilder) BodyString(body string) RequestBuilder {
|
|
return r.Body(strings.NewReader(body))
|
|
}
|
|
|
|
// BodyBytes sets the request body to the given buffer.
|
|
func (r *requestBuilder) BodyBytes(body []byte) RequestBuilder {
|
|
return r.Body(bytes.NewReader(body))
|
|
}
|
|
|
|
// Body sets the request body to the given reader.
|
|
func (r *requestBuilder) Body(body io.Reader) RequestBuilder {
|
|
r.body = body
|
|
return r
|
|
}
|
|
|
|
// FileBody sets the request body to the given reader wrapped into multipartreader.
|
|
func (r *requestBuilder) FileBody(body io.Reader) RequestBuilder {
|
|
pr, _ := files.NewReaderPathFile("/dev/stdin", io.NopCloser(body), nil)
|
|
d := files.NewMapDirectory(map[string]files.Node{"": pr})
|
|
|
|
version, err := r.shell.loadRemoteVersion()
|
|
if err != nil {
|
|
// Unfortunately, we cannot return an error here. Changing this API is also
|
|
// not the best since we would otherwise have an inconsistent RequestBuilder.
|
|
// We save the error and return it when calling [requestBuilder.Send].
|
|
r.buildError = err
|
|
return r
|
|
}
|
|
|
|
useEncodedAbsPaths := version.LT(encodedAbsolutePathVersion)
|
|
r.body = files.NewMultiFileReader(d, false, useEncodedAbsPaths)
|
|
|
|
return r
|
|
}
|
|
|
|
// Option sets the given option.
|
|
func (r *requestBuilder) Option(key string, value interface{}) RequestBuilder {
|
|
var s string
|
|
switch v := value.(type) {
|
|
case bool:
|
|
s = strconv.FormatBool(v)
|
|
case string:
|
|
s = v
|
|
case []byte:
|
|
s = string(v)
|
|
default:
|
|
// slow case.
|
|
s = fmt.Sprint(value)
|
|
}
|
|
if r.opts == nil {
|
|
r.opts = make(map[string]string, 1)
|
|
}
|
|
r.opts[key] = s
|
|
return r
|
|
}
|
|
|
|
// Header sets the given header.
|
|
func (r *requestBuilder) Header(name, value string) RequestBuilder {
|
|
if r.headers == nil {
|
|
r.headers = make(map[string]string, 1)
|
|
}
|
|
r.headers[name] = value
|
|
return r
|
|
}
|
|
|
|
// Send sends the request and return the response.
|
|
func (r *requestBuilder) Send(ctx context.Context) (*Response, error) {
|
|
if r.buildError != nil {
|
|
return nil, r.buildError
|
|
}
|
|
|
|
r.shell.applyGlobal(r)
|
|
|
|
req := NewRequest(ctx, r.shell.url, r.command, r.args...)
|
|
req.Opts = r.opts
|
|
req.Headers = r.headers
|
|
req.Body = r.body
|
|
return req.Send(&r.shell.httpcli)
|
|
}
|
|
|
|
// Exec sends the request a request and decodes the response.
|
|
func (r *requestBuilder) Exec(ctx context.Context, res interface{}) error {
|
|
httpRes, err := r.Send(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res == nil {
|
|
lateErr := httpRes.Close()
|
|
if httpRes.Error != nil {
|
|
return httpRes.Error
|
|
}
|
|
return lateErr
|
|
}
|
|
|
|
return httpRes.decode(res)
|
|
}
|
|
|
|
var _ RequestBuilder = &requestBuilder{}
|