Skip to content

Commit 05037ea

Browse files
authored
rpc: handle wrong HTTP batch response length (#26064)
1 parent 4a81e5a commit 05037ea

File tree

3 files changed

+52
-0
lines changed

3 files changed

+52
-0
lines changed

rpc/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
)
3333

3434
var (
35+
ErrBadResult = errors.New("bad result in JSON-RPC response")
3536
ErrClientQuit = errors.New("client is closed")
3637
ErrNoResult = errors.New("no result in JSON-RPC response")
3738
ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow")

rpc/client_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package rpc
1919
import (
2020
"context"
2121
"encoding/json"
22+
"errors"
2223
"fmt"
2324
"math/rand"
2425
"net"
@@ -148,6 +149,53 @@ func TestClientBatchRequest(t *testing.T) {
148149
}
149150
}
150151

152+
func TestClientBatchRequest_len(t *testing.T) {
153+
b, err := json.Marshal([]jsonrpcMessage{
154+
{Version: "2.0", ID: json.RawMessage("1"), Method: "foo", Result: json.RawMessage(`"0x1"`)},
155+
{Version: "2.0", ID: json.RawMessage("2"), Method: "bar", Result: json.RawMessage(`"0x2"`)},
156+
})
157+
if err != nil {
158+
t.Fatal("failed to encode jsonrpc message:", err)
159+
}
160+
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
161+
_, err := rw.Write(b)
162+
if err != nil {
163+
t.Error("failed to write response:", err)
164+
}
165+
}))
166+
t.Cleanup(s.Close)
167+
168+
client, err := Dial(s.URL)
169+
if err != nil {
170+
t.Fatal("failed to dial test server:", err)
171+
}
172+
defer client.Close()
173+
174+
t.Run("too-few", func(t *testing.T) {
175+
batch := []BatchElem{
176+
{Method: "foo"},
177+
{Method: "bar"},
178+
{Method: "baz"},
179+
}
180+
ctx, cancelFn := context.WithTimeout(context.Background(), time.Second)
181+
defer cancelFn()
182+
if err := client.BatchCallContext(ctx, batch); !errors.Is(err, ErrBadResult) {
183+
t.Errorf("expected %q but got: %v", ErrBadResult, err)
184+
}
185+
})
186+
187+
t.Run("too-many", func(t *testing.T) {
188+
batch := []BatchElem{
189+
{Method: "foo"},
190+
}
191+
ctx, cancelFn := context.WithTimeout(context.Background(), time.Second)
192+
defer cancelFn()
193+
if err := client.BatchCallContext(ctx, batch); !errors.Is(err, ErrBadResult) {
194+
t.Errorf("expected %q but got: %v", ErrBadResult, err)
195+
}
196+
})
197+
}
198+
151199
func TestClientNotify(t *testing.T) {
152200
server := newTestServer()
153201
defer server.Stop()

rpc/http.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonr
192192
if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil {
193193
return err
194194
}
195+
if len(respmsgs) != len(msgs) {
196+
return fmt.Errorf("batch has %d requests but response has %d: %w", len(msgs), len(respmsgs), ErrBadResult)
197+
}
195198
for i := 0; i < len(respmsgs); i++ {
196199
op.resp <- &respmsgs[i]
197200
}

0 commit comments

Comments
 (0)