From 0116be0a2b90db3d6bfa9909738ca2ed137b5be2 Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 16 Oct 2026 16:39:00 -0400 Subject: [PATCH] Treat a zero WebSocket payload limit as none CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH = 0 rejected every frame with a payload, while 0 means no limit for payload_max_length. Make it mean the same here, and refuse a length that size_t cannot hold. --- README-websocket.md | 2 +- httplib.h | 9 +++++++-- test/test.cc | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/README-websocket.md b/README-websocket.md index 06d77844..223af7b7 100644 --- a/README-websocket.md +++ b/README-websocket.md @@ -435,7 +435,7 @@ if (ws.connect()) { | Macro | Default | Description | |---------------------------------------------|-------------------|----------------------------------------------------------| -| `CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH` | `16777216` (16MB) | Maximum payload size per message | +| `CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH` | `16777216` (16MB) | Maximum payload size per message (`0` means no limit) | | `CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND` | `0` | Client read timeout (seconds); `0` waits forever | | `CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND` | `300` | Server read timeout (seconds) | | `CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND` | `5` | Timeout for waiting peer's Close response (seconds) | diff --git a/httplib.h b/httplib.h index ac094c8d..91e2246d 100644 --- a/httplib.h +++ b/httplib.h @@ -5758,7 +5758,11 @@ inline FrameRead read_websocket_frame(Stream &strm, Opcode &opcode, } } - if (payload_len > max_len) { return FrameRead::Fail; } + // A max_len of 0 means no limit, as payload_max_length does. + if ((max_len > 0 && payload_len > max_len) || + payload_len > (std::numeric_limits::max)()) { + return FrameRead::Fail; + } // Read mask key if present uint8_t mask_key[4] = {0}; @@ -22887,7 +22891,8 @@ inline ReadResult WebSocket::read(std::string &msg) { return Fail; } msg += cont_payload; - if (msg.size() > CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH) { + if (CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH > 0 && + msg.size() > CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH) { closed_ = true; return Fail; } diff --git a/test/test.cc b/test/test.cc index f37f8e60..487d3802 100644 --- a/test/test.cc +++ b/test/test.cc @@ -25811,6 +25811,26 @@ TEST(WebSocketFrameTest, MultiByteFieldsSplitAcrossReads) { EXPECT_EQ(payload, body); } +TEST(WebSocketFrameTest, ZeroMaxLenMeansNoLimit) { + std::string body(2000, 'x'); + std::string frame; + frame += static_cast(0x82); // FIN + Binary + frame += static_cast(126); // 16-bit length follows + frame += static_cast(body.size() >> 8); + frame += static_cast(body.size() & 0xff); + frame += body; + + detail::BufferStream strm; + strm.write(frame.data(), frame.size()); + ws::Opcode opcode; + std::string payload; + bool fin; + EXPECT_EQ( + ws::impl::read_websocket_frame(strm, opcode, payload, fin, false, 0), + ws::impl::FrameRead::Ok); + EXPECT_EQ(body, payload); +} + TEST(WebSocketFrameTest, PayloadGrowsOnlyAsItArrives) { // The header announces the largest payload allowed, and only 3 bytes of it // ever arrive.