ipfsconn/ipfshttp: handle cid args passed in url path correctly

The extractCid function was added to enable the extraction of
a cid argument from either the url path or query string.
This puts the proxy behaviour on par with the current IPFS API.
The function does rely on the fact that ipfs-cluster doesn't
intercept any command that has more than one subcommand.
If that changes, this function will have to be updated.

License: MIT
Signed-off-by: Adrian Lanzafame <adrianlanzafame92@gmail.com>
This commit is contained in:
Adrian Lanzafame 2018-04-20 14:09:07 +10:00
parent dbcc5c2fde
commit 22ec210c25
No known key found for this signature in database
GPG Key ID: 87E40C5D62EAE192
3 changed files with 440 additions and 214 deletions

View File

@ -155,11 +155,13 @@ func NewConnector(cfg *Config) (*Connector, error) {
client: c,
}
smux.HandleFunc("/", ipfs.handle)
ipfs.handlers["/api/v0/pin/add"] = ipfs.pinHandler
ipfs.handlers["/api/v0/pin/rm"] = ipfs.unpinHandler
ipfs.handlers["/api/v0/pin/ls"] = ipfs.pinLsHandler
ipfs.handlers["/api/v0/add"] = ipfs.addHandler
smux.HandleFunc("/", ipfs.defaultHandler)
smux.HandleFunc("/api/v0/pin/add/", ipfs.pinHandler)
smux.HandleFunc("/api/v0/pin/rm/", ipfs.unpinHandler)
smux.HandleFunc("/api/v0/pin/ls", ipfs.pinLsHandler) // to handle /pin/ls for all pins
smux.HandleFunc("/api/v0/pin/ls/", ipfs.pinLsHandler)
smux.HandleFunc("/api/v0/add", ipfs.addHandler)
smux.HandleFunc("/api/v0/add/", ipfs.addHandler)
go ipfs.run()
return ipfs, nil
@ -179,9 +181,11 @@ func (ipfs *Connector) run() {
ipfs.wg.Add(1)
go func() {
defer ipfs.wg.Done()
logger.Infof("IPFS Proxy: %s -> %s",
logger.Infof(
"IPFS Proxy: %s -> %s",
ipfs.config.ProxyAddr,
ipfs.config.NodeAddr)
ipfs.config.NodeAddr,
)
err := ipfs.server.Serve(ipfs.listener) // hangs here
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
logger.Error(err)
@ -209,17 +213,6 @@ func (ipfs *Connector) run() {
}()
}
// This will run a custom handler if we have one for a URL.Path, or
// otherwise just proxy the requests.
func (ipfs *Connector) handle(w http.ResponseWriter, r *http.Request) {
if customHandler, ok := ipfs.handlers[r.URL.Path]; ok {
customHandler(w, r)
} else {
ipfs.defaultHandler(w, r)
}
}
func (ipfs *Connector) proxyRequest(r *http.Request) (*http.Response, error) {
newURL := *r.URL
newURL.Host = ipfs.nodeAddr
@ -282,26 +275,26 @@ func ipfsErrorResponder(w http.ResponseWriter, errMsg string) {
}
func (ipfs *Connector) pinOpHandler(op string, w http.ResponseWriter, r *http.Request) {
argA := r.URL.Query()["arg"]
if len(argA) == 0 {
arg, ok := extractArgument(r.URL)
if !ok {
ipfsErrorResponder(w, "Error: bad argument")
return
}
arg := argA[0]
_, err := cid.Decode(arg)
if err != nil {
ipfsErrorResponder(w, "Error parsing CID: "+err.Error())
return
}
err = ipfs.rpcClient.Call("",
err = ipfs.rpcClient.Call(
"",
"Cluster",
op,
api.PinSerial{
Cid: arg,
},
&struct{}{})
&struct{}{},
)
if err != nil {
ipfsErrorResponder(w, err.Error())
return
@ -329,24 +322,23 @@ func (ipfs *Connector) pinLsHandler(w http.ResponseWriter, r *http.Request) {
pinLs := ipfsPinLsResp{}
pinLs.Keys = make(map[string]ipfsPinType)
q := r.URL.Query()
arg := q.Get("arg")
if arg != "" {
arg, ok := extractArgument(r.URL)
if ok {
c, err := cid.Decode(arg)
if err != nil {
ipfsErrorResponder(w, err.Error())
return
}
var pin api.PinSerial
err = ipfs.rpcClient.Call("",
err = ipfs.rpcClient.Call(
"",
"Cluster",
"PinGet",
api.PinCid(c).ToSerial(),
&pin)
&pin,
)
if err != nil {
ipfsErrorResponder(w, fmt.Sprintf(
"Error: path '%s' is not pinned",
arg))
ipfsErrorResponder(w, fmt.Sprintf("Error: path '%s' is not pinned", arg))
return
}
pinLs.Keys[pin.Cid] = ipfsPinType{
@ -354,12 +346,13 @@ func (ipfs *Connector) pinLsHandler(w http.ResponseWriter, r *http.Request) {
}
} else {
var pins []api.PinSerial
err := ipfs.rpcClient.Call("",
err := ipfs.rpcClient.Call(
"",
"Cluster",
"Pins",
struct{}{},
&pins)
&pins,
)
if err != nil {
ipfsErrorResponder(w, err.Error())
return
@ -455,13 +448,15 @@ func (ipfs *Connector) addHandler(w http.ResponseWriter, r *http.Request) {
logger.Debugf("proxy /add request and will pin %s", pinHashes)
for _, pin := range pinHashes {
err := ipfs.rpcClient.Call("",
err := ipfs.rpcClient.Call(
"",
"Cluster",
"Pin",
api.PinSerial{
Cid: pin,
},
&struct{}{})
&struct{}{},
)
if err != nil {
// we need to fail the operation and make sure the
// user knows about it.
@ -782,11 +777,13 @@ func (ipfs *Connector) apiURL() string {
// triggers ipfs swarm connect requests
func (ipfs *Connector) ConnectSwarms() error {
var idsSerial []api.IDSerial
err := ipfs.rpcClient.Call("",
err := ipfs.rpcClient.Call(
"",
"Cluster",
"Peers",
struct{}{},
&idsSerial)
&idsSerial,
)
if err != nil {
logger.Error(err)
return err
@ -799,8 +796,7 @@ func (ipfs *Connector) ConnectSwarms() error {
// This is a best effort attempt
// We ignore errors which happens
// when passing in a bunch of addresses
_, err := ipfs.post(
fmt.Sprintf("swarm/connect?arg=%s", addr))
_, err := ipfs.post(fmt.Sprintf("swarm/connect?arg=%s", addr))
if err != nil {
logger.Debug(err)
continue
@ -918,3 +914,24 @@ func (ipfs *Connector) SwarmPeers() (api.SwarmPeers, error) {
}
return swarm, nil
}
// extractArgument extracts the cid argument from a url.URL, either via
// the query string parameters or from the url path itself.
func extractArgument(u *url.URL) (string, bool) {
arg := u.Query().Get("arg")
if arg != "" {
return arg, true
}
p := strings.TrimPrefix(u.Path, "/api/v0/")
segs := strings.Split(p, "/")
if len(segs) > 2 {
warnMsg := "You are using an undocumented form of the IPFS API."
warnMsg += "Consider passing your command arguments"
warnMsg += "with the '?arg=' query parameter"
logger.Warning(warnMsg)
return segs[len(segs)-1], true
}
return "", false
}

View File

@ -11,12 +11,12 @@ import (
"testing"
"time"
"github.com/ipfs/ipfs-cluster/api"
"github.com/ipfs/ipfs-cluster/test"
cid "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
ma "github.com/multiformats/go-multiaddr"
"github.com/ipfs/ipfs-cluster/api"
"github.com/ipfs/ipfs-cluster/test"
)
func init() {
@ -199,48 +199,100 @@ func TestIPFSProxyPin(t *testing.T) {
defer mock.Close()
defer ipfs.Shutdown()
res, err := http.Post(fmt.Sprintf("%s/pin/add?arg=%s", proxyURL(ipfs), test.TestCid1), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
type args struct {
urlPath string
testCid string
statusCode int
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
"pin good cid query arg",
args{
"/pin/add?arg=",
test.TestCid1,
http.StatusOK,
},
test.TestCid1,
false,
},
{
"pin good cid url arg",
args{
"/pin/add/",
test.TestCid1,
http.StatusOK,
},
test.TestCid1,
false,
},
{
"pin bad cid query arg",
args{
"/pin/add?arg=",
test.ErrorCid,
http.StatusInternalServerError,
},
"",
true,
},
{
"pin bad cid url arg",
args{
"/pin/add/",
test.ErrorCid,
http.StatusInternalServerError,
},
"",
true,
},
}
resBytes, _ := ioutil.ReadAll(res.Body)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u := fmt.Sprintf("%s%s%s", proxyURL(ipfs), tt.args.urlPath, tt.args.testCid)
res, err := http.Post(u, "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
var resp ipfsPinOpResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != tt.args.statusCode {
t.Errorf("statusCode: got = %v, want %v", res.StatusCode, tt.args.statusCode)
}
if len(resp.Pins) != 1 || resp.Pins[0] != test.TestCid1 {
t.Error("wrong response")
}
resBytes, _ := ioutil.ReadAll(res.Body)
// Try with a bad cid
res2, err := http.Post(fmt.Sprintf("%s/pin/add?arg=%s", proxyURL(ipfs), test.ErrorCid), "", nil)
if err != nil {
t.Fatal("request should work: ", err)
}
defer res2.Body.Close()
switch tt.wantErr {
case false:
var resp ipfsPinOpResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
t.Log(fmt.Sprintf("%s/pin/add?arg=%s", proxyURL(ipfs), test.ErrorCid))
if res2.StatusCode != http.StatusInternalServerError {
t.Error("the request should return with InternalServerError")
}
if len(resp.Pins) != 1 {
t.Fatalf("wrong number of pins: got = %d, want %d", len(resp.Pins), 1)
}
resBytes, _ = ioutil.ReadAll(res2.Body)
var respErr ipfsError
err = json.Unmarshal(resBytes, &respErr)
if err != nil {
t.Fatal(err)
}
if resp.Pins[0] != tt.want {
t.Errorf("wrong pin cid: got = %s, want = %s", resp.Pins[0], tt.want)
}
case true:
var respErr ipfsError
err = json.Unmarshal(resBytes, &respErr)
if err != nil {
t.Fatal(err)
}
if respErr.Message != test.ErrBadCid.Error() {
t.Error("wrong response")
if respErr.Message != test.ErrBadCid.Error() {
t.Errorf("wrong response: got = %s, want = %s", respErr.Message, test.ErrBadCid.Error())
}
}
})
}
}
@ -249,48 +301,100 @@ func TestIPFSProxyUnpin(t *testing.T) {
defer mock.Close()
defer ipfs.Shutdown()
res, err := http.Post(fmt.Sprintf("%s/pin/rm?arg=%s", proxyURL(ipfs), test.TestCid1), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
type args struct {
urlPath string
testCid string
statusCode int
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
"unpin good cid query arg",
args{
"/pin/rm?arg=",
test.TestCid1,
http.StatusOK,
},
test.TestCid1,
false,
},
{
"unpin good cid url arg",
args{
"/pin/rm/",
test.TestCid1,
http.StatusOK,
},
test.TestCid1,
false,
},
{
"unpin bad cid query arg",
args{
"/pin/rm?arg=",
test.ErrorCid,
http.StatusInternalServerError,
},
"",
true,
},
{
"unpin bad cid url arg",
args{
"/pin/rm/",
test.ErrorCid,
http.StatusInternalServerError,
},
"",
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
u := fmt.Sprintf("%s%s%s", proxyURL(ipfs), tt.args.urlPath, tt.args.testCid)
res, err := http.Post(u, "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
resBytes, _ := ioutil.ReadAll(res.Body)
if res.StatusCode != tt.args.statusCode {
t.Errorf("statusCode: got = %v, want %v", res.StatusCode, tt.args.statusCode)
}
var resp ipfsPinOpResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
resBytes, _ := ioutil.ReadAll(res.Body)
if len(resp.Pins) != 1 || resp.Pins[0] != test.TestCid1 {
t.Error("wrong response")
}
switch tt.wantErr {
case false:
var resp ipfsPinOpResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
// Try with a bad cid
res2, err := http.Post(fmt.Sprintf("%s/pin/rm?arg=%s", proxyURL(ipfs), test.ErrorCid), "", nil)
if err != nil {
t.Fatal("request should work: ", err)
}
defer res2.Body.Close()
if len(resp.Pins) != 1 {
t.Fatalf("wrong number of pins: got = %d, want %d", len(resp.Pins), 1)
}
if res2.StatusCode != http.StatusInternalServerError {
t.Error("the request should return with InternalServerError")
}
if resp.Pins[0] != tt.want {
t.Errorf("wrong pin cid: got = %s, want = %s", resp.Pins[0], tt.want)
}
case true:
var respErr ipfsError
err = json.Unmarshal(resBytes, &respErr)
if err != nil {
t.Fatal(err)
}
resBytes, _ = ioutil.ReadAll(res2.Body)
var respErr ipfsError
err = json.Unmarshal(resBytes, &respErr)
if err != nil {
t.Fatal(err)
}
if respErr.Message != test.ErrBadCid.Error() {
t.Error("wrong response")
if respErr.Message != test.ErrBadCid.Error() {
t.Errorf("wrong response: got = %s, want = %s", respErr.Message, test.ErrBadCid.Error())
}
}
})
}
}
@ -299,55 +403,84 @@ func TestIPFSProxyPinLs(t *testing.T) {
defer mock.Close()
defer ipfs.Shutdown()
res, err := http.Post(fmt.Sprintf("%s/pin/ls?arg=%s", proxyURL(ipfs), test.TestCid1), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
}
t.Run("pin/ls query arg", func(t *testing.T) {
res, err := http.Post(fmt.Sprintf("%s/pin/ls?arg=%s", proxyURL(ipfs), test.TestCid1), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
}
resBytes, _ := ioutil.ReadAll(res.Body)
resBytes, _ := ioutil.ReadAll(res.Body)
var resp ipfsPinLsResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
var resp ipfsPinLsResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
_, ok := resp.Keys[test.TestCid1]
if len(resp.Keys) != 1 || !ok {
t.Error("wrong response")
}
})
_, ok := resp.Keys[test.TestCid1]
if len(resp.Keys) != 1 || !ok {
t.Error("wrong response")
}
t.Run("pin/ls url arg", func(t *testing.T) {
res, err := http.Post(fmt.Sprintf("%s/pin/ls/%s", proxyURL(ipfs), test.TestCid1), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
}
res2, err := http.Post(fmt.Sprintf("%s/pin/ls", proxyURL(ipfs)), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res2.Body.Close()
if res2.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
}
resBytes, _ := ioutil.ReadAll(res.Body)
var resp ipfsPinLsResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
resBytes, _ = ioutil.ReadAll(res2.Body)
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
_, ok := resp.Keys[test.TestCid1]
if len(resp.Keys) != 1 || !ok {
t.Error("wrong response")
}
})
if len(resp.Keys) != 3 {
t.Error("wrong response")
}
t.Run("pin/ls all no arg", func(t *testing.T) {
res2, err := http.Post(fmt.Sprintf("%s/pin/ls", proxyURL(ipfs)), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res2.Body.Close()
if res2.StatusCode != http.StatusOK {
t.Error("the request should have succeeded")
}
res3, err := http.Post(fmt.Sprintf("%s/pin/ls?arg=%s", proxyURL(ipfs), test.ErrorCid), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res3.Body.Close()
if res3.StatusCode != http.StatusInternalServerError {
t.Error("the request should have failed")
}
resBytes, _ := ioutil.ReadAll(res2.Body)
var resp ipfsPinLsResp
err = json.Unmarshal(resBytes, &resp)
if err != nil {
t.Fatal(err)
}
if len(resp.Keys) != 3 {
t.Error("wrong response")
}
})
t.Run("pin/ls bad cid query arg", func(t *testing.T) {
res3, err := http.Post(fmt.Sprintf("%s/pin/ls?arg=%s", proxyURL(ipfs), test.ErrorCid), "", nil)
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res3.Body.Close()
if res3.StatusCode != http.StatusInternalServerError {
t.Error("the request should have failed")
}
})
}
func TestProxyAdd(t *testing.T) {
@ -388,42 +521,44 @@ func TestProxyAdd(t *testing.T) {
}
for i := 0; i < len(urlQueries); i++ {
res, err := http.DefaultClient.Do(reqs[i])
if err != nil {
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Fatal("Bad response status")
}
var hash ipfsAddResp
// We might return a progress notification, so we do it
// like this to ignore it easily
dec := json.NewDecoder(res.Body)
for dec.More() {
var resp ipfsAddResp
err := dec.Decode(&resp)
t.Run(urlQueries[i], func(t *testing.T) {
res, err := http.DefaultClient.Do(reqs[i])
if err != nil {
t.Fatal(err)
t.Fatal("should have succeeded: ", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Fatalf("Bad response status: got = %d, want = %d", res.StatusCode, http.StatusOK)
}
if resp.Bytes != 0 {
continue
} else {
hash = resp
}
}
var hash ipfsAddResp
if hash.Hash != test.TestCid3 {
t.Logf("%+v", hash)
t.Error("expected TestCid1 as it is hardcoded in ipfs mock")
}
if hash.Name != "testfile" {
t.Logf("%+v", hash)
t.Error("expected testfile for hash name")
}
// We might return a progress notification, so we do it
// like this to ignore it easily
dec := json.NewDecoder(res.Body)
for dec.More() {
var resp ipfsAddResp
err := dec.Decode(&resp)
if err != nil {
t.Fatal(err)
}
if resp.Bytes != 0 {
continue
} else {
hash = resp
}
}
if hash.Hash != test.TestCid3 {
t.Logf("%+v", hash)
t.Error("expected TestCid1 as it is hardcoded in ipfs mock")
}
if hash.Name != "testfile" {
t.Logf("%+v", hash)
t.Error("expected testfile for hash name")
}
})
}
}
@ -437,8 +572,7 @@ func TestProxyAddError(t *testing.T) {
}
res.Body.Close()
if res.StatusCode != http.StatusInternalServerError {
t.Log(res.StatusCode)
t.Error("expected an error")
t.Errorf("wrong status code: got = %d, want = %d", res.StatusCode, http.StatusInternalServerError)
}
}
@ -634,3 +768,72 @@ func proxyURL(c *Connector) string {
addr := c.listener.Addr()
return fmt.Sprintf("http://%s/api/v0", addr.String())
}
func Test_extractArgument(t *testing.T) {
type args struct {
handlePath string
u *url.URL
}
tests := []struct {
name string
args args
want string
want1 bool
}{
{
"pin/add url arg",
args{
"add",
mustParseURL(fmt.Sprintf("/api/v0/pin/add/%s", test.TestCid1)),
},
test.TestCid1,
true,
},
{
"pin/add query arg",
args{
"add",
mustParseURL(fmt.Sprintf("/api/v0/pin/add?arg=%s", test.TestCid1)),
},
test.TestCid1,
true,
},
{
"pin/ls url arg",
args{
"pin/ls",
mustParseURL(fmt.Sprintf("/api/v0/pin/ls/%s", test.TestCid1)),
},
test.TestCid1,
true,
},
{
"pin/ls query arg",
args{
"pin/ls",
mustParseURL(fmt.Sprintf("/api/v0/pin/ls?arg=%s", test.TestCid1)),
},
test.TestCid1,
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := extractArgument(tt.args.u)
if got != tt.want {
t.Errorf("extractCid() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("extractCid() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func mustParseURL(rawurl string) *url.URL {
u, err := url.Parse(rawurl)
if err != nil {
panic(err)
}
return u
}

View File

@ -99,7 +99,6 @@ func NewIpfsMock() *IpfsMock {
func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
endp := strings.TrimPrefix(p, "/api/v0/")
var cidStr string
switch endp {
case "id":
resp := mockIDResp{
@ -138,45 +137,40 @@ func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
j, _ := json.Marshal(resp)
w.Write(j)
case "pin/add":
query := r.URL.Query()
arg, ok := query["arg"]
if !ok || len(arg) != 1 {
arg, ok := extractCid(r.URL)
if !ok {
goto ERROR
}
cidStr = arg[0]
if cidStr == ErrorCid {
if arg == ErrorCid {
goto ERROR
}
c, err := cid.Decode(cidStr)
c, err := cid.Decode(arg)
if err != nil {
goto ERROR
}
m.pinMap.Add(api.PinCid(c))
resp := mockPinResp{
Pins: []string{cidStr},
Pins: []string{arg},
}
j, _ := json.Marshal(resp)
w.Write(j)
case "pin/rm":
query := r.URL.Query()
arg, ok := query["arg"]
if !ok || len(arg) != 1 {
arg, ok := extractCid(r.URL)
if !ok {
goto ERROR
}
cidStr = arg[0]
c, err := cid.Decode(cidStr)
c, err := cid.Decode(arg)
if err != nil {
goto ERROR
}
m.pinMap.Rm(c)
resp := mockPinResp{
Pins: []string{cidStr},
Pins: []string{arg},
}
j, _ := json.Marshal(resp)
w.Write(j)
case "pin/ls":
query := r.URL.Query()
arg, ok := query["arg"]
arg, ok := extractCid(r.URL)
if !ok {
rMap := make(map[string]mockPinType)
pins := m.pinMap.List()
@ -187,11 +181,8 @@ func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
w.Write(j)
break
}
if len(arg) != 1 {
goto ERROR
}
cidStr = arg[0]
cidStr := arg
c, err := cid.Decode(cidStr)
if err != nil {
goto ERROR
@ -209,12 +200,11 @@ func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
w.Write(j)
}
case "swarm/connect":
query := r.URL.Query()
arg, ok := query["arg"]
arg, ok := extractCid(r.URL)
if !ok {
goto ERROR
}
addr := arg[0]
addr := arg
splits := strings.Split(addr, "/")
pid := splits[len(splits)-1]
resp := struct {
@ -256,13 +246,12 @@ func (m *IpfsMock) handler(w http.ResponseWriter, r *http.Request) {
j, _ := json.Marshal(resp)
w.Write(j)
case "refs":
query := r.URL.Query()
arg, ok := query["arg"]
arg, ok := extractCid(r.URL)
if !ok {
goto ERROR
}
resp := mockRefsResp{
Ref: arg[0],
Ref: arg,
}
j, _ := json.Marshal(resp)
w.Write(j)
@ -281,3 +270,20 @@ ERROR:
func (m *IpfsMock) Close() {
m.server.Close()
}
// extractCid extracts the cid argument from a url.URL, either via
// the query string parameters or from the url path itself.
func extractCid(u *url.URL) (string, bool) {
arg := u.Query().Get("arg")
if arg != "" {
return arg, true
}
p := strings.TrimPrefix(u.Path, "/api/v0/")
segs := strings.Split(p, "/")
if len(segs) > 2 {
return segs[len(segs)-1], true
}
return "", false
}