[
https://issues.apache.org/jira/browse/THRIFT-6178?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Jens Geyer updated THRIFT-6178:
-------------------------------
Description:
TWebSocketServer::readFrame reads a frame's payload with a single call to the
underlying transport and treats anything short of the whole payload as end of
stream (lib/cpp/src/thrift/transport/TWebSocketServer.h):
{code}
readBuffer_.resetBuffer(length);
uint8_t* buffer = readBuffer_.getWritePtr(length);
read = transport_->read(buffer, length);
readBuffer_.wroteBytes(read);
if (read < length) {
return false;
}
{code}
TSocket::read performs one recv() and returns whatever that produced, and the
transport factory hands TWebSocketServer the accepted TSocket directly, so the
payload arrives in as many pieces as the network chose to split it into.
Returning false makes readAll_virt return 0, which the caller reads as a closed
connection.
The effect is that no frame whose payload does not arrive in a single read can
be received at all. Demonstrated with a TServerSocket, a TWebSocketServer over
the accepted socket and a client that writes a well-formed 4000-byte frame as
2000 + 2000 bytes with a pause between them: readAll returns 0 of 4000. In
practice this is any frame larger than the path MTU, so the transport cannot
carry a Thrift message beyond roughly 1.4 kB over an ordinary Ethernet path.
The fix is to read the payload with transport_->readAll(), which is what
TFramedTransport::readFrame does for its own frames, and to keep the existing
signal for a peer that goes away in the middle of one: readAll raises
END_OF_FILE, which is caught and turned back into the same "return false" the
caller sees today. Nothing else about the contract changes.
This depends on THRIFT-6177 and should land after it. readAll() waits for the
number of bytes the frame declared, so it must not be used until that number is
bounded -- otherwise a header declaring 0xFFFFFFFF would make the server wait
for 4 GB rather than return. With THRIFT-6177 in place the wait is bounded by
TConfiguration::maxFrameSize and by the socket's own receive timeout, the same
two limits that bound TFramedTransport.
Tests are added to lib/cpp/test/TWebSocketServerTest.cpp: a frame delivered in
several pieces now arrives whole, and a frame whose payload stops early is
still reported as end of stream rather than handed over half-read.
was:
TWebSocketServer::readFrame reads a frame's payload with a single call to the
underlying transport and treats anything short of the whole payload as end of
stream (lib/cpp/src/thrift/transport/TWebSocketServer.h):
readBuffer_.resetBuffer(length);
uint8_t* buffer = readBuffer_.getWritePtr(length);
read = transport_->read(buffer, length);
readBuffer_.wroteBytes(read);
if (read < length) {
return false;
}
TSocket::read performs one recv() and returns whatever that produced, and the
transport factory hands TWebSocketServer the accepted TSocket directly, so the
payload arrives in as many pieces as the network chose to split it into.
Returning false makes readAll_virt return 0, which the caller reads as a closed
connection.
The effect is that no frame whose payload does not arrive in a single read can
be received at all. Demonstrated with a TServerSocket, a TWebSocketServer over
the accepted socket and a client that writes a well-formed 4000-byte frame as
2000 + 2000 bytes with a pause between them: readAll returns 0 of 4000. In
practice this is any frame larger than the path MTU, so the transport cannot
carry a Thrift message beyond roughly 1.4 kB over an ordinary Ethernet path.
The fix is to read the payload with transport_->readAll(), which is what
TFramedTransport::readFrame does for its own frames, and to keep the existing
signal for a peer that goes away in the middle of one: readAll raises
END_OF_FILE, which is caught and turned back into the same "return false" the
caller sees today. Nothing else about the contract changes.
This depends on THRIFT-6177 and should land after it. readAll() waits for the
number of bytes the frame declared, so it must not be used until that number is
bounded -- otherwise a header declaring 0xFFFFFFFF would make the server wait
for 4 GB rather than return. With THRIFT-6177 in place the wait is bounded by
TConfiguration::maxFrameSize and by the socket's own receive timeout, the same
two limits that bound TFramedTransport.
Tests are added to lib/cpp/test/TWebSocketServerTest.cpp: a frame delivered in
several pieces now arrives whole, and a frame whose payload stops early is
still reported as end of stream rather than handed over half-read.
> C++ WebSocket server drops any frame whose payload does not arrive in one read
> ------------------------------------------------------------------------------
>
> Key: THRIFT-6178
> URL: https://issues.apache.org/jira/browse/THRIFT-6178
> Project: Thrift
> Issue Type: Bug
> Components: C++ - Library
> Reporter: Jens Geyer
> Assignee: Jens Geyer
> Priority: Major
> Fix For: 0.25.0
>
> Time Spent: 0.5h
> Remaining Estimate: 0h
>
> TWebSocketServer::readFrame reads a frame's payload with a single call to the
> underlying transport and treats anything short of the whole payload as end of
> stream (lib/cpp/src/thrift/transport/TWebSocketServer.h):
> {code}
> readBuffer_.resetBuffer(length);
> uint8_t* buffer = readBuffer_.getWritePtr(length);
> read = transport_->read(buffer, length);
> readBuffer_.wroteBytes(read);
> if (read < length) {
> return false;
> }
> {code}
> TSocket::read performs one recv() and returns whatever that produced, and the
> transport factory hands TWebSocketServer the accepted TSocket directly, so
> the payload arrives in as many pieces as the network chose to split it into.
> Returning false makes readAll_virt return 0, which the caller reads as a
> closed connection.
> The effect is that no frame whose payload does not arrive in a single read
> can be received at all. Demonstrated with a TServerSocket, a TWebSocketServer
> over the accepted socket and a client that writes a well-formed 4000-byte
> frame as 2000 + 2000 bytes with a pause between them: readAll returns 0 of
> 4000. In practice this is any frame larger than the path MTU, so the
> transport cannot carry a Thrift message beyond roughly 1.4 kB over an
> ordinary Ethernet path.
> The fix is to read the payload with transport_->readAll(), which is what
> TFramedTransport::readFrame does for its own frames, and to keep the existing
> signal for a peer that goes away in the middle of one: readAll raises
> END_OF_FILE, which is caught and turned back into the same "return false" the
> caller sees today. Nothing else about the contract changes.
> This depends on THRIFT-6177 and should land after it. readAll() waits for the
> number of bytes the frame declared, so it must not be used until that number
> is bounded -- otherwise a header declaring 0xFFFFFFFF would make the server
> wait for 4 GB rather than return. With THRIFT-6177 in place the wait is
> bounded by TConfiguration::maxFrameSize and by the socket's own receive
> timeout, the same two limits that bound TFramedTransport.
> Tests are added to lib/cpp/test/TWebSocketServerTest.cpp: a frame delivered
> in several pieces now arrives whole, and a frame whose payload stops early is
> still reported as end of stream rather than handed over half-read.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)