@cryptotaxi247 / kubo / commits / b85aa9639

fix: make Block().* return correct ABI based ipld.ErrNotFound errors

This commit was moved from ipfs/go-ipfs-http-client@4f5f8e9b144d1f38e0fc37c5080845611dcbcf1e

Jorropo committed Mar 27, 2022 at 14:11 UTC b85aa9639f861d659b49e0396b409398462226a2
3 files changed +167 -8
client/httpapi/abyfy_errors.go new
+104
@@ -0,0 +1,104 @@
1 +package httpapi
2 +
3 +import (
4 + "errors"
5 + "strings"
6 +
7 + "github.com/ipfs/go-cid"
8 + ipld "github.com/ipfs/go-ipld-format"
9 +)
10 +
11 +type prePostWrappedNotFoundError struct {
12 + pre string
13 + post string
14 +
15 + wrapped ipld.ErrNotFound
16 +}
17 +
18 +func (e prePostWrappedNotFoundError) String() string {
19 + return e.Error()
20 +}
21 +
22 +func (e prePostWrappedNotFoundError) Error() string {
23 + return e.pre + e.wrapped.Error() + e.post
24 +}
25 +
26 +func (e prePostWrappedNotFoundError) Unwrap() error {
27 + return e.wrapped
28 +}
29 +
30 +func abyfyIpldNotFoundFallbackToMSG(msg string) error {
31 + err, handled := abyfyIpldNotFound(msg)
32 + if handled {
33 + return err
34 + }
35 +
36 + return errors.New(msg)
37 +}
38 +
39 +func abyfyIpldNotFoundFallbackToError(msg error) error {
40 + err, handled := abyfyIpldNotFound(msg.Error())
41 + if handled {
42 + return err
43 + }
44 +
45 + return msg
46 +}
47 +
48 +// This file handle parsing and returning the correct ABI based errors from error messages
49 +//lint:ignore ST1008 this function is not using the error as a mean to return failure but it massages it to return the correct type
50 +func abyfyIpldNotFound(msg string) (error, bool) {
51 + if msg == "" {
52 + return nil, true // Fast path
53 + }
54 +
55 + // The patern we search for is:
56 + // node not found (fallback)
57 + // or
58 + // CID not found (here we parse the CID)
59 + notFoundIndex := strings.LastIndex(msg, " not found")
60 +
61 + if notFoundIndex == -1 {
62 + // Unknown, ot found not found
63 + return nil, false
64 + }
65 +
66 + preNotFound := msg[:notFoundIndex]
67 +
68 + var c cid.Cid
69 + var preIndex int
70 + if strings.HasSuffix(preNotFound, "node") {
71 + // Fallback case
72 + c = cid.Undef
73 + preIndex = notFoundIndex - len("node")
74 + } else {
75 + // Assume that CIDs does not include whitespace to pull out the CID
76 + preIndex = strings.LastIndexByte(preNotFound, ' ')
77 + // + 1 is to normalise not founds to zeros and point to the start of the CID, not the previous space
78 + preIndex++
79 + var err error
80 + c, err = cid.Decode(preNotFound[preIndex:])
81 + if err != nil {
82 + // Unknown
83 + return nil, false
84 + }
85 + }
86 +
87 + postIndex := notFoundIndex + len(" not found")
88 +
89 + err := ipld.ErrNotFound{Cid: c}
90 +
91 + pre := msg[:preIndex]
92 + post := msg[postIndex:]
93 +
94 + if len(pre) > 0 || len(post) > 0 {
95 + // We have some text to wrap arround the ErrNotFound one
96 + return prePostWrappedNotFoundError{
97 + pre: pre,
98 + post: post,
99 + wrapped: err,
100 + }, true
101 + }
102 +
103 + return err, true
104 +}
client/httpapi/abyfy_errors_test.go new
+60
@@ -0,0 +1,60 @@
1 +package httpapi
2 +
3 +import (
4 + "errors"
5 + "fmt"
6 + "testing"
7 +
8 + "github.com/ipfs/go-cid"
9 + ipld "github.com/ipfs/go-ipld-format"
10 + mh "github.com/multiformats/go-multihash"
11 +)
12 +
13 +var randomSha256MH = mh.Multihash{0x12, 0x20, 0x88, 0x82, 0x73, 0x37, 0x7c, 0xc1, 0xc9, 0x96, 0xad, 0xee, 0xd, 0x26, 0x84, 0x2, 0xc9, 0xc9, 0x5c, 0xf9, 0x5c, 0x4d, 0x9b, 0xc3, 0x3f, 0xfb, 0x4a, 0xd8, 0xaf, 0x28, 0x6b, 0xca, 0x1a, 0xf2}
14 +
15 +func doAbyfyIpldNotFoundTest(t *testing.T, original error) {
16 + originalMsg := original.Error()
17 +
18 + rebuilt := abyfyIpldNotFoundFallbackToMSG(originalMsg)
19 +
20 + rebuiltMsg := rebuilt.Error()
21 +
22 + if originalMsg != rebuiltMsg {
23 + t.Errorf("expected message to be %q; got %q", originalMsg, rebuiltMsg)
24 + }
25 +
26 + originalNotFound := ipld.IsNotFound(original)
27 + rebuiltNotFound := ipld.IsNotFound(original)
28 + if originalNotFound != rebuiltNotFound {
29 + t.Errorf("expected Ipld.IsNotFound to be %t; got %t", originalNotFound, rebuiltNotFound)
30 + }
31 +}
32 +
33 +func TestAbyfyIpldNotFound(t *testing.T) {
34 + if err := abyfyIpldNotFoundFallbackToMSG(""); err != nil {
35 + t.Errorf("expected empty string to give no error; got %T %q", err, err.Error())
36 + }
37 +
38 + for _, wrap := range [...]string{
39 + "",
40 + "merkledag: %w",
41 + "testing: %w the test",
42 + "%w is wrong",
43 + } {
44 + for _, err := range [...]error{
45 + errors.New("file not found"),
46 + errors.New(" not found"),
47 + errors.New("Bad_CID not found"),
48 + errors.New("network connection timeout"),
49 + ipld.ErrNotFound{Cid: cid.Undef},
50 + ipld.ErrNotFound{Cid: cid.NewCidV0(randomSha256MH)},
51 + ipld.ErrNotFound{Cid: cid.NewCidV1(cid.Raw, randomSha256MH)},
52 + } {
53 + if wrap != "" {
54 + err = fmt.Errorf(wrap, err)
55 + }
56 +
57 + doAbyfyIpldNotFoundTest(t, err)
58 + }
59 + }
60 +}
client/httpapi/block.go
+3 -8
@@ -3,7 +3,6 @@ package httpapi
3 import (
4 "bytes"
5 "context"
6 - "errors"
6 "fmt"
7 "io"
8
@@ -67,7 +66,7 @@ func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) {
66 return nil, err
67 }
68 if resp.Error != nil {
70 - return nil, resp.Error
69 + return nil, abyfyIpldNotFoundFallbackToError(resp.Error)
70 }
71
72 //TODO: make get return ReadCloser to avoid copying
@@ -99,18 +98,14 @@ func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRm
98 return err
99 }
100
102 - if removedBlock.Error != "" {
103 - return errors.New(removedBlock.Error)
104 - }
105 -
106 - return nil
101 + return abyfyIpldNotFoundFallbackToMSG(removedBlock.Error)
102 }
103
104 func (api *BlockAPI) Stat(ctx context.Context, p path.Path) (iface.BlockStat, error) {
105 var out blockStat
106 err := api.core().Request("block/stat", p.String()).Exec(ctx, &out)
107 if err != nil {
113 - return nil, err
108 + return nil, abyfyIpldNotFoundFallbackToError(err)
109 }
110 out.cid, err = cid.Parse(out.Key)
111 if err != nil {