Skip to content

Commit 53f405f

Browse files
authored
chore: upgrade Go to 1.22, adapt to http server URL changes (#494)
Strange behaviour causing e2e test failures when upgrading to 1.22, diagnosed as a different form that an HTTP server decodes a request and doesn't fully unescape its `Path` when it has special characters in it. This seems to be related to the new ServeMux changes in 1.22 because GODEBUG=httpmuxgo121=1 reverts the behaviour and the test passes. The fix is to explicitly unescape the path before passing it on, go-trustless-utils will re-escape itself when it builds a new request to pass on downstream, but in the meantime we need to know the exact unescaped path so we can traverse it to validate the incoming data as well as pass it on upstream to the remote.
1 parent 6ccf6ac commit 53f405f

File tree

3 files changed

+27
-14
lines changed

3 files changed

+27
-14
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/filecoin-project/lassie
22

3-
go 1.21
3+
go 1.22
44

55
require (
66
github.com/benbjohnson/clock v1.3.5

pkg/internal/itest/e2e_test.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"path/filepath"
1313
"runtime"
14+
"strings"
1415
"testing"
1516
"time"
1617

@@ -54,11 +55,14 @@ func TestTrustlessGatewayE2E(t *testing.T) {
5455
req.NoError(err)
5556

5657
// install the indexer to announce to
58+
t.Log("Installing indexer")
5759
indexer := filepath.Join(tr.Dir, "storetheindex")
58-
tr.Run("go", "install", "github.com/ipni/storetheindex@latest")
60+
tr.Run("go", "install", "github.com/ipni/storetheindex@v0.8.14")
5961
// install the ipni cli to inspect the indexer
62+
t.Log("Installing ipni")
6063
ipni := filepath.Join(tr.Dir, "ipni")
61-
tr.Run("go", "install", "github.com/ipni/ipni-cli/cmd/ipni@latest")
64+
tr.Run("go", "install", "github.com/ipni/ipni-cli/cmd/[email protected]")
65+
t.Log("Installing frisbii")
6266
// install frisbii to serve the content
6367
frisbii := filepath.Join(tr.Dir, "frisbii")
6468
tr.Run("go", "install", "github.com/ipld/frisbii/cmd/frisbii@latest")
@@ -67,7 +71,10 @@ func TestTrustlessGatewayE2E(t *testing.T) {
6771
req.NoError(err)
6872

6973
// initialise and start the indexer and adjust the config
74+
t.Log("Initialising indexer")
7075
tr.Run(indexer, "init", "--store", "pebble", "--pubsub-topic", "/indexer/ingest/mainnet", "--no-bootstrap")
76+
77+
t.Log("Starting indexer")
7178
indexerReady := test.NewStdoutWatcher(test.IndexerReadyMatch)
7279
cmdIndexer := tr.Start(test.NewExecution(indexer, "daemon").WithWatcher(indexerReady))
7380
select {
@@ -82,6 +89,7 @@ func TestTrustlessGatewayE2E(t *testing.T) {
8289
carPath := trustlesspathing.Unixfs20mVarietyCARPath()
8390

8491
// start frisbii with the fixture CAR
92+
t.Logf("Starting frisbii with CAR [%s] and root [%s]", carPath, root)
8593
frisbiiReady := test.NewStdoutWatcher("Announce() complete")
8694
cmdFrisbii := tr.Start(test.NewExecution(frisbii,
8795
"--listen", "localhost:37471",
@@ -101,17 +109,13 @@ func TestTrustlessGatewayE2E(t *testing.T) {
101109
req.Eventually(func() bool {
102110
mh := root.Hash().B58String()
103111
findOutput := tr.Run(ipni, "find", "--no-priv", "-i", "http://localhost:3000", "-mh", mh)
104-
t.Logf("import output:\n%s\n", findOutput)
105-
106112
if bytes.Contains(findOutput, []byte("not found")) {
107113
return false
108114
}
109115
if !bytes.Contains(findOutput, []byte("Provider:")) {
110116
t.Logf("mh %s: unexpected error: %s", mh, findOutput)
111117
return false
112118
}
113-
114-
t.Logf("mh %s: found", mh)
115119
return true
116120
}, 10*time.Second, time.Second)
117121

@@ -172,6 +176,7 @@ func TestTrustlessGatewayE2E(t *testing.T) {
172176
if testCase.Path != "" {
173177
args[len(args)-1] = args[len(args)-1] + "/" + testCase.Path
174178
}
179+
t.Logf("Running lassie %s", strings.Join(args, " "))
175180
tr.Run(lassie, args...)
176181

177182
_, err = os.Stat(expectedCarPath)
@@ -188,6 +193,8 @@ func TestTrustlessGatewayE2E(t *testing.T) {
188193
reqUrl, err := url.Parse("http://localhost:30000/" + testCase.AsQuery())
189194
req.NoError(err)
190195

196+
t.Logf("Fetching %s", reqUrl.String())
197+
191198
// download and read all body from URL along with Accept:application/vnd.ipld.car header
192199
reqReq, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl.String(), nil)
193200
req.NoError(err)

pkg/server/http/ipfs.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8+
"net/url"
89
"strconv"
910
"strings"
1011

@@ -24,13 +25,18 @@ import (
2425

2526
func IpfsHandler(fetcher types.Fetcher, cfg HttpServerConfig) func(http.ResponseWriter, *http.Request) {
2627
return func(res http.ResponseWriter, req *http.Request) {
27-
statusLogger := newStatusLogger(req.Method, req.URL.Path)
28+
unescapedPath, err := url.PathUnescape(req.URL.Path)
29+
if err != nil {
30+
logger.Warnf("error unescaping path: %s", err)
31+
unescapedPath = req.URL.Path
32+
}
33+
statusLogger := newStatusLogger(req.Method, unescapedPath)
2834

2935
if !checkGet(req, res, statusLogger) {
3036
return
3137
}
3238

33-
ok, request := decodeRetrievalRequest(cfg, res, req, statusLogger)
39+
ok, request := decodeRetrievalRequest(cfg, res, req, unescapedPath, statusLogger)
3440
if !ok {
3541
return
3642
}
@@ -87,7 +93,7 @@ func IpfsHandler(fetcher types.Fetcher, cfg HttpServerConfig) func(http.Response
8793
res.Header().Set("Content-Type", trustlesshttp.DefaultContentType().WithDuplicates(request.Duplicates).String())
8894
res.Header().Set("Etag", request.Etag())
8995
res.Header().Set("X-Content-Type-Options", "nosniff")
90-
res.Header().Set("X-Ipfs-Path", trustlessutils.PathEscape(req.URL.Path))
96+
res.Header().Set("X-Ipfs-Path", trustlessutils.PathEscape(unescapedPath))
9197
res.Header().Set("X-Trace-Id", requestId)
9298
statusLogger.logStatus(200, "OK")
9399
close(bytesWritten)
@@ -152,8 +158,8 @@ func checkGet(req *http.Request, res http.ResponseWriter, statusLogger *statusLo
152158
return false
153159
}
154160

155-
func decodeRequest(res http.ResponseWriter, req *http.Request, statusLogger *statusLogger) (bool, trustlessutils.Request) {
156-
rootCid, path, err := trustlesshttp.ParseUrlPath(req.URL.Path)
161+
func decodeRequest(res http.ResponseWriter, req *http.Request, unescapedPath string, statusLogger *statusLogger) (bool, trustlessutils.Request) {
162+
rootCid, path, err := trustlesshttp.ParseUrlPath(unescapedPath)
157163
if err != nil {
158164
if errors.Is(err, trustlesshttp.ErrPathNotFound) {
159165
errorResponse(res, statusLogger, http.StatusNotFound, err)
@@ -205,8 +211,8 @@ func decodeRequest(res http.ResponseWriter, req *http.Request, statusLogger *sta
205211
}
206212
}
207213

208-
func decodeRetrievalRequest(cfg HttpServerConfig, res http.ResponseWriter, req *http.Request, statusLogger *statusLogger) (bool, types.RetrievalRequest) {
209-
ok, request := decodeRequest(res, req, statusLogger)
214+
func decodeRetrievalRequest(cfg HttpServerConfig, res http.ResponseWriter, req *http.Request, unescapedPath string, statusLogger *statusLogger) (bool, types.RetrievalRequest) {
215+
ok, request := decodeRequest(res, req, unescapedPath, statusLogger)
210216
if !ok {
211217
return false, types.RetrievalRequest{}
212218
}

0 commit comments

Comments
 (0)