From 6829ed68657f7dee018008b0f2fe3b4a489e30bd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 15 May 2023 12:09:42 +0200 Subject: [PATCH 1/2] fix: add newline in NDJSON responses --- routing/http/server/server.go | 7 ++++ routing/http/server/server_test.go | 56 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/routing/http/server/server.go b/routing/http/server/server.go index 8ce7d063b..d2c7f5221 100644 --- a/routing/http/server/server.go +++ b/routing/http/server/server.go @@ -254,6 +254,13 @@ func (s *server) findProvidersNDJSON(w http.ResponseWriter, provIter iter.Result logger.Warn("FindProviders ndjson write error", "Error", err) return } + + _, err = w.Write([]byte{'\n'}) + if err != nil { + logger.Warn("FindProviders ndjson write error", "Error", err) + return + } + if f, ok := w.(http.Flusher); ok { f.Flush() } diff --git a/routing/http/server/server_test.go b/routing/http/server/server_test.go index fec5eaf9a..a1e0892d8 100644 --- a/routing/http/server/server_test.go +++ b/routing/http/server/server_test.go @@ -2,6 +2,7 @@ package server import ( "context" + "io" "net/http" "net/http/httptest" "testing" @@ -10,6 +11,7 @@ import ( "github.com/ipfs/boxo/routing/http/types" "github.com/ipfs/boxo/routing/http/types/iter" "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -48,6 +50,60 @@ func TestHeaders(t *testing.T) { require.Equal(t, "text/plain; charset=utf-8", header) } +func TestResponse(t *testing.T) { + pidStr := "12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn" + cidStr := "bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4" + + pid, err := peer.Decode(pidStr) + require.NoError(t, err) + + cid, err := cid.Decode(cidStr) + require.NoError(t, err) + + runTest := func(t *testing.T, contentType string, expected string) { + t.Parallel() + + results := iter.FromSlice([]iter.Result[types.ProviderResponse]{ + {Val: &types.ReadBitswapProviderRecord{ + Protocol: "transport-bitswap", + Schema: types.SchemaBitswap, + ID: &pid, + Addrs: []types.Multiaddr{}, + }}}, + ) + + router := &mockContentRouter{} + server := httptest.NewServer(Handler(router)) + t.Cleanup(server.Close) + serverAddr := "http://" + server.Listener.Addr().String() + router.On("FindProviders", mock.Anything, cid).Return(results, nil) + urlStr := serverAddr + ProvidePath + cidStr + + req, err := http.NewRequest(http.MethodGet, urlStr, nil) + require.NoError(t, err) + req.Header.Set("Accept", contentType) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + header := resp.Header.Get("Content-Type") + require.Equal(t, contentType, header) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + require.Equal(t, string(body), expected) + } + + t.Run("JSON Response", func(t *testing.T) { + runTest(t, mediaTypeJSON, `{"Providers":[{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]}]}`) + }) + + t.Run("NDJSON Response", func(t *testing.T) { + runTest(t, mediaTypeNDJSON, `{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]}`+"\n") + }) +} + type mockContentRouter struct{ mock.Mock } func (m *mockContentRouter) FindProviders(ctx context.Context, key cid.Cid) (iter.ResultIter[types.ProviderResponse], error) { From 1870df4149e01cc5a6f7b37c131012d23585281d Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 16 May 2023 14:30:39 +0200 Subject: [PATCH 2/2] test(ndjson): test more than one entry --- routing/http/server/server_test.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/routing/http/server/server_test.go b/routing/http/server/server_test.go index a1e0892d8..6e7d4ba9f 100644 --- a/routing/http/server/server_test.go +++ b/routing/http/server/server_test.go @@ -52,10 +52,13 @@ func TestHeaders(t *testing.T) { func TestResponse(t *testing.T) { pidStr := "12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn" + pid2Str := "12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz" cidStr := "bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4" pid, err := peer.Decode(pidStr) require.NoError(t, err) + pid2, err := peer.Decode(pid2Str) + require.NoError(t, err) cid, err := cid.Decode(cidStr) require.NoError(t, err) @@ -69,6 +72,12 @@ func TestResponse(t *testing.T) { Schema: types.SchemaBitswap, ID: &pid, Addrs: []types.Multiaddr{}, + }}, + {Val: &types.ReadBitswapProviderRecord{ + Protocol: "transport-bitswap", + Schema: types.SchemaBitswap, + ID: &pid2, + Addrs: []types.Multiaddr{}, }}}, ) @@ -96,11 +105,11 @@ func TestResponse(t *testing.T) { } t.Run("JSON Response", func(t *testing.T) { - runTest(t, mediaTypeJSON, `{"Providers":[{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]}]}`) + runTest(t, mediaTypeJSON, `{"Providers":[{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]},{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz","Addrs":[]}]}`) }) t.Run("NDJSON Response", func(t *testing.T) { - runTest(t, mediaTypeNDJSON, `{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]}`+"\n") + runTest(t, mediaTypeNDJSON, `{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Addrs":[]}`+"\n"+`{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz","Addrs":[]}`+"\n") }) }