mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
Expose SetReadLimits for Websocket server
This commit is contained in:
parent
21ee50facc
commit
134c18e992
3 changed files with 126 additions and 4 deletions
|
|
@ -52,6 +52,7 @@ type Server struct {
|
||||||
run atomic.Bool
|
run atomic.Bool
|
||||||
batchItemLimit int
|
batchItemLimit int
|
||||||
batchResponseLimit int
|
batchResponseLimit int
|
||||||
|
readerLimit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new server instance with no registered handlers.
|
// NewServer creates a new server instance with no registered handlers.
|
||||||
|
|
@ -60,6 +61,7 @@ func NewServer() *Server {
|
||||||
idgen: randomIDGenerator(),
|
idgen: randomIDGenerator(),
|
||||||
codecs: make(map[ServerCodec]struct{}),
|
codecs: make(map[ServerCodec]struct{}),
|
||||||
denyList: make(map[string]struct{}),
|
denyList: make(map[string]struct{}),
|
||||||
|
readerLimit: wsDefaultReadLimit,
|
||||||
}
|
}
|
||||||
server.run.Store(true)
|
server.run.Store(true)
|
||||||
// Register the default service providing meta information about the RPC service such
|
// Register the default service providing meta information about the RPC service such
|
||||||
|
|
@ -80,6 +82,12 @@ func (s *Server) SetBatchLimits(itemLimit, maxResponseSize int) {
|
||||||
s.batchResponseLimit = maxResponseSize
|
s.batchResponseLimit = maxResponseSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetReadLimits sets the limit for max message size to limit the request message size.
|
||||||
|
// This would be useful to prevent the server from OOM by taking too large messages.
|
||||||
|
func (s *Server) SetReadLimits(maxMessageSize int64) {
|
||||||
|
s.readerLimit = maxMessageSize
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterName creates a service for the given receiver type under the given name. When no
|
// RegisterName creates a service for the given receiver type under the given name. When no
|
||||||
// methods on the given receiver match the criteria to be either a RPC method or a
|
// methods on the given receiver match the criteria to be either a RPC method or a
|
||||||
// subscription an error is returned. Otherwise a new service is created and added to the
|
// subscription an error is returned. Otherwise a new service is created and added to the
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ package rpc
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -192,3 +194,115 @@ func TestServerBatchResponseSizeLimit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServerSetReadLimits(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Test different read limits
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
readLimit int64
|
||||||
|
testSize int
|
||||||
|
shouldFail bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "small limit with small request - should succeed",
|
||||||
|
readLimit: 2048,
|
||||||
|
testSize: 500, // Small request data
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "small limit with large request - should fail",
|
||||||
|
readLimit: 2048,
|
||||||
|
testSize: 5000, // Large request data that should exceed limit
|
||||||
|
shouldFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "medium limit with medium request - should succeed",
|
||||||
|
readLimit: 10240,
|
||||||
|
testSize: 5000, // Medium request data
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "medium limit with large request - should fail",
|
||||||
|
readLimit: 10240,
|
||||||
|
testSize: 20000, // Large request data
|
||||||
|
shouldFail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "large limit with large request - should succeed",
|
||||||
|
readLimit: 50000,
|
||||||
|
testSize: 20000, // Large request data that should fit
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// Create server and set read limits
|
||||||
|
srv := newTestServer()
|
||||||
|
srv.SetReadLimits(tc.readLimit)
|
||||||
|
defer srv.Stop()
|
||||||
|
|
||||||
|
// Start HTTP server with WebSocket handler
|
||||||
|
httpsrv := httptest.NewServer(srv.WebsocketHandler([]string{"*"}))
|
||||||
|
defer httpsrv.Close()
|
||||||
|
|
||||||
|
wsURL := "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
|
||||||
|
|
||||||
|
// Connect WebSocket client
|
||||||
|
client, err := DialOptions(context.Background(), wsURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't dial: %v", err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// Create large request data - this is what will be limited
|
||||||
|
largeString := strings.Repeat("A", tc.testSize)
|
||||||
|
|
||||||
|
// Send the large string as a parameter in the request
|
||||||
|
var result echoResult
|
||||||
|
err = client.Call(&result, "test_echo", largeString, 42, &echoArgs{S: "test"})
|
||||||
|
|
||||||
|
if tc.shouldFail {
|
||||||
|
// Expecting an error due to read limit exceeded
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error for request size %d with limit %d, but got none", tc.testSize, tc.readLimit)
|
||||||
|
}
|
||||||
|
// Check if it's the expected message size limit error
|
||||||
|
if !strings.Contains(err.Error(), "message too big") {
|
||||||
|
t.Fatalf("expected 'message too big' error, got: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Expecting success
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error for request size %d with limit %d: %v", tc.testSize, tc.readLimit, err)
|
||||||
|
}
|
||||||
|
// Verify the response is correct - the echo should return our string
|
||||||
|
if result.String != largeString {
|
||||||
|
t.Fatalf("expected echo result to match input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that SetReadLimits properly updates the server's readerLimit field
|
||||||
|
func TestServerSetReadLimitsField(t *testing.T) {
|
||||||
|
server := NewServer()
|
||||||
|
|
||||||
|
// Test initial default value
|
||||||
|
if server.readerLimit != wsDefaultReadLimit {
|
||||||
|
t.Errorf("expected initial readerLimit to be %d, got %d", wsDefaultReadLimit, server.readerLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setting different values
|
||||||
|
testValues := []int64{1024, 10240, 102400, 1048576}
|
||||||
|
|
||||||
|
for _, expectedLimit := range testValues {
|
||||||
|
server.SetReadLimits(expectedLimit)
|
||||||
|
if server.readerLimit != expectedLimit {
|
||||||
|
t.Errorf("expected readerLimit to be %d after SetReadLimits, got %d", expectedLimit, server.readerLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
|
||||||
log.Debug("WebSocket upgrade failed", "err", err)
|
log.Debug("WebSocket upgrade failed", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
codec := newWebsocketCodec(conn, r.Host, r.Header, wsDefaultReadLimit)
|
codec := newWebsocketCodec(conn, r.Host, r.Header, s.readerLimit)
|
||||||
s.ServeCodec(codec, 0)
|
s.ServeCodec(codec, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue