With permessage-deflate, a message whose decompressed size exceeds max_size by 1-2 bytes is silently truncated to max_size instead of being rejected with PayloadTooBig. Larger overages are rejected correctly - the unconsumed_tail check misses the case where all compressed input was consumed while output is still pending in the decompressor.
from websockets.client import ClientProtocol
from websockets.extensions.permessage_deflate import ClientPerMessageDeflateFactory, ServerPerMessageDeflateFactory
from websockets.server import ServerProtocol
from websockets.uri import parse_uri
MAX_SIZE = 2**24
client = ClientProtocol(parse_uri("ws://localhost/"), extensions=[ClientPerMessageDeflateFactory()])
server = ServerProtocol(extensions=[ServerPerMessageDeflateFactory()], max_size=MAX_SIZE)
client.send_request(client.connect())
server.receive_data(b"".join(client.data_to_send()))
server.send_response(server.accept(server.events_received()[0]))
client.receive_data(b"".join(server.data_to_send()))
client.events_received()
client.send_binary(b"\x01" * (MAX_SIZE + 1)) # one byte over max_size
server.receive_data(b"".join(client.data_to_send()))
print(server.parser_exc) # None - expected PayloadTooBig
print(len(server.events_received()[0].data)) # 16777216 - silently truncated
Reproduced on 16.0. Found while moving Uvicorn off the legacy implementation: Kludex/uvicorn#2985.
With permessage-deflate, a message whose decompressed size exceeds
max_sizeby 1-2 bytes is silently truncated tomax_sizeinstead of being rejected withPayloadTooBig. Larger overages are rejected correctly - theunconsumed_tailcheck misses the case where all compressed input was consumed while output is still pending in the decompressor.Reproduced on 16.0. Found while moving Uvicorn off the legacy implementation: Kludex/uvicorn#2985.