THRIFT-926. cpp: Fix inconsistencies in transport read() behavior
- TBufferedTransport::borrow() could block if not enough data was
available. Now it returns NULL immediately in this case, like all
other transports.
- TBufferedTransport::read() could block some data was available in the
readahead buffer, but not enough to satisfy the request. It would
attempt to call read() on the underlying transport, but this might
block. Now it just returns the remaining data in the readahead
buffer. The caller is responsible for calling read() again to get the
rest of the data they want.
- TFrameTransport::read() threw an exception if read() on the underlying
transport returned 0 when looking for a frame header. Now
TFrameTransport::read() returns 0, too. (It still throws an exception
if the underlying transport returns 0 after a partial frame or frame
header has been read.)
- TFDTransport::read() threw an exception on EINTR. Now it retries up
to 5 times, similarly to the way TSocket::read() behaves.
- TZlibTransport::read() could block when less data than was requested
is available. Now it only calls read() on the underlying transport
when it would otherwise have nothing to return.
This does mean that TZlibTransport::read() now often returns less data
than is actually available at the time. This required updating
several of the ZlibTest tests to use readAll() instead of read(),
since they previously assumed read() would return all available data.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@1005161 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/test/TransportTest.cpp b/lib/cpp/test/TransportTest.cpp
index 6eb4523..c42b6ac 100644
--- a/lib/cpp/test/TransportTest.cpp
+++ b/lib/cpp/test/TransportTest.cpp
@@ -581,6 +581,64 @@
}
template <class CoupledTransports>
+void test_read_partial_midframe() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != NULL);
+ BOOST_REQUIRE(transports.out != NULL);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attempt to read 10 bytes, when only 9 are available, but after we have
+ // already read part of the data that is available. This exercises a
+ // different code path for several of the transports.
+ //
+ // For transports that add their own framing (e.g., TFramedTransport and
+ // TFileTransport), the two flush calls break up the data in to a 10 byte
+ // frame and a 3 byte frame. The first read then puts us partway through the
+ // first frame, and then we attempt to read past the end of that frame, and
+ // through the next frame, too.
+ //
+ // For buffered transports that perform read-ahead (e.g.,
+ // TBufferedTransport), the read-ahead will most likely see all 13 bytes
+ // written on the first read. The next read will then attempt to read past
+ // the end of the read-ahead buffer.
+ //
+ // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
+ // transports that track framing internally.
+ transports.out->write(write_buf, 10);
+ transports.out->flush();
+ transports.out->write(write_buf, 3);
+ transports.out->flush();
+
+ // Now read 4 bytes, so that we are partway through the written data.
+ uint32_t bytes_read = transports.in->read(read_buf, 4);
+ BOOST_CHECK_EQUAL(bytes_read, 4);
+
+ // Now attempt to read 10 bytes. Only 9 more are available.
+ //
+ // We should be able to get all 9 bytes, but it might take multiple read
+ // calls, since it is valid for read() to return fewer bytes than requested.
+ // (Most transports do immediately return 9 bytes, but the framing transports
+ // tend to only return to the end of the current frame, which is 6 bytes in
+ // this case.)
+ uint32_t total_read = 0;
+ while (total_read < 9) {
+ set_trigger(3, transports.out, 1);
+ bytes_read = transports.in->read(read_buf, 10);
+ BOOST_REQUIRE_EQUAL(numTriggersFired, 0);
+ BOOST_REQUIRE_GT(bytes_read, 0);
+ total_read += bytes_read;
+ BOOST_REQUIRE_LE(total_read, 9);
+ }
+
+ BOOST_CHECK_EQUAL(total_read, 9);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
void test_borrow_part_available() {
CoupledTransports transports;
BOOST_REQUIRE(transports.in != NULL);
@@ -851,6 +909,12 @@
test_read_part_available<CoupledTransports>, name);
suite_->add(tc, expectedFailures);
+ snprintf(name, sizeof(name), "%s::test_read_partial_midframe()",
+ transportName);
+ tc = boost::unit_test::make_test_case(
+ test_read_partial_midframe<CoupledTransports>, name);
+ suite_->add(tc, expectedFailures);
+
snprintf(name, sizeof(name), "%s::test_read_none_available()",
transportName);
tc = boost::unit_test::make_test_case(
diff --git a/lib/cpp/test/ZlibTest.cpp b/lib/cpp/test/ZlibTest.cpp
index 1e9f187..d2e6252 100644
--- a/lib/cpp/test/ZlibTest.cpp
+++ b/lib/cpp/test/ZlibTest.cpp
@@ -148,7 +148,7 @@
zlib_trans->finish();
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
- uint32_t got = zlib_trans->read(mirror.get(), buf_len);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0);
zlib_trans->verifyChecksum();
@@ -172,7 +172,7 @@
tmp_buf.length()-1));
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
- uint32_t got = zlib_trans->read(mirror.get(), buf_len);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0);
zlib_trans->verifyChecksum();
@@ -193,7 +193,7 @@
tmp_buf.length());
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
- uint32_t got = zlib_trans->read(mirror.get(), buf_len);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0);
try {
@@ -233,7 +233,8 @@
expected_read_len = buf_len - tot;
}
uint32_t got = zlib_trans->read(mirror.get() + tot, read_len);
- BOOST_REQUIRE_EQUAL(got, expected_read_len);
+ BOOST_REQUIRE_LE(got, expected_read_len);
+ BOOST_REQUIRE_NE(got, 0);
tot += got;
}
@@ -271,7 +272,7 @@
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
try {
- zlib_trans->read(mirror.get(), buf_len);
+ zlib_trans->readAll(mirror.get(), buf_len);
zlib_trans->verifyChecksum();
BOOST_ERROR("verifyChecksum() did not report an error");
} catch (TZlibTransportException& ex) {