Skip to content

Commit 211dbb7

Browse files
jmank88fjl
authored andcommitted
rpc: handle wrong HTTP batch response length (#26064)
1 parent 27600a5 commit 211dbb7

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
@@ -31,6 +31,7 @@ import (
3131
)
3232

3333
var (
34+
ErrBadResult = errors.New("bad result in JSON-RPC response")
3435
ErrClientQuit = errors.New("client is closed")
3536
ErrNoResult = errors.New("no result in JSON-RPC response")
3637
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"
@@ -144,6 +145,53 @@ func TestClientBatchRequest(t *testing.T) {
144145
}
145146
}
146147

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

rpc/http.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonr
173173
if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil {
174174
return err
175175
}
176+
if len(respmsgs) != len(msgs) {
177+
return fmt.Errorf("batch has %d requests but response has %d: %w", len(msgs), len(respmsgs), ErrBadResult)
178+
}
176179
for i := 0; i < len(respmsgs); i++ {
177180
op.resp <- &respmsgs[i]
178181
}

0 commit comments

Comments
 (0)