From 9b8e63bf94a3f1cad759d3de942ffde77557bc54 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 16 May 2023 14:51:33 +0200 Subject: [PATCH] fix(routing/v1): add newline in NDJSON responses (#300) Co-authored-by: Marcin Rataj --- routing/http/server/server.go | 7 ++++ routing/http/server/server_test.go | 65 ++++++++++++++++++++++++++++++ 2 files changed, 72 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..6e7d4ba9f 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,69 @@ func TestHeaders(t *testing.T) { require.Equal(t, "text/plain; charset=utf-8", header) } +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) + + 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{}, + }}, + {Val: &types.ReadBitswapProviderRecord{ + Protocol: "transport-bitswap", + Schema: types.SchemaBitswap, + ID: &pid2, + 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":[]},{"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"+`{"Protocol":"transport-bitswap","Schema":"bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz","Addrs":[]}`+"\n") + }) +} + type mockContentRouter struct{ mock.Mock } func (m *mockContentRouter) FindProviders(ctx context.Context, key cid.Cid) (iter.ResultIter[types.ProviderResponse], error) {