diff --git a/quic/QuicConstants.h b/quic/QuicConstants.h index ac8321674..44c3de5eb 100644 --- a/quic/QuicConstants.h +++ b/quic/QuicConstants.h @@ -69,6 +69,12 @@ constexpr uint16_t kDefaultMsgSizeBackOffSize = 50; // larger than this, unless configured otherwise. constexpr uint16_t kDefaultUDPReadBufferSize = 1500; +// UDP's typical MTU size is 1500, so a large number of buffers +// does not make sense. We can optimize for buffer chains with +// fewer than 16 buffers, which is the highest I can think of +// for a real use case. +constexpr size_t kNumIovecBufferChains = 16; + // Number of GRO buffers to use // 1 means GRO is not enabled // 64 is the max possible value diff --git a/quic/api/QuicBatchWriter.cpp b/quic/api/QuicBatchWriter.cpp index bf2564aa9..dcf1d925b 100644 --- a/quic/api/QuicBatchWriter.cpp +++ b/quic/api/QuicBatchWriter.cpp @@ -32,7 +32,9 @@ bool SinglePacketBatchWriter::append( ssize_t SinglePacketBatchWriter::write( QuicAsyncUDPSocket& sock, const folly::SocketAddress& address) { - return sock.write(address, buf_); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(buf_, vec); + return sock.write(address, vec, iovec_len); } // SinglePacketInplaceBatchWriter @@ -54,7 +56,10 @@ ssize_t SinglePacketInplaceBatchWriter::write( const folly::SocketAddress& address) { auto& buf = conn_.bufAccessor->buf(); CHECK(!conn_.bufAccessor->isChained()); - auto ret = sock.write(address, buf); + + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(buf, vec); + auto ret = sock.write(address, vec, iovec_len); conn_.bufAccessor->clear(); return ret; } @@ -102,7 +107,9 @@ bool SinglePacketBackpressureBatchWriter::append( ssize_t SinglePacketBackpressureBatchWriter::write( QuicAsyncUDPSocket& sock, const folly::SocketAddress& address) { - auto written = sock.write(address, buf_); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(buf_, vec); + auto written = sock.write(address, vec, iovec_len); lastWriteSuccessful_ = written > 0; return written; } @@ -149,7 +156,9 @@ ssize_t SendmmsgPacketBatchWriter::write( const folly::SocketAddress& address) { CHECK_GT(bufs_.size(), 0); if (bufs_.size() == 1) { - return sock.write(address, bufs_[0]); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(bufs_.at(0), vec); + return sock.write(address, vec, iovec_len); } int ret = sock.writem( diff --git a/quic/api/QuicGsoBatchWriters.cpp b/quic/api/QuicGsoBatchWriters.cpp index 6ec0ead8b..f375b7a03 100644 --- a/quic/api/QuicGsoBatchWriters.cpp +++ b/quic/api/QuicGsoBatchWriters.cpp @@ -78,7 +78,9 @@ ssize_t GSOPacketBatchWriter::write( auto options = QuicAsyncUDPSocket::WriteOptions(gsoVal, false /*zerocopyVal*/); options.txTime = txTime_; - return sock.writeGSO(address, buf_, options); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(buf_, vec); + return sock.writeGSO(address, vec, iovec_len, options); } GSOInplacePacketBatchWriter::GSOInplacePacketBatchWriter( @@ -159,7 +161,9 @@ ssize_t GSOInplacePacketBatchWriter::write( auto options = QuicAsyncUDPSocket::WriteOptions(gsoVal, false /*zerocopyVal*/); options.txTime = txTime_; - auto bytesWritten = sock.writeGSO(address, buf, options); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(buf, vec); + auto bytesWritten = sock.writeGSO(address, vec, iovec_len, options); /** * If there is one more bytes after lastPacketEnd_, that means there is a * packet we choose not to write in this batch (e.g., it has a size larger @@ -281,8 +285,11 @@ ssize_t SendmmsgGSOPacketBatchWriter::write( const folly::SocketAddress& /*unused*/) { CHECK_GT(bufs_.size(), 0); if (bufs_.size() == 1) { - return (currBufs_ > 1) ? sock.writeGSO(addrs_[0], bufs_[0], options_[0]) - : sock.write(addrs_[0], bufs_[0]); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(bufs_[0], vec); + return (currBufs_ > 1) + ? sock.writeGSO(addrs_[0], vec, iovec_len, options_[0]) + : sock.write(addrs_[0], vec, iovec_len); } int ret = sock.writemGSO( diff --git a/quic/api/QuicTransportFunctions.cpp b/quic/api/QuicTransportFunctions.cpp index d9ea087eb..6cbecbc83 100644 --- a/quic/api/QuicTransportFunctions.cpp +++ b/quic/api/QuicTransportFunctions.cpp @@ -1319,7 +1319,9 @@ void writeCloseCommon( // best effort writing to the socket, ignore any errors. Buf packetBufPtr = packetBuf.clone(); - auto ret = sock.write(connection.peerAddress, packetBufPtr); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(packetBufPtr, vec); + auto ret = sock.write(connection.peerAddress, vec, iovec_len); connection.lossState.totalBytesSent += packetSize; if (ret < 0) { VLOG(4) << "Error writing connection close " << folly::errnoStr(errno) diff --git a/quic/api/test/BUCK b/quic/api/test/BUCK index 986befe90..11f7556f2 100644 --- a/quic/api/test/BUCK +++ b/quic/api/test/BUCK @@ -159,6 +159,7 @@ cpp_unittest( deps = [ "//quic/api:quic_batch_writer", "//quic/common/events:folly_eventbase", + "//quic/common/test:test_utils", "//quic/common/testutil:mock_async_udp_socket", "//quic/common/udpsocket:folly_async_udp_socket", "//quic/fizz/server/handshake:fizz_server_handshake", diff --git a/quic/api/test/CMakeLists.txt b/quic/api/test/CMakeLists.txt index 1f8657397..4ec8a083d 100644 --- a/quic/api/test/CMakeLists.txt +++ b/quic/api/test/CMakeLists.txt @@ -77,6 +77,7 @@ quic_add_test(TARGET QuicBatchWriterTest mvfst_buf_accessor mvfst_server mvfst_transport + mvfst_test_utils ) quic_add_test(TARGET QuicStreamAsyncTransportTest diff --git a/quic/api/test/QuicBatchWriterTest.cpp b/quic/api/test/QuicBatchWriterTest.cpp index a6c70ec68..431cb7f90 100644 --- a/quic/api/test/QuicBatchWriterTest.cpp +++ b/quic/api/test/QuicBatchWriterTest.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -464,12 +465,13 @@ TEST_F(QuicBatchWriterTest, InplaceWriterWriteAll) { ASSERT_TRUE( batchWriter->append(nullptr, 700, folly::SocketAddress(), nullptr)); - EXPECT_CALL(sock, writeGSO(_, _, _)) + EXPECT_CALL(sock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t, QuicAsyncUDPSocket::WriteOptions options) { - EXPECT_EQ(1000 * 5 + 700, buf->length()); + EXPECT_EQ(1000 * 5 + 700, vec[0].iov_len); EXPECT_EQ(1000, options.gso); return 1000 * 5 + 700; })); @@ -506,14 +508,13 @@ TEST_F(QuicBatchWriterTest, InplaceWriterWriteOne) { ASSERT_FALSE( batchWriter->append(nullptr, 1000, folly::SocketAddress(), nullptr)); - EXPECT_CALL(sock, writeGSO(_, _, _)) + EXPECT_CALL(sock, writeGSO(_, _, _, _)) .Times(1) - .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf, - auto) { - EXPECT_EQ(1000, buf->length()); - return 1000; - })); + .WillOnce(Invoke( + [&](const auto& /* addr */, const struct iovec* vec, size_t, auto) { + EXPECT_EQ(1000, vec[0].iov_len); + return 1000; + })); EXPECT_EQ(1000, batchWriter->write(sock, folly::SocketAddress())); EXPECT_TRUE(bufAccessor->ownsBuffer()); @@ -550,12 +551,13 @@ TEST_F(QuicBatchWriterTest, InplaceWriterLastOneTooBig) { bufAccessor->release(std::move(buf)); EXPECT_TRUE(batchWriter->needsFlush(1000)); - EXPECT_CALL(sock, writeGSO(_, _, _)) + EXPECT_CALL(sock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t, QuicAsyncUDPSocket::WriteOptions options) { - EXPECT_EQ(5 * 700, buf->length()); + EXPECT_EQ(5 * 700, vec[0].iov_len); EXPECT_EQ(700, options.gso); return 700 * 5; })); @@ -598,14 +600,13 @@ TEST_F(QuicBatchWriterTest, InplaceWriterBufResidueCheck) { rawBuf->append(packetSizeBig); EXPECT_TRUE(batchWriter->needsFlush(packetSizeBig)); - EXPECT_CALL(sock, writeGSO(_, _, _)) + EXPECT_CALL(sock, writeGSO(_, _, _, _)) .Times(1) - .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf, - auto) { - EXPECT_EQ(700, buf->length()); - return 700; - })); + .WillOnce(Invoke( + [&](const auto& /* addr */, const struct iovec* vec, size_t, auto) { + EXPECT_EQ(700, vec[0].iov_len); + return 700; + })); // No crash: EXPECT_EQ(700, batchWriter->write(sock, folly::SocketAddress())); EXPECT_EQ(1009, rawBuf->length()); @@ -751,13 +752,13 @@ TEST_F(SinglePacketInplaceBatchWriterTest, TestWrite) { std::shared_ptr qEvb = std::make_shared(&evb); quic::test::MockAsyncUDPSocket sock(qEvb); - EXPECT_CALL(sock, write(_, _)) + EXPECT_CALL(sock, write(_, _, _)) .Times(1) - .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf) { - EXPECT_EQ(appendSize, buf->length()); - return appendSize; - })); + .WillOnce( + Invoke([&](const auto& /* addr */, const struct iovec* vec, size_t) { + EXPECT_EQ(appendSize, vec[0].iov_len); + return appendSize; + })); EXPECT_EQ(appendSize, batchWriter->write(sock, folly::SocketAddress())); EXPECT_TRUE(batchWriter->empty()); } @@ -822,10 +823,11 @@ TEST_F(SinglePacketBackpressureBatchWriterTest, TestFailedWriteCachedOnEAGAIN) { folly::SocketAddress(), &sock_)); - EXPECT_CALL(sock_, write(_, _)) + EXPECT_CALL(sock_, write(_, _, _)) .Times(1) .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& /*buf*/) { + const struct iovec* /* vec */, + size_t /* iovec_len */) { errno = EAGAIN; return 0; })); @@ -846,11 +848,12 @@ TEST_F(SinglePacketBackpressureBatchWriterTest, TestFailedWriteCachedOnEAGAIN) { EXPECT_FALSE(conn_.pendingWriteBatch_.buf); // The write succeeds - EXPECT_CALL(sock_, write(_, _)) + EXPECT_CALL(sock_, write(_, _, _)) .Times(1) .WillOnce(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf) { - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + return ::quic::test::getTotalIovecLen(vec, iovec_len); })); EXPECT_EQ( batchWriter->write(sock_, folly::SocketAddress()), testString.size()); diff --git a/quic/api/test/QuicTransportBaseTest.cpp b/quic/api/test/QuicTransportBaseTest.cpp index 20d34366d..bec5966ff 100644 --- a/quic/api/test/QuicTransportBaseTest.cpp +++ b/quic/api/test/QuicTransportBaseTest.cpp @@ -1718,7 +1718,7 @@ TEST_P(QuicTransportImplTestBase, ReadDataAlsoChecksLossAlarm) { TEST_P(QuicTransportImplTestBase, ConnectionErrorOnWrite) { transport->transportConn->oneRttWriteCipher = test::createNoOpAead(); auto stream = transport->createBidirectionalStream().value(); - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .WillOnce(SetErrnoAndReturn(ENETUNREACH, -1)); transport->writeChain(stream, folly::IOBuf::copyBuffer("Hey"), true, nullptr); transport->addDataToStream( @@ -1743,10 +1743,12 @@ TEST_P(QuicTransportImplTestBase, ReadErrorUnsanitizedErrorMsg) { EXPECT_EQ("You need to calm down.", error.message); })); - EXPECT_CALL(*socketPtr, write(_, _)).WillOnce(Invoke([](auto&, auto&) { - throw std::runtime_error("You need to calm down."); - return 0; - })); + EXPECT_CALL(*socketPtr, write(_, _, _)) + .WillOnce( + Invoke([](const folly::SocketAddress&, const struct iovec*, size_t) { + throw std::runtime_error("You need to calm down."); + return 0; + })); transport->writeChain( stream, folly::IOBuf::copyBuffer("You are being too loud."), @@ -1765,10 +1767,12 @@ TEST_P(QuicTransportImplTestBase, ConnectionErrorUnhandledException) { onConnectionSetupError(QuicError( QuicErrorCode(TransportErrorCode::INTERNAL_ERROR), std::string("Well there's your problem")))); - EXPECT_CALL(*socketPtr, write(_, _)).WillOnce(Invoke([](auto&, auto&) { - throw std::runtime_error("Well there's your problem"); - return 0; - })); + EXPECT_CALL(*socketPtr, write(_, _, _)) + .WillOnce( + Invoke([](const folly::SocketAddress&, const struct iovec*, size_t) { + throw std::runtime_error("Well there's your problem"); + return 0; + })); transport->writeChain(stream, folly::IOBuf::copyBuffer("Hey"), true, nullptr); transport->addDataToStream( stream, StreamBuffer(folly::IOBuf::copyBuffer("Data"), 0)); @@ -2939,7 +2943,7 @@ TEST_P(QuicTransportImplTestBase, TestGracefulCloseWithActiveStream) { transport->notifyPendingWriteOnConnection(&wcbConn); transport->notifyPendingWriteOnStream(stream, &wcb); transport->setReadCallback(stream, &rcb); - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1)); transport->writeChain(stream, IOBuf::copyBuffer("hello"), true, &deliveryCb); EXPECT_CALL(txCb, onByteEventRegistered(getTxMatcher(stream, 0))); @@ -2993,7 +2997,7 @@ TEST_P(QuicTransportImplTestBase, TestGracefulCloseWithNoActiveStream) { EXPECT_CALL(connCallback, onConnectionError(_)).Times(0); transport->setReadCallback(stream, &rcb); - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1)); transport->writeChain(stream, IOBuf::copyBuffer("hello"), true, &deliveryCb); EXPECT_CALL(txCb, onByteEventRegistered(getTxMatcher(stream, 0))); @@ -3055,7 +3059,7 @@ TEST_P(QuicTransportImplTestBase, TestImmediateClose) { transport->notifyPendingWriteOnStream(stream, &wcb); transport->setReadCallback(stream, &rcb); transport->setPeekCallback(stream, &pcb); - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .WillRepeatedly(SetErrnoAndReturn(EAGAIN, -1)); transport->writeChain(stream, IOBuf::copyBuffer("hello"), true, &deliveryCb); EXPECT_CALL(txCb, onByteEventRegistered(getTxMatcher(stream, 0))); @@ -3195,7 +3199,8 @@ TEST_P(QuicTransportImplTestBase, ExceptionInWriteLooperDoesNotCrash) { transport->writeChain(stream, IOBuf::copyBuffer("hello"), true, nullptr); transport->addDataToStream( stream, StreamBuffer(IOBuf::copyBuffer("hello"), 0, false)); - EXPECT_CALL(*socketPtr, write(_, _)).WillOnce(SetErrnoAndReturn(EBADF, -1)); + EXPECT_CALL(*socketPtr, write(_, _, _)) + .WillOnce(SetErrnoAndReturn(EBADF, -1)); EXPECT_CALL(connSetupCallback, onConnectionSetupError(_)) .WillOnce(Invoke([&](auto) { transport.reset(); })); transport->writeLooper()->runLoopCallback(); @@ -4913,14 +4918,14 @@ TEST_P( })); // Fail the first write loop. - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .Times(2) // We attempt to flush the batch twice inside the write loop. // Fail both. - .WillRepeatedly(Invoke([&](const auto& /* addr */, - const std::unique_ptr& /*buf*/) { - errno = EAGAIN; - return 0; - })); + .WillRepeatedly( + Invoke([&](const folly::SocketAddress&, const struct iovec*, size_t) { + errno = EAGAIN; + return 0; + })); transport->writeLooper()->run(true /* thisIteration */); EXPECT_TRUE(transport->writeLooper()->isRunning()); @@ -4937,12 +4942,13 @@ TEST_P( EXPECT_TRUE(writeCallbackArmed); // Reset will make one write attempt. We don't care what happens to it - EXPECT_CALL(*socketPtr, write(_, _)) + EXPECT_CALL(*socketPtr, write(_, _, _)) .Times(1) - .WillRepeatedly(Invoke([&](const auto& /* addr */, - const std::unique_ptr& buf) { + .WillRepeatedly(Invoke([&](const folly::SocketAddress&, + const struct iovec* vec, + size_t iovec_len) { errno = 0; - return buf->computeChainDataLength(); + return getTotalIovecLen(vec, iovec_len); })); transport.reset(); } diff --git a/quic/api/test/QuicTransportFunctionsTest.cpp b/quic/api/test/QuicTransportFunctionsTest.cpp index b37ab4c54..2c34af839 100644 --- a/quic/api/test/QuicTransportFunctionsTest.cpp +++ b/quic/api/test/QuicTransportFunctionsTest.cpp @@ -2620,13 +2620,14 @@ TEST_F(QuicTransportFunctionsTest, WriteQuicDataToSocketWithCC) { EXPECT_CALL(*rawCongestionController, getWritableBytes()) .WillRepeatedly( InvokeWithoutArgs([&writableBytes]() { return writableBytes; })); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - EXPECT_LE(iobuf->computeChainDataLength(), 30); - writableBytes -= iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + size_t totalLen = getTotalIovecLen(vec, iovec_len); + EXPECT_LE(totalLen, 30); + writableBytes -= totalLen; + return totalLen; + })); EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(1); EXPECT_CALL(*quicStats_, onWrite(_)); writeQuicDataToSocket( @@ -2697,7 +2698,7 @@ TEST_F(QuicTransportFunctionsTest, WriteQuicDataToSocketLimitTest) { // Limit to zero conn->transportSettings.writeConnectionDataPacketsLimit = 0; - EXPECT_CALL(*rawSocket, write(_, _)).Times(0); + EXPECT_CALL(*rawSocket, write(_, _, _)).Times(0); EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(0); EXPECT_CALL(*quicStats_, onWrite(_)).Times(0); auto res = writeQuicDataToSocket( @@ -2720,14 +2721,14 @@ TEST_F(QuicTransportFunctionsTest, WriteQuicDataToSocketLimitTest) { conn->transportSettings.writeConnectionDataPacketsLimit = kDefaultWriteConnectionDataPacketLimit; uint64_t actualWritten = 0; - EXPECT_CALL(*rawSocket, write(_, _)) + EXPECT_CALL(*rawSocket, write(_, _, _)) .Times(1) - .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - writableBytes -= iobuf->computeChainDataLength(); - actualWritten += iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + .WillOnce(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + writableBytes -= getTotalIovecLen(vec, iovec_len); + actualWritten += getTotalIovecLen(vec, iovec_len); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(1); EXPECT_CALL(*quicStats_, onWrite(_)).Times(1); res = writeQuicDataToSocket( @@ -2754,13 +2755,13 @@ TEST_F(QuicTransportFunctionsTest, WriteQuicDataToSocketLimitTest) { EXPECT_CALL(*rawCongestionController, getWritableBytes()) .WillRepeatedly( InvokeWithoutArgs([&writableBytes]() { return writableBytes; })); - EXPECT_CALL(*rawSocket, write(_, _)) + EXPECT_CALL(*rawSocket, write(_, _, _)) .Times(kDefaultWriteConnectionDataPacketLimit * 2) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - actualWritten += iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + actualWritten += getTotalIovecLen(vec, iovec_len); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_CALL(*rawCongestionController, onPacketSent(_)) .Times(kDefaultWriteConnectionDataPacketLimit * 2); EXPECT_CALL(*quicStats_, onWrite(_)).Times(1); @@ -2807,15 +2808,15 @@ TEST_F( EXPECT_CALL(*rawCongestionController, getWritableBytes()) .WillRepeatedly( InvokeWithoutArgs([&writableBytes]() { return writableBytes; })); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - EXPECT_LE( - iobuf->computeChainDataLength(), - *conn->writableBytesLimit - conn->lossState.totalBytesSent); - writableBytes -= iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + EXPECT_LE( + getTotalIovecLen(vec, iovec_len), + *conn->writableBytesLimit - conn->lossState.totalBytesSent); + writableBytes -= getTotalIovecLen(vec, iovec_len); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_NE(WriteDataReason::NO_WRITE, shouldWriteData(*conn)); writeQuicDataToSocket( *rawSocket, @@ -2962,13 +2963,13 @@ TEST_F(QuicTransportFunctionsTest, WriteBlockedFrameWhenBlocked) { auto originalNextSeq = conn->ackStates.appDataAckState.nextPacketNum; uint64_t sentBytes = 0; - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - auto len = iobuf->computeChainDataLength(); - sentBytes += len; - return len; - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + auto len = getTotalIovecLen(vec, iovec_len); + sentBytes += len; + return len; + })); // Artificially Block the stream stream1->flowControlState.peerAdvertisedMaxOffset = 10; @@ -3033,13 +3034,13 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingNewData) { auto currentStreamWriteOffset = stream1->currentWriteOffset; EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(1); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - auto len = iobuf->computeChainDataLength(); - EXPECT_EQ(conn->udpSendPacketLen - aead->getCipherOverhead(), len); - return len; - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillOnce(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + auto len = getTotalIovecLen(vec, iovec_len); + EXPECT_EQ(conn->udpSendPacketLen - aead->getCipherOverhead(), len); + return len; + })); writeProbingDataToSocketForTest( *rawSocket, *conn, 1, *aead, *headerCipher, getVersion(*conn)); EXPECT_LT(currentPacketSeqNum, conn->ackStates.appDataAckState.nextPacketNum); @@ -3061,7 +3062,7 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingOldData) { auto socket = std::make_unique>(qEvb); auto rawSocket = socket.get(); - EXPECT_CALL(*rawSocket, write(_, _)).WillRepeatedly(Return(100)); + EXPECT_CALL(*rawSocket, write(_, _, _)).WillRepeatedly(Return(100)); auto capturingAead = std::make_unique(); auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = folly::IOBuf::copyBuffer("Where you wanna go"); @@ -3111,7 +3112,7 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingOldDataAckFreq) { auto socket = std::make_unique>(qEvb); auto rawSocket = socket.get(); - EXPECT_CALL(*rawSocket, write(_, _)).WillRepeatedly(Return(100)); + EXPECT_CALL(*rawSocket, write(_, _, _)).WillRepeatedly(Return(100)); auto capturingAead = std::make_unique(); auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = folly::IOBuf::copyBuffer("Where you wanna go"); @@ -3186,13 +3187,13 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingCryptoData) { auto currentStreamWriteOffset = cryptoStream->currentWriteOffset; EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(1); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - auto len = iobuf->computeChainDataLength(); - EXPECT_EQ(conn.udpSendPacketLen - aead->getCipherOverhead(), len); - return len; - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillOnce(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + auto len = getTotalIovecLen(vec, iovec_len); + EXPECT_EQ(conn.udpSendPacketLen - aead->getCipherOverhead(), len); + return len; + })); writeCryptoDataProbesToSocketForTest( *rawSocket, conn, 1, *aead, *headerCipher, getVersion(conn)); EXPECT_LT(currentPacketSeqNum, conn.ackStates.initialAckState->nextPacketNum); @@ -3234,13 +3235,13 @@ TEST_F(QuicTransportFunctionsTest, WriteableBytesLimitedProbingCryptoData) { auto currentStreamWriteOffset = cryptoStream->currentWriteOffset; EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(2); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - auto len = iobuf->computeChainDataLength(); - EXPECT_EQ(conn.udpSendPacketLen - aead->getCipherOverhead(), len); - return len; - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + auto len = getTotalIovecLen(vec, iovec_len); + EXPECT_EQ(conn.udpSendPacketLen - aead->getCipherOverhead(), len); + return len; + })); writeCryptoDataProbesToSocketForTest( *rawSocket, conn, probesToSend, *aead, *headerCipher, getVersion(conn)); @@ -3265,7 +3266,7 @@ TEST_F(QuicTransportFunctionsTest, ProbingNotFallbackToPingWhenNoQuota) { std::make_unique>(qEvb); auto rawSocket = socket.get(); EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(0); - EXPECT_CALL(*rawSocket, write(_, _)).Times(0); + EXPECT_CALL(*rawSocket, write(_, _, _)).Times(0); uint8_t probesToSend = 0; EXPECT_EQ( 0, @@ -3286,12 +3287,12 @@ TEST_F(QuicTransportFunctionsTest, ProbingFallbackToPing) { auto socket = std::make_unique>(qEvb); auto rawSocket = socket.get(); - EXPECT_CALL(*rawSocket, write(_, _)) + EXPECT_CALL(*rawSocket, write(_, _, _)) .Times(1) - .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - return iobuf->computeChainDataLength(); - })); + .WillOnce(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + return getTotalIovecLen(vec, iovec_len); + })); uint8_t probesToSend = 1; EXPECT_EQ( 1, @@ -3315,12 +3316,12 @@ TEST_F(QuicTransportFunctionsTest, ProbingFallbackToImmediateAck) { auto socket = std::make_unique>(qEvb); auto rawSocket = socket.get(); - EXPECT_CALL(*rawSocket, write(_, _)) + EXPECT_CALL(*rawSocket, write(_, _, _)) .Times(1) - .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - return iobuf->computeChainDataLength(); - })); + .WillOnce(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + return getTotalIovecLen(vec, iovec_len); + })); uint8_t probesToSend = 1; EXPECT_EQ( 1, @@ -3464,13 +3465,13 @@ TEST_F(QuicTransportFunctionsTest, WritePureAckWhenNoWritableBytes) { .WillRepeatedly(Return(0)); uint64_t actualWritten = 0; - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - EXPECT_LE(iobuf->computeChainDataLength(), 30); - actualWritten += iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + EXPECT_LE(getTotalIovecLen(vec, iovec_len), 30); + actualWritten += getTotalIovecLen(vec, iovec_len); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_CALL(*rawCongestionController, onPacketSent(_)).Times(0); auto res = writeQuicDataToSocket( *rawSocket, @@ -4051,12 +4052,12 @@ TEST_F(QuicTransportFunctionsTest, WriteLimitBytRttFraction) { writeDataToQuicStream(*stream1, buf->clone(), true); uint64_t actualWritten = 0; - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - actualWritten += iobuf->computeChainDataLength(); - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + actualWritten += getTotalIovecLen(vec, iovec_len); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_CALL(*rawCongestionController, getWritableBytes()) .WillRepeatedly(Return(50)); auto writeLoopBeginTime = Clock::now(); @@ -4111,7 +4112,7 @@ TEST_F(QuicTransportFunctionsTest, WriteLimitBytRttFractionNoLimit) { auto buf = buildRandomInputData(2048 * 2048); writeDataToQuicStream(*stream1, buf->clone(), true); - EXPECT_CALL(*rawSocket, write(_, _)).WillRepeatedly(Return(1)); + EXPECT_CALL(*rawSocket, write(_, _, _)).WillRepeatedly(Return(1)); EXPECT_CALL(*rawCongestionController, getWritableBytes()) .WillRepeatedly(Return(50)); auto writeLoopBeginTime = Clock::now(); @@ -4248,11 +4249,11 @@ TEST_F(QuicTransportFunctionsTest, ProbeWriteNewFunctionalFrames) { auto sock = std::make_unique>(qEvb); auto rawSocket = sock.get(); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + return getTotalIovecLen(vec, iovec_len); + })); auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = folly::IOBuf::copyBuffer("Drug facts"); @@ -4299,11 +4300,11 @@ TEST_F(QuicTransportFunctionsTest, ProbeWriteNewFunctionalFramesAckFreq) { auto sock = std::make_unique>(qEvb); auto rawSocket = sock.get(); - EXPECT_CALL(*rawSocket, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*rawSocket, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + return getTotalIovecLen(vec, iovec_len); + })); auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = folly::IOBuf::copyBuffer("Drug facts"); @@ -4362,17 +4363,18 @@ TEST_F(QuicTransportFunctionsTest, WriteWithInplaceBuilder) { auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = folly::IOBuf::copyBuffer("Andante in C minor"); writeDataToQuicStream(*stream, buf->clone(), true); - EXPECT_CALL(mockSock, writeGSO(_, _, _)) + EXPECT_CALL(mockSock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& sockBuf, + const struct iovec* vec, + size_t iovec_len, auto) { EXPECT_GT(bufPtr->length(), 0); - EXPECT_GE(sockBuf->length(), buf->length()); - EXPECT_EQ(sockBuf.get(), bufPtr); - EXPECT_TRUE(folly::IOBufEqualTo()(*sockBuf, *bufPtr)); - EXPECT_FALSE(sockBuf->isChained()); - return sockBuf->computeChainDataLength(); + EXPECT_GE(vec[0].iov_len, buf->length()); + EXPECT_TRUE(folly::IOBufEqualTo()( + *folly::IOBuf::wrapIov(vec, iovec_len), *bufPtr)); + EXPECT_EQ(iovec_len, 1); + return getTotalIovecLen(vec, iovec_len); })); writeQuicDataToSocket( mockSock, @@ -4401,7 +4403,7 @@ TEST_F(QuicTransportFunctionsTest, WriteWithInplaceBuilderRollbackBuf) { std::make_shared(&evb); quic::test::MockAsyncUDPSocket mockSock(qEvb); EXPECT_CALL(mockSock, getGSO()).WillRepeatedly(Return(true)); - EXPECT_CALL(mockSock, write(_, _)).Times(0); + EXPECT_CALL(mockSock, write(_, _, _)).Times(0); writeQuicDataToSocket( mockSock, *conn, @@ -4432,17 +4434,18 @@ TEST_F(QuicTransportFunctionsTest, WriteWithInplaceBuilderGSOMultiplePackets) { auto stream = conn->streamManager->createNextBidirectionalStream().value(); auto buf = buildRandomInputData(conn->udpSendPacketLen * 10); writeDataToQuicStream(*stream, buf->clone(), true); - EXPECT_CALL(mockSock, writeGSO(_, _, _)) + EXPECT_CALL(mockSock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& sockBuf, + const struct iovec* vec, + size_t iovec_len, QuicAsyncUDPSocket::WriteOptions options) { EXPECT_LE(options.gso, conn->udpSendPacketLen); - EXPECT_GT(bufPtr->length(), 0); - EXPECT_EQ(sockBuf.get(), bufPtr); - EXPECT_TRUE(folly::IOBufEqualTo()(*sockBuf, *bufPtr)); - EXPECT_FALSE(sockBuf->isChained()); - return sockBuf->length(); + EXPECT_GT(vec[0].iov_len, 0); + EXPECT_TRUE(folly::IOBufEqualTo()( + *folly::IOBuf::wrapIov(vec, iovec_len), *bufPtr)); + EXPECT_EQ(iovec_len, 1); + return getTotalIovecLen(vec, iovec_len); })); writeQuicDataToSocket( mockSock, @@ -4479,20 +4482,21 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) { conn->udpSendPacketLen * conn->transportSettings.writeConnectionDataPacketsLimit); writeDataToQuicStream(*stream, inputBuf->clone(), true); - EXPECT_CALL(mockSock, writeGSO(_, _, _)) + EXPECT_CALL(mockSock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& sockBuf, + const struct iovec* vec, + size_t iovec_len, QuicAsyncUDPSocket::WriteOptions options) { EXPECT_LE(options.gso, conn->udpSendPacketLen); EXPECT_GE( bufPtr->length(), conn->udpSendPacketLen * conn->transportSettings.writeConnectionDataPacketsLimit); - EXPECT_EQ(sockBuf.get(), bufPtr); - EXPECT_TRUE(folly::IOBufEqualTo()(*sockBuf, *bufPtr)); - EXPECT_FALSE(sockBuf->isChained()); - return sockBuf->length(); + EXPECT_TRUE(folly::IOBufEqualTo()( + *folly::IOBuf::wrapIov(vec, iovec_len), *bufPtr)); + EXPECT_EQ(iovec_len, 1); + return getTotalIovecLen(vec, iovec_len); })); writeQuicDataToSocket( mockSock, @@ -4515,14 +4519,15 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) { conn->outstandings.packets.front().metadata.encodedSize; auto outstandingPacketsCount = conn->outstandings.packets.size(); ASSERT_EQ(firstPacketSize, conn->udpSendPacketLen); - EXPECT_CALL(mockSock, writeGSO(_, _, _)) + EXPECT_CALL(mockSock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, auto) { - EXPECT_FALSE(buf->isChained()); - EXPECT_EQ(buf->length(), firstPacketSize); - return buf->length(); + EXPECT_EQ(iovec_len, 1); + EXPECT_EQ(vec[0].iov_len, firstPacketSize); + return getTotalIovecLen(vec, iovec_len); })); writeProbingDataToSocketForTest( mockSock, @@ -4536,15 +4541,16 @@ TEST_F(QuicTransportFunctionsTest, WriteProbingWithInplaceBuilder) { EXPECT_EQ(0, bufPtr->headroom()); // Clone again, this time 2 pacckets. - EXPECT_CALL(mockSock, writeGSO(_, _, _)) + EXPECT_CALL(mockSock, writeGSO(_, _, _, _)) .Times(1) .WillOnce(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, QuicAsyncUDPSocket::WriteOptions options) { - EXPECT_FALSE(buf->isChained()); + EXPECT_EQ(iovec_len, 1); EXPECT_EQ(conn->udpSendPacketLen, options.gso); - EXPECT_EQ(buf->length(), conn->udpSendPacketLen * 2); - return buf->length(); + EXPECT_EQ(vec[0].iov_len, conn->udpSendPacketLen * 2); + return getTotalIovecLen(vec, iovec_len); })); writeProbingDataToSocketForTest( mockSock, diff --git a/quic/api/test/QuicTransportTest.cpp b/quic/api/test/QuicTransportTest.cpp index 6f70a7c26..26e056227 100644 --- a/quic/api/test/QuicTransportTest.cpp +++ b/quic/api/test/QuicTransportTest.cpp @@ -142,12 +142,6 @@ RegularQuicWritePacket stripPaddingFrames(RegularQuicWritePacket packet) { return packet; } -size_t bufLength( - const SocketAddress&, - const std::unique_ptr& buf) { - return buf->computeChainDataLength(); -} - void dropPackets(QuicServerConnectionState& conn) { for (const auto& packet : conn.outstandings.packets) { for (const auto& frame : packet.packet.frames) { @@ -267,12 +261,12 @@ TEST_F(QuicTransportTest, WriteDataWithProbing) { Invoke([&](const auto& /* packet */) { onPacketSentCounter++; })); // Probing will send out one. Then regular write may send out multiple ones: int socketWriteCounter = 0; - EXPECT_CALL(*socket_, write(_, _)) - .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& iobuf) { - socketWriteCounter++; - return iobuf->computeChainDataLength(); - })); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(Invoke( + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWriteCounter++; + return getTotalIovecLen(vec, iovec_len); + })); transport_->writeChain(streamId, buf->clone(), true); loopForWrites(); transport_->close(none); @@ -1630,7 +1624,8 @@ TEST_F(QuicTransportTest, WriteSmall) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); transport_->setStreamPriority(stream, Priority(0, false)); loopForWrites(); @@ -1639,7 +1634,8 @@ TEST_F(QuicTransportTest, WriteSmall) { // Test retransmission dropPackets(conn); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1661,9 +1657,9 @@ TEST_F(QuicTransportTest, WriteLarge) { auto buf = buildRandomInputData(NumFullPackets * kDefaultUDPSendPacketLen + 20); folly::IOBuf passedIn; - EXPECT_CALL(*socket_, write(_, _)) + EXPECT_CALL(*socket_, write(_, _, _)) .Times(NumFullPackets + 1) - .WillRepeatedly(Invoke(bufLength)); + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); auto& conn = transport_->getConnectionState(); @@ -1672,9 +1668,9 @@ TEST_F(QuicTransportTest, WriteLarge) { // Test retransmission dropPackets(conn); - EXPECT_CALL(*socket_, write(_, _)) + EXPECT_CALL(*socket_, write(_, _, _)) .Times(NumFullPackets + 1) - .WillRepeatedly(Invoke(bufLength)); + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1692,7 +1688,8 @@ TEST_F(QuicTransportTest, WriteLarge) { TEST_F(QuicTransportTest, WriteMultipleTimes) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); auto& conn = transport_->getConnectionState(); @@ -1703,7 +1700,8 @@ TEST_F(QuicTransportTest, WriteMultipleTimes) { conn.outstandings.reset(); conn.streamManager->findStream(stream)->retransmissionBuffer.clear(); buf = buildRandomInputData(50); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); verifyCorrectness(conn, originalWriteOffset, stream, *buf); @@ -1715,14 +1713,16 @@ TEST_F(QuicTransportTest, WriteMultipleStreams) { auto s1 = transport_->createBidirectionalStream().value(); auto s2 = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(s1, buf->clone(), false); loopForWrites(); auto& conn = transport_->getConnectionState(); verifyCorrectness(conn, 0, s1, *buf); auto buf2 = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(s2, buf2->clone(), false); loopForWrites(); verifyCorrectness(conn, 0, s2, *buf2); @@ -1730,7 +1730,8 @@ TEST_F(QuicTransportTest, WriteMultipleStreams) { dropPackets(conn); // Should retransmit lost streams in a single packet - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1761,7 +1762,8 @@ TEST_F(QuicTransportTest, WriteFlowControl) { auto buf = buildRandomInputData(150); folly::IOBuf passedIn; // Write stream blocked frame - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(streamId, buf->clone(), false); loopForWrites(); @@ -1793,7 +1795,8 @@ TEST_F(QuicTransportTest, WriteFlowControl) { stream->flowControlState.pendingBlockedFrame = false; EXPECT_CALL(*mockQLogger, addTransportStateUpdate(getFlowControlEvent(200))); conn.streamManager->updateWritableStreams(*stream); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1809,7 +1812,9 @@ TEST_F(QuicTransportTest, WriteFlowControl) { auto num_outstandings = conn.outstandings.packets.size(); stream->flowControlState.peerAdvertisedMaxOffset = 300; conn.streamManager->updateWritableStreams(*stream); - EXPECT_CALL(*socket_, write(_, _)).Times(2).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .Times(2) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1841,7 +1846,7 @@ TEST_F(QuicTransportTest, WriteFlowControl) { // Try again, verify that there should not be any Data blocked frame emitted // again. - EXPECT_CALL(*socket_, write(_, _)).Times(0); + EXPECT_CALL(*socket_, write(_, _, _)).Times(0); writeQuicDataToSocket( *socket_, conn, @@ -1854,7 +1859,8 @@ TEST_F(QuicTransportTest, WriteFlowControl) { // Flow control lifted stream->conn.flowControlState.peerAdvertisedMaxOffset = 300; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1871,7 +1877,7 @@ TEST_F(QuicTransportTest, WriteErrorEagain) { // Test network error auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(SetErrnoAndReturn(EAGAIN, -1)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(SetErrnoAndReturn(EAGAIN, -1)); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); } @@ -1880,7 +1886,7 @@ TEST_F(QuicTransportTest, WriteErrorBad) { // Test network error auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(SetErrnoAndReturn(EBADF, -1)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(SetErrnoAndReturn(EBADF, -1)); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); EXPECT_TRUE(transport_->closed); @@ -1898,7 +1904,8 @@ TEST_F(QuicTransportTest, WriteInvalid) { TEST_F(QuicTransportTest, WriteFin) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), true); loopForWrites(); auto& conn = transport_->getConnectionState(); @@ -1906,7 +1913,8 @@ TEST_F(QuicTransportTest, WriteFin) { // Test retransmission dropPackets(conn); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1923,10 +1931,12 @@ TEST_F(QuicTransportTest, WriteFin) { TEST_F(QuicTransportTest, WriteOnlyFin) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, nullptr, true); loopForWrites(); auto& conn = transport_->getConnectionState(); @@ -1934,7 +1944,8 @@ TEST_F(QuicTransportTest, WriteOnlyFin) { // Test retransmission dropPackets(conn); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -1951,7 +1962,8 @@ TEST_F(QuicTransportTest, WriteOnlyFin) { TEST_F(QuicTransportTest, WriteDataWithRetransmission) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); auto& conn = transport_->getConnectionState(); @@ -1959,7 +1971,8 @@ TEST_F(QuicTransportTest, WriteDataWithRetransmission) { dropPackets(conn); auto buf2 = buildRandomInputData(50); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf2->clone(), false); loopForWrites(); // The first packet was lost. We should expect this packet contains both @@ -1975,7 +1988,8 @@ TEST_F(QuicTransportTest, WriteImmediateAcks) { PacketNum end = 15; conn.ackStates.appDataAckState.needsToSendAckImmediately = true; addAckStatesWithCurrentTimestamps(conn.ackStates.appDataAckState, start, end); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -2001,7 +2015,8 @@ TEST_F(QuicTransportTest, WritePendingAckIfHavingData) { addAckStatesWithCurrentTimestamps(conn.ackStates.appDataAckState, start, end); conn.ackStates.appDataAckState.needsToSendAckImmediately = false; conn.ackStates.appDataAckState.numNonRxPacketsRecvd = 3; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); // We should write acks if there is data pending transport_->writeChain(streamId, buf->clone(), true); loopForWrites(); @@ -2042,7 +2057,8 @@ TEST_F(QuicTransportTest, NoWritePendingAckIfHavingData) { addAckStatesWithCurrentTimestamps(conn.ackStates.appDataAckState, start, end); conn.ackStates.appDataAckState.needsToSendAckImmediately = false; conn.ackStates.appDataAckState.numNonRxPacketsRecvd = 3; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); // We should write acks if there is data pending transport_->writeChain(streamId, buf->clone(), true); loopForWrites(); @@ -2071,7 +2087,8 @@ TEST_F(QuicTransportTest, NoWritePendingAckIfHavingData) { TEST_F(QuicTransportTest, RstStream) { auto streamId = transport_->createBidirectionalStream().value(); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); EXPECT_EQ(1, transport_->getConnectionState().outstandings.packets.size()); @@ -2107,7 +2124,8 @@ TEST_F(QuicTransportTest, RstStream) { TEST_F(QuicTransportTest, StopSending) { auto streamId = transport_->createBidirectionalStream().value(); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->stopSending(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); EXPECT_EQ(1, transport_->getConnectionState().outstandings.packets.size()); @@ -2136,7 +2154,8 @@ TEST_F(QuicTransportTest, StopSending) { TEST_F(QuicTransportTest, StopSendingReadCallbackDefault) { auto streamId = transport_->createBidirectionalStream().value(); NiceMock readCb; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->setReadCallback(streamId, &readCb); transport_->setReadCallback(streamId, nullptr); loopForWrites(); @@ -2166,7 +2185,8 @@ TEST_F(QuicTransportTest, StopSendingReadCallbackDefault) { TEST_F(QuicTransportTest, StopSendingReadCallback) { auto streamId = transport_->createBidirectionalStream().value(); NiceMock readCb; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->setReadCallback(streamId, &readCb); transport_->setReadCallback( streamId, nullptr, GenericApplicationErrorCode::UNKNOWN); @@ -2213,7 +2233,8 @@ TEST_F(QuicTransportTest, NoStopSendingReadCallback) { } TEST_F(QuicTransportTest, SendPathChallenge) { - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); PathChallengeFrame pathChallenge(123); conn.pendingEvents.pathChallenge = pathChallenge; @@ -2257,7 +2278,8 @@ TEST_F(QuicTransportTest, SendPathChallenge) { } TEST_F(QuicTransportTest, PathValidationTimeoutExpired) { - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); PathChallengeFrame pathChallenge(123); conn.pendingEvents.pathChallenge = pathChallenge; @@ -2442,7 +2464,8 @@ TEST_F(QuicTransportTest, DoNotResendLostPathChallengeIfNotOutstanding) { } TEST_F(QuicTransportTest, SendPathResponse) { - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); EXPECT_EQ(conn.pendingEvents.frames.size(), 0); PathResponseFrame pathResponse(123); @@ -2565,7 +2588,8 @@ TEST_F(QuicTransportTest, ResendPathResponseOnLoss) { } TEST_F(QuicTransportTest, SendNewConnectionIdFrame) { - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); NewConnectionIdFrame newConnId( 1, 0, ConnectionId({2, 4, 2, 3}), StatelessResetToken()); @@ -2656,7 +2680,7 @@ TEST_F(QuicTransportTest, BusyWriteLoopDetection) { EXPECT_TRUE(conn.writeDebugState.needsWriteLoopDetect); EXPECT_EQ(0, conn.writeDebugState.currentEmptyLoopCount); EXPECT_EQ(WriteDataReason::STREAM, conn.writeDebugState.writeDataReason); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Return(1000)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(Return(1000)); loopForWrites(); EXPECT_EQ(1, conn.outstandings.packets.size()); EXPECT_EQ(0, conn.writeDebugState.currentEmptyLoopCount); @@ -2667,7 +2691,7 @@ TEST_F(QuicTransportTest, BusyWriteLoopDetection) { EXPECT_TRUE( WriteDataReason::STREAM_WINDOW_UPDATE == conn.writeDebugState.writeDataReason); - EXPECT_CALL(*socket_, write(_, _)).Times(0); + EXPECT_CALL(*socket_, write(_, _, _)).Times(0); EXPECT_CALL( *rawLoopDetectorCallback, onSuspiciousWriteLoops(1, WriteDataReason::STREAM_WINDOW_UPDATE, _, _)) @@ -2704,7 +2728,8 @@ TEST_F(QuicTransportTest, ResendNewConnectionIdOnLoss) { } TEST_F(QuicTransportTest, SendRetireConnectionIdFrame) { - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); RetireConnectionIdFrame retireConnId(1); sendSimpleFrame(conn, retireConnId); @@ -2790,7 +2815,8 @@ TEST_F(QuicTransportTest, ResendRetireConnectionIdOnLoss) { TEST_F(QuicTransportTest, NonWritableStreamAPI) { auto streamId = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto& conn = transport_->getConnectionState(); auto streamState = conn.streamManager->getStream(streamId); @@ -2826,7 +2852,8 @@ TEST_F(QuicTransportTest, RstWrittenStream) { ASSERT_TRUE(stream); auto currentWriteOffset = stream->currentWriteOffset; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); // 2 packets are outstanding: one for Stream frame one for RstStream frame: @@ -2860,7 +2887,7 @@ TEST_F(QuicTransportTest, RstWrittenStream) { TEST_F(QuicTransportTest, RstStreamUDPWriteFailNonFatal) { auto streamId = transport_->createBidirectionalStream().value(); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(SetErrnoAndReturn(EAGAIN, -1)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(SetErrnoAndReturn(EAGAIN, -1)); transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); @@ -2897,7 +2924,7 @@ TEST_F(QuicTransportTest, RstStreamUDPWriteFailNonFatal) { TEST_F(QuicTransportTest, RstStreamUDPWriteFailFatal) { auto streamId = transport_->createBidirectionalStream().value(); - EXPECT_CALL(*socket_, write(_, _)) + EXPECT_CALL(*socket_, write(_, _, _)) .WillRepeatedly(SetErrnoAndReturn(EBADF, -1)); transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); @@ -2916,7 +2943,8 @@ TEST_F(QuicTransportTest, WriteAfterSendRst) { auto stream = conn.streamManager->findStream(streamId); ASSERT_TRUE(stream); auto currentWriteOffset = stream->currentWriteOffset; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN); loopForWrites(); @@ -2960,7 +2988,8 @@ TEST_F(QuicTransportTest, WriteAfterSendRst) { TEST_F(QuicTransportTest, DoubleReset) { auto streamId = transport_->createBidirectionalStream().value(); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); EXPECT_FALSE( transport_->resetStream(streamId, GenericApplicationErrorCode::UNKNOWN) .hasError()); @@ -2975,7 +3004,8 @@ TEST_F(QuicTransportTest, DoubleReset) { TEST_F(QuicTransportTest, WriteStreamDataSetLossAlarm) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(1); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); EXPECT_TRUE(transport_->isLossTimeoutScheduled()); @@ -2986,7 +3016,8 @@ TEST_F(QuicTransportTest, WriteAckNotSetLossAlarm) { addAckStatesWithCurrentTimestamps( conn.ackStates.appDataAckState, 0 /* start */, 100 /* ind */); conn.ackStates.appDataAckState.needsToSendAckImmediately = true; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto res = writeQuicDataToSocket( *socket_, conn, @@ -3005,7 +3036,8 @@ TEST_F(QuicTransportTest, WriteWindowUpdate) { conn.flowControlState.windowSize = 100; conn.flowControlState.advertisedMaxOffset = 0; conn.pendingEvents.connWindowUpdate = true; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); auto res = writeQuicDataToSocket( *socket_, conn, @@ -3041,7 +3073,8 @@ TEST_F(QuicTransportTest, WriteWindowUpdate) { streamState->flowControlState.advertisedMaxOffset = 0; MaxStreamDataFrame frame(stream, 100); conn.streamManager->queueWindowUpdate(stream); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); res = writeQuicDataToSocket( *socket_, conn, @@ -3084,7 +3117,8 @@ TEST_F(QuicTransportTest, DeliveryCallbackClosesClosedTransport) { auto stream1 = transport_->createBidirectionalStream().value(); auto buf1 = buildRandomInputData(20); TransportClosingDeliveryCallback dc(transport_.get(), 20); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream1, buf1->clone(), true, &dc); loopForWrites(); transport_->close(none); @@ -3094,7 +3128,8 @@ TEST_F(QuicTransportTest, DeliveryCallbackClosesTransportOnDelivered) { auto stream1 = transport_->createBidirectionalStream().value(); auto buf1 = buildRandomInputData(20); TransportClosingDeliveryCallback dc(transport_.get(), 0); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream1, 0, &dc); transport_->writeChain(stream1, buf1->clone(), true); loopForWrites(); @@ -3113,7 +3148,8 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksNothingDelivered) { NiceMock mockedDeliveryCallback; auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream, 1, &mockedDeliveryCallback); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); @@ -3147,7 +3183,8 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksAllDelivered) { NiceMock mockedDeliveryCallback; auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(20); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream, 1, &mockedDeliveryCallback); transport_->writeChain(stream, buf->clone(), true); loopForWrites(); @@ -3171,7 +3208,8 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksPartialDelivered) { mockedDeliveryCallback2; auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(100); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream, 50, &mockedDeliveryCallback1); transport_->registerDeliveryCallback(stream, 150, &mockedDeliveryCallback2); transport_->writeChain(stream, buf->clone(), false); @@ -3212,7 +3250,8 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksRetxBuffer) { mockedDeliveryCallback2; auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(100); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream, 50, &mockedDeliveryCallback1); transport_->registerDeliveryCallback(stream, 150, &mockedDeliveryCallback2); transport_->writeChain(stream, buf->clone(), false); @@ -3259,7 +3298,8 @@ TEST_F(QuicTransportTest, InvokeDeliveryCallbacksLossAndRetxBuffer) { mockedDeliveryCallback2, mockedDeliveryCallback3; auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(100); - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->registerDeliveryCallback(stream, 30, &mockedDeliveryCallback1); transport_->registerDeliveryCallback(stream, 50, &mockedDeliveryCallback2); transport_->registerDeliveryCallback(stream, 150, &mockedDeliveryCallback3); @@ -4219,7 +4259,8 @@ TEST_F(QuicTransportTest, WriteStreamFromMiddleOfMap) { auto stream2 = conn.streamManager->getStream(s2); writeDataToQuicStream(*stream2, buf2->clone(), false); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -4242,7 +4283,8 @@ TEST_F(QuicTransportTest, WriteStreamFromMiddleOfMap) { conn.streamManager->writeQueue().setNextScheduledStream(s2); writableBytes = kDefaultUDPSendPacketLen - 100; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -4266,7 +4308,8 @@ TEST_F(QuicTransportTest, WriteStreamFromMiddleOfMap) { // Test wrap around conn.streamManager->writeQueue().setNextScheduledStream(s2); writableBytes = kDefaultUDPSendPacketLen; - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillOnce(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); writeQuicDataToSocket( *socket_, conn, @@ -4392,7 +4435,8 @@ TEST_F(QuicTransportTest, CloseTransportCancelsAckTimeout) { auto stream = transport_->createBidirectionalStream().value(); auto buf = buildRandomInputData(kDefaultUDPSendPacketLen + 20); folly::IOBuf passedIn; - EXPECT_CALL(*socket_, write(_, _)).WillRepeatedly(Invoke(bufLength)); + EXPECT_CALL(*socket_, write(_, _, _)) + .WillRepeatedly(testing::WithArgs<1, 2>(Invoke(getTotalIovecLen))); transport_->writeChain(stream, buf->clone(), false); loopForWrites(); transport_->scheduleLossTimeout(500ms); @@ -4453,7 +4497,7 @@ TEST_F(QuicTransportTest, PacedWriteNoDataToWrite) { ASSERT_EQ( WriteDataReason::NO_WRITE, shouldWriteData(transport_->getConnectionState())); - EXPECT_CALL(*socket_, write(_, _)).Times(0); + EXPECT_CALL(*socket_, write(_, _, _)).Times(0); transport_->pacedWrite(); } @@ -4474,7 +4518,7 @@ TEST_F(QuicTransportTest, PacingWillBurstFirst) { auto buf = buildRandomInputData(200); auto streamId = transport_->createBidirectionalStream().value(); transport_->writeChain(streamId, buf->clone(), false); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Return(0)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(Return(0)); EXPECT_CALL(*rawPacer, updateAndGetWriteBatchSize(_)) .WillRepeatedly(Return(1)); transport_->pacedWrite(); @@ -4499,7 +4543,7 @@ TEST_F(QuicTransportTest, AlreadyScheduledPacingNoWrite) { auto buf = buildRandomInputData(200); auto streamId = transport_->createBidirectionalStream().value(); transport_->writeChain(streamId, buf->clone(), false); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Return(0)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(Return(0)); EXPECT_CALL(*rawPacer, updateAndGetWriteBatchSize(_)) .WillRepeatedly(Return(1)); EXPECT_CALL(*rawPacer, getTimeUntilNextWrite(_)) @@ -4510,7 +4554,7 @@ TEST_F(QuicTransportTest, AlreadyScheduledPacingNoWrite) { ASSERT_NE(WriteDataReason::NO_WRITE, shouldWriteData(conn)); EXPECT_TRUE(transport_->isPacingScheduled()); - EXPECT_CALL(*socket_, write(_, _)).Times(0); + EXPECT_CALL(*socket_, write(_, _, _)).Times(0); transport_->pacedWrite(); } @@ -4531,7 +4575,7 @@ TEST_F(QuicTransportTest, NoScheduleIfNoNewData) { auto buf = buildRandomInputData(200); auto streamId = transport_->createBidirectionalStream().value(); transport_->writeChain(streamId, buf->clone(), false); - EXPECT_CALL(*socket_, write(_, _)).WillOnce(Return(0)); + EXPECT_CALL(*socket_, write(_, _, _)).WillOnce(Return(0)); EXPECT_CALL(*rawPacer, updateAndGetWriteBatchSize(_)) .WillRepeatedly(Return(1)); // This will write out everything. After that because there is no new data, diff --git a/quic/client/QuicClientTransport.cpp b/quic/client/QuicClientTransport.cpp index 6ddd6848b..4ba71a6f6 100644 --- a/quic/client/QuicClientTransport.cpp +++ b/quic/client/QuicClientTransport.cpp @@ -1041,9 +1041,11 @@ void QuicClientTransport::startCryptoHandshake() { conn_->transportParametersEncoded = true; if (!conn_->transportSettings.flowPriming.empty() && conn_->peerAddress.isInitialized()) { - socket_->write( - conn_->peerAddress, - folly::IOBuf::copyBuffer(conn_->transportSettings.flowPriming)); + auto flowPrimingBuf = + folly::IOBuf::copyBuffer(conn_->transportSettings.flowPriming); + iovec vec[kNumIovecBufferChains]; + size_t iovec_len = fillIovec(flowPrimingBuf, vec); + socket_->write(conn_->peerAddress, vec, iovec_len); } handshakeLayer->connect(hostname_, std::move(paramsExtension)); diff --git a/quic/common/BufUtil.cpp b/quic/common/BufUtil.cpp index 21e90d9fe..76fcee0cd 100644 --- a/quic/common/BufUtil.cpp +++ b/quic/common/BufUtil.cpp @@ -9,6 +9,17 @@ namespace quic { +size_t fillIovec(std::unique_ptr& buf, iovec (&vec)[16]) { + size_t iovec_len = buf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; + if (FOLLY_UNLIKELY(iovec_len == 0)) { + buf->coalesce(); + vec[0].iov_base = const_cast(buf->data()); + vec[0].iov_len = buf->length(); + iovec_len = 1; + } + return iovec_len; +} + Buf BufQueue::splitAtMost(size_t len) { folly::IOBuf* current = chain_.get(); // empty queue / requested 0 bytes diff --git a/quic/common/BufUtil.h b/quic/common/BufUtil.h index 3af6b4bbf..8b82c445c 100644 --- a/quic/common/BufUtil.h +++ b/quic/common/BufUtil.h @@ -13,6 +13,8 @@ namespace quic { +size_t fillIovec(std::unique_ptr& buf, iovec (&vec)[16]); + class BufQueue { public: BufQueue() = default; diff --git a/quic/common/test/TestUtils.cpp b/quic/common/test/TestUtils.cpp index 39c4b5649..079ae51c9 100644 --- a/quic/common/test/TestUtils.cpp +++ b/quic/common/test/TestUtils.cpp @@ -821,5 +821,26 @@ std::unique_ptr getProtectionKey() { folly::IOBuf::create(0), pnCipher->keyLength()); } + +size_t getTotalIovecLen(const struct iovec* vec, size_t iovec_len) { + uint32_t result = 0; + for (uint32_t i = 0; i < iovec_len; i++) { + result += vec[i].iov_len; + } + return result; +} + +Buf copyChain(Buf&& input) { + folly::IOBuf* current = input.get(); + Buf headCopy = folly::IOBuf::copyBuffer(current->data(), current->length()); + current = current->next(); + while (current != input.get()) { + Buf currCopy = folly::IOBuf::copyBuffer(current->data(), current->length()); + headCopy->appendToChain(std::move(currCopy)); + current = current->next(); + } + return headCopy; +} + } // namespace test } // namespace quic diff --git a/quic/common/test/TestUtils.h b/quic/common/test/TestUtils.h index 73557db2c..a14e7928b 100644 --- a/quic/common/test/TestUtils.h +++ b/quic/common/test/TestUtils.h @@ -580,5 +580,9 @@ class FakeServerHandshake : public FizzServerHandshake { Optional clientActiveConnectionIdLimit_; }; +size_t getTotalIovecLen(const struct iovec* vec, size_t iovec_len); + +Buf copyChain(Buf&& input); + } // namespace test } // namespace quic diff --git a/quic/common/testutil/MockAsyncUDPSocket.h b/quic/common/testutil/MockAsyncUDPSocket.h index adccc7321..4e326dd42 100644 --- a/quic/common/testutil/MockAsyncUDPSocket.h +++ b/quic/common/testutil/MockAsyncUDPSocket.h @@ -26,7 +26,7 @@ struct MockAsyncUDPSocket : public FollyQuicAsyncUDPSocket { MOCK_METHOD( ssize_t, write, - (const folly::SocketAddress&, const std::unique_ptr&)); + (const folly::SocketAddress&, const struct iovec*, size_t)); MOCK_METHOD( int, writem, @@ -37,7 +37,8 @@ struct MockAsyncUDPSocket : public FollyQuicAsyncUDPSocket { ssize_t, writeGSO, (const folly::SocketAddress&, - const std::unique_ptr&, + const struct iovec*, + size_t, QuicAsyncUDPSocket::WriteOptions)); MOCK_METHOD( ssize_t, diff --git a/quic/common/udpsocket/FollyQuicAsyncUDPSocket.cpp b/quic/common/udpsocket/FollyQuicAsyncUDPSocket.cpp index 6370e7b0c..14fc17494 100644 --- a/quic/common/udpsocket/FollyQuicAsyncUDPSocket.cpp +++ b/quic/common/udpsocket/FollyQuicAsyncUDPSocket.cpp @@ -57,8 +57,11 @@ void FollyQuicAsyncUDPSocket::setErrMessageCallback( ssize_t FollyQuicAsyncUDPSocket::write( const folly::SocketAddress& address, - const std::unique_ptr& buf) { - return follySocket_.write(address, buf); + const struct iovec* vec, + size_t iovec_len) { + folly::AsyncUDPSocket::WriteOptions writeOptions( + 0 /*gsoVal*/, false /* zerocopyVal*/); + return follySocket_.writev(address, vec, iovec_len, writeOptions); } int FollyQuicAsyncUDPSocket::writem( @@ -70,12 +73,13 @@ int FollyQuicAsyncUDPSocket::writem( ssize_t FollyQuicAsyncUDPSocket::writeGSO( const folly::SocketAddress& address, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, WriteOptions options) { folly::AsyncUDPSocket::WriteOptions follyOptions( options.gso, options.zerocopy); follyOptions.txTime = options.txTime; - return follySocket_.writeGSO(address, buf, follyOptions); + return follySocket_.writev(address, vec, iovec_len, follyOptions); } int FollyQuicAsyncUDPSocket::writemGSO( diff --git a/quic/common/udpsocket/FollyQuicAsyncUDPSocket.h b/quic/common/udpsocket/FollyQuicAsyncUDPSocket.h index 7efdef1c0..dd1648a3f 100644 --- a/quic/common/udpsocket/FollyQuicAsyncUDPSocket.h +++ b/quic/common/udpsocket/FollyQuicAsyncUDPSocket.h @@ -70,7 +70,8 @@ class FollyQuicAsyncUDPSocket : public QuicAsyncUDPSocketImpl { ssize_t write( const folly::SocketAddress& address, - const std::unique_ptr& buf) override; + const struct iovec* vec, + size_t iovec_len) override; int writem( folly::Range addrs, @@ -79,7 +80,8 @@ class FollyQuicAsyncUDPSocket : public QuicAsyncUDPSocketImpl { ssize_t writeGSO( const folly::SocketAddress& address, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, WriteOptions options) override; /** diff --git a/quic/common/udpsocket/LibevQuicAsyncUDPSocket.cpp b/quic/common/udpsocket/LibevQuicAsyncUDPSocket.cpp index dc76ac9bf..09f4f28a5 100644 --- a/quic/common/udpsocket/LibevQuicAsyncUDPSocket.cpp +++ b/quic/common/udpsocket/LibevQuicAsyncUDPSocket.cpp @@ -74,7 +74,8 @@ void LibevQuicAsyncUDPSocket::pauseWrite() { ssize_t LibevQuicAsyncUDPSocket::write( const folly::SocketAddress& address, - const std::unique_ptr& buf) { + const struct iovec* vec, + size_t iovec_len) { if (fd_ == -1) { throw folly::AsyncSocketException( folly::AsyncSocketException::NOT_OPEN, "socket is not initialized"); @@ -97,15 +98,6 @@ ssize_t LibevQuicAsyncUDPSocket::write( msg.msg_namelen = 0; } - iovec vec[16]; - size_t iovec_len = buf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; - if (UNLIKELY(iovec_len == 0)) { - buf->coalesce(); - vec[0].iov_base = const_cast(buf->data()); - vec[0].iov_len = buf->length(); - iovec_len = 1; - } - msg.msg_iov = const_cast(vec); msg.msg_iovlen = iovec_len; msg.msg_control = nullptr; diff --git a/quic/common/udpsocket/LibevQuicAsyncUDPSocket.h b/quic/common/udpsocket/LibevQuicAsyncUDPSocket.h index d11e079c8..aca282253 100644 --- a/quic/common/udpsocket/LibevQuicAsyncUDPSocket.h +++ b/quic/common/udpsocket/LibevQuicAsyncUDPSocket.h @@ -36,7 +36,8 @@ class LibevQuicAsyncUDPSocket : public QuicAsyncUDPSocketImpl { ssize_t write( const folly::SocketAddress& address, - const std::unique_ptr& buf) override; + const struct iovec* vec, + size_t iovec_len) override; int writem( folly::Range addrs, @@ -45,7 +46,8 @@ class LibevQuicAsyncUDPSocket : public QuicAsyncUDPSocketImpl { ssize_t writeGSO( const folly::SocketAddress& /*address*/, - const std::unique_ptr& /*buf*/, + const struct iovec* /* vec */, + size_t /* iovec_len */, WriteOptions /*options*/) override { LOG(FATAL) << __func__ << " not supported in LibevQuicAsyncUDPSocket"; } diff --git a/quic/common/udpsocket/QuicAsyncUDPSocket.h b/quic/common/udpsocket/QuicAsyncUDPSocket.h index a92d28703..8089562f8 100644 --- a/quic/common/udpsocket/QuicAsyncUDPSocket.h +++ b/quic/common/udpsocket/QuicAsyncUDPSocket.h @@ -156,7 +156,8 @@ class QuicAsyncUDPSocket { */ virtual ssize_t write( const folly::SocketAddress& /* address */, - const std::unique_ptr& /* buf */) = 0; + const struct iovec* vec, + size_t iovec_len) = 0; /** * Send the data in buffers to destination. Returns the return code from @@ -189,7 +190,8 @@ class QuicAsyncUDPSocket { */ virtual ssize_t writeGSO( const folly::SocketAddress& address, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, WriteOptions options) = 0; /** diff --git a/quic/common/udpsocket/test/QuicAsyncUDPSocketMock.h b/quic/common/udpsocket/test/QuicAsyncUDPSocketMock.h index 301434834..b66d2a83d 100644 --- a/quic/common/udpsocket/test/QuicAsyncUDPSocketMock.h +++ b/quic/common/udpsocket/test/QuicAsyncUDPSocketMock.h @@ -24,7 +24,7 @@ class QuicAsyncUDPSocketMock : public QuicAsyncUDPSocket { MOCK_METHOD( (ssize_t), write, - (const folly::SocketAddress&, const std::unique_ptr&)); + (const folly::SocketAddress&, const struct iovec* vec, size_t iovec_len)); MOCK_METHOD( (int), writem, @@ -35,7 +35,8 @@ class QuicAsyncUDPSocketMock : public QuicAsyncUDPSocket { ssize_t, writeGSO, (const folly::SocketAddress&, - const std::unique_ptr&, + const struct iovec* vec, + size_t iovec_len, WriteOptions)); MOCK_METHOD( (int), diff --git a/quic/common/udpsocket/test/QuicAsyncUDPSocketTestBase.h b/quic/common/udpsocket/test/QuicAsyncUDPSocketTestBase.h index 4ef8b9103..ab933fd84 100644 --- a/quic/common/udpsocket/test/QuicAsyncUDPSocketTestBase.h +++ b/quic/common/udpsocket/test/QuicAsyncUDPSocketTestBase.h @@ -60,7 +60,11 @@ TYPED_TEST_P(QuicAsyncUDPSocketTest, ErrToNonExistentServer) { // If an error is received, the read callback should not be triggered EXPECT_CALL(this->readCb_, onNotifyDataAvailable_(testing::_)).Times(0); #endif // FOLLY_HAVE_MSG_ERRQUEUE - this->udpSocket_->write(addr, folly::IOBuf::copyBuffer("hey")); + auto sendBuf = folly::IOBuf::copyBuffer("hey"); + iovec vec[quic::kNumIovecBufferChains]; + size_t iovec_len = + sendBuf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; + this->udpSocket_->write(addr, vec, iovec_len); this->udpSocket_->getEventBase()->loopForever(); EXPECT_TRUE(errRecvd); } @@ -72,7 +76,12 @@ TYPED_TEST_P(QuicAsyncUDPSocketTest, TestUnsetErrCallback) { folly::SocketAddress addr("127.0.0.1", 10000); EXPECT_CALL(this->errCb_, errMessage_(testing::_)).Times(0); EXPECT_CALL(this->readCb_, onNotifyDataAvailable_(testing::_)).Times(0); - this->udpSocket_->write(addr, folly::IOBuf::copyBuffer("hey")); + + auto sendBuf = folly::IOBuf::copyBuffer("hey"); + iovec vec[quic::kNumIovecBufferChains]; + size_t iovec_len = + sendBuf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; + this->udpSocket_->write(addr, vec, iovec_len); class EvbTerminateTimeout : public quic::QuicTimerCallback { public: @@ -113,7 +122,11 @@ TYPED_TEST_P(QuicAsyncUDPSocketTest, CloseInErrorCallback) { // should not be triggered EXPECT_CALL(this->readCb_, onNotifyDataAvailable_(testing::_)).Times(0); #endif // FOLLY_HAVE_MSG_ERRQUEUE - this->udpSocket_->write(addr, folly::IOBuf::copyBuffer("hey")); + auto sendBuf = folly::IOBuf::copyBuffer("hey"); + iovec vec[quic::kNumIovecBufferChains]; + size_t iovec_len = + sendBuf->fillIov(vec, sizeof(vec) / sizeof(vec[0])).numIovecs; + this->udpSocket_->write(addr, vec, iovec_len); this->udpSocket_->getEventBase()->loopForever(); EXPECT_TRUE(errRecvd); } diff --git a/quic/dsr/backend/test/DSRPacketizerTest.cpp b/quic/dsr/backend/test/DSRPacketizerTest.cpp index 5476768b2..8f6aa0807 100644 --- a/quic/dsr/backend/test/DSRPacketizerTest.cpp +++ b/quic/dsr/backend/test/DSRPacketizerTest.cpp @@ -171,18 +171,20 @@ TEST_F(DSRMultiWriteTest, TwoRequestsWithLoss) { std::vector sentData; auto sock = std::make_unique>(qEvb_); - EXPECT_CALL(*sock, writeGSO(conn_.peerAddress, _, _)) + EXPECT_CALL(*sock, writeGSO(conn_.peerAddress, _, _, _)) .WillRepeatedly(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf, + const struct iovec* vec, + size_t iovec_len, QuicAsyncUDPSocket::WriteOptions) { - sentData.push_back(buf->clone()); - return buf->computeChainDataLength(); + sentData.push_back(copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*sock, write(conn_.peerAddress, _)) + EXPECT_CALL(*sock, write(conn_.peerAddress, _, _)) .WillRepeatedly(Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf) { - sentData.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + sentData.push_back(copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); auto& instruction = pendingInstructions_.front(); CipherBuilder builder; diff --git a/quic/fizz/client/test/QuicClientTransportTest.cpp b/quic/fizz/client/test/QuicClientTransportTest.cpp index ce9d6fc74..5301e2afe 100644 --- a/quic/fizz/client/test/QuicClientTransportTest.cpp +++ b/quic/fizz/client/test/QuicClientTransportTest.cpp @@ -1057,7 +1057,7 @@ TEST_F(QuicClientTransportTest, FirstPacketProcessedCallback) { TEST_F(QuicClientTransportTest, CloseSocketOnWriteError) { client->addNewPeerAddress(serverAddr); - EXPECT_CALL(*sock, write(_, _)).WillOnce(SetErrnoAndReturn(EBADF, -1)); + EXPECT_CALL(*sock, write(_, _, _)).WillOnce(SetErrnoAndReturn(EBADF, -1)); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_FALSE(client->isClosed()); @@ -1162,11 +1162,13 @@ TEST_F(QuicClientTransportTest, SocketClosedDuringOnTransportReady) { ConnectionCallbackThatWritesOnTransportReady callback(client); EXPECT_CALL(callback, onTransportReadyMock()); EXPECT_CALL(callback, onReplaySafe()).Times(0); - ON_CALL(*sock, write(_, _)) + ON_CALL(*sock, write(_, _, _)) .WillByDefault(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); + ; })); ON_CALL(*sock, address()).WillByDefault(ReturnRef(serverAddr)); @@ -1181,7 +1183,8 @@ TEST_F(QuicClientTransportTest, NetworkUnreachableIsFatalToConn) { client->addNewPeerAddress(serverAddr); setupCryptoLayer(); EXPECT_CALL(clientConnSetupCallback, onConnectionSetupError(_)); - EXPECT_CALL(*sock, write(_, _)).WillOnce(SetErrnoAndReturn(ENETUNREACH, -1)); + EXPECT_CALL(*sock, write(_, _, _)) + .WillOnce(SetErrnoAndReturn(ENETUNREACH, -1)); client->start(&clientConnSetupCallback, &clientConnCallback); loopForWrites(); } @@ -1380,14 +1383,16 @@ class QuicClientTransportHappyEyeballsTest auto& conn = client->getConn(); setupInitialDcidForRetry(); auto firstPacketType = GetParam(); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); setConnectionIds(); @@ -1406,7 +1411,7 @@ class QuicClientTransportHappyEyeballsTest EXPECT_CALL(clientConnSetupCallback, onTransportReady()); EXPECT_CALL(clientConnSetupCallback, onReplaySafe()); } - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); EXPECT_CALL(*secondSock, pauseRead()); EXPECT_CALL(*secondSock, close()); if (firstPacketType == ServerFirstPacketType::Retry) { @@ -1428,13 +1433,15 @@ class QuicClientTransportHappyEyeballsTest auto firstPacketType = GetParam(); setupInitialDcidForRetry(); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); setConnectionIds(); @@ -1458,13 +1465,15 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); EXPECT_EQ(socketWrites.size(), 1); @@ -1477,14 +1486,16 @@ class QuicClientTransportHappyEyeballsTest EXPECT_CALL(clientConnSetupCallback, onTransportReady()); EXPECT_CALL(clientConnSetupCallback, onReplaySafe()); } - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); EXPECT_CALL(*secondSock, pauseRead()); EXPECT_CALL(*secondSock, close()); if (firstPacketType == ServerFirstPacketType::Retry) { @@ -1506,13 +1517,15 @@ class QuicClientTransportHappyEyeballsTest auto firstPacketType = GetParam(); setupInitialDcidForRetry(); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); setConnectionIds(); EXPECT_EQ(conn.peerAddress, firstAddress); @@ -1536,12 +1549,14 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .WillOnce(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); @@ -1556,15 +1571,17 @@ class QuicClientTransportHappyEyeballsTest EXPECT_CALL(clientConnSetupCallback, onTransportReady()); EXPECT_CALL(clientConnSetupCallback, onReplaySafe()); } - EXPECT_CALL(*sock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); EXPECT_CALL(*sock, pauseRead()); EXPECT_CALL(*sock, close()); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .Times(AtLeast(1)) .WillRepeatedly(Invoke([&](const SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); if (firstPacketType == ServerFirstPacketType::Retry) { recvServerRetry(secondAddress); @@ -1584,7 +1601,7 @@ class QuicClientTransportHappyEyeballsTest auto& conn = client->getConn(); setupInitialDcidForRetry(); - EXPECT_CALL(*sock, write(firstAddress, _)); + EXPECT_CALL(*sock, write(firstAddress, _, _)); EXPECT_CALL(*secondSock, bind(_)) .WillOnce(Invoke( [](const folly::SocketAddress&) { throw std::exception(); })); @@ -1603,9 +1620,9 @@ class QuicClientTransportHappyEyeballsTest auto& conn = client->getConn(); TransportSettings settings; client->setTransportSettings(settings); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EAGAIN, -1)); - EXPECT_CALL(*secondSock, write(_, _)); + EXPECT_CALL(*secondSock, write(_, _, _)); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1615,8 +1632,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_FALSE(client->happyEyeballsConnAttemptDelayTimeout() .isTimerCallbackScheduled()); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1625,13 +1642,13 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& firstAddress, const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EBADF, -1)); // Socket is paused read once during happy eyeballs // Socket is paused read for the second time when QuicClientTransport dies EXPECT_CALL(*sock, pauseRead()).Times(2); EXPECT_CALL(*sock, close()).Times(1); - EXPECT_CALL(*secondSock, write(_, _)); + EXPECT_CALL(*secondSock, write(_, _, _)); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1641,8 +1658,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_FALSE(client->happyEyeballsConnAttemptDelayTimeout() .isTimerCallbackScheduled()); - EXPECT_CALL(*sock, write(_, _)).Times(0); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1652,7 +1669,7 @@ class QuicClientTransportHappyEyeballsTest [[maybe_unused]] const SocketAddress& secondAddress) { #ifdef FOLLY_HAVE_MSG_ERRQUEUE auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); + EXPECT_CALL(*sock, write(firstAddress, _, _)); // Socket is paused read once during happy eyeballs // Socket is paused read for the second time when QuicClientTransport dies EXPECT_CALL(*sock, pauseRead()).Times(2); @@ -1677,8 +1694,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_FALSE(client->happyEyeballsConnAttemptDelayTimeout() .isTimerCallbackScheduled()); - EXPECT_CALL(*sock, write(_, _)).Times(0); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); #endif @@ -1689,8 +1706,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1707,17 +1724,17 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EAGAIN, -1)); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(firstAddress, _)).Times(1); - EXPECT_CALL(*secondSock, write(secondAddress, _)).Times(1); + EXPECT_CALL(*sock, write(firstAddress, _, _)).Times(1); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)).Times(1); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1727,8 +1744,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1745,21 +1762,21 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EBADF, -1)); // Socket is paused read once during happy eyeballs // Socket is paused read for the second time when QuicClientTransport dies EXPECT_CALL(*sock, pauseRead()).Times(2); EXPECT_CALL(*sock, close()).Times(1); - EXPECT_CALL(*secondSock, write(secondAddress, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); EXPECT_FALSE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(_, _)).Times(0); - EXPECT_CALL(*secondSock, write(secondAddress, _)).Times(1); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)).Times(1); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1770,8 +1787,8 @@ class QuicClientTransportHappyEyeballsTest #ifdef FOLLY_HAVE_MSG_ERRQUEUE auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1804,8 +1821,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_FALSE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(_, _)).Times(0); - EXPECT_CALL(*secondSock, write(secondAddress, _)).Times(1); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)).Times(1); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); #endif @@ -1816,8 +1833,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1834,8 +1851,8 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .WillOnce(SetErrnoAndReturn(EAGAIN, -1)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); @@ -1843,8 +1860,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(firstAddress, _)).Times(1); - EXPECT_CALL(*secondSock, write(secondAddress, _)).Times(1); + EXPECT_CALL(*sock, write(firstAddress, _, _)).Times(1); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)).Times(1); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1854,8 +1871,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1872,8 +1889,8 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .WillOnce(SetErrnoAndReturn(EBADF, -1)); // Socket is paused read once during happy eyeballs // Socket is paused read for the second time when QuicClientTransport dies @@ -1885,8 +1902,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_FALSE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(firstAddress, _)).Times(1); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)).Times(1); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1897,8 +1914,8 @@ class QuicClientTransportHappyEyeballsTest #ifdef FOLLY_HAVE_MSG_ERRQUEUE auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1915,7 +1932,7 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)); + EXPECT_CALL(*sock, write(firstAddress, _, _)); union { struct cmsghdr hdr; unsigned char buf[CMSG_SPACE(sizeof(sock_extended_err))]; @@ -1936,8 +1953,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_FALSE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(firstAddress, _)).Times(1); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)).Times(1); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); #endif @@ -1948,8 +1965,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -1966,9 +1983,9 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EAGAIN, -1)); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .WillOnce(SetErrnoAndReturn(EAGAIN, -1)); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); @@ -1976,8 +1993,8 @@ class QuicClientTransportHappyEyeballsTest EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToFirstSocket); EXPECT_TRUE(conn.happyEyeballsState.shouldWriteToSecondSocket); - EXPECT_CALL(*sock, write(firstAddress, _)).Times(1); - EXPECT_CALL(*secondSock, write(secondAddress, _)).Times(1); + EXPECT_CALL(*sock, write(firstAddress, _, _)).Times(1); + EXPECT_CALL(*secondSock, write(secondAddress, _, _)).Times(1); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); } @@ -1987,8 +2004,8 @@ class QuicClientTransportHappyEyeballsTest const SocketAddress& secondAddress) { auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -2005,9 +2022,9 @@ class QuicClientTransportHappyEyeballsTest // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(SetErrnoAndReturn(EBADF, -1)); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .WillOnce(SetErrnoAndReturn(EBADF, -1)); EXPECT_CALL(clientConnSetupCallback, onConnectionSetupError(_)); client->lossTimeout().cancelTimerCallback(); @@ -2023,8 +2040,8 @@ class QuicClientTransportHappyEyeballsTest #ifdef FOLLY_HAVE_MSG_ERRQUEUE auto& conn = client->getConn(); - EXPECT_CALL(*sock, write(firstAddress, _)); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(firstAddress, _, _)); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); client->start(&clientConnSetupCallback, &clientConnCallback); EXPECT_EQ(conn.peerAddress, firstAddress); EXPECT_EQ(conn.happyEyeballsState.secondPeerAddress, secondAddress); @@ -3996,11 +4013,12 @@ TEST_F(QuicClientTransportVersionAndRetryTest, RetryPacket) { std::unique_ptr bytesWrittenToNetwork = nullptr; - EXPECT_CALL(*sock, write(_, _)) + EXPECT_CALL(*sock, write(_, _, _)) .WillRepeatedly(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - bytesWrittenToNetwork = buf->clone(); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + bytesWrittenToNetwork = + copyChain(folly::IOBuf::wrapIov(vec, iovec_len)); + return getTotalIovecLen(vec, iovec_len); })); auto serverCid = recvServerRetry(serverAddr); @@ -4637,7 +4655,8 @@ TEST_F(QuicClientTransportAfterStartTest, WriteThrowsExceptionWhileDraining) { auto err = QuicError( QuicErrorCode(LocalErrorCode::INTERNAL_ERROR), toString(LocalErrorCode::INTERNAL_ERROR).str()); - EXPECT_CALL(*sock, write(_, _)).WillRepeatedly(SetErrnoAndReturn(EBADF, -1)); + EXPECT_CALL(*sock, write(_, _, _)) + .WillRepeatedly(SetErrnoAndReturn(EBADF, -1)); client->close(err); EXPECT_FALSE(client->idleTimeout().isTimerCallbackScheduled()); } @@ -4860,13 +4879,15 @@ TEST_F(QuicClientTransportAfterStartTest, OneCloseFramePerRtt) { auto streamId = client->createBidirectionalStream().value(); auto& conn = client->getNonConstConn(); conn.lossState.srtt = 10s; - EXPECT_CALL(*sock, write(_, _)).WillRepeatedly(Return(100)); + EXPECT_CALL(*sock, write(_, _, _)).WillRepeatedly(Return(100)); loopForWrites(); Mock::VerifyAndClearExpectations(sock); // Close the client transport. There could be multiple writes given how many // ciphers we have. - EXPECT_CALL(*sock, write(_, _)).Times(AtLeast(1)).WillRepeatedly(Return(10)); + EXPECT_CALL(*sock, write(_, _, _)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(10)); client->close(QuicError( QuicErrorCode(LocalErrorCode::INTERNAL_ERROR), toString(LocalErrorCode::INTERNAL_ERROR).str())); @@ -4874,7 +4895,7 @@ TEST_F(QuicClientTransportAfterStartTest, OneCloseFramePerRtt) { Mock::VerifyAndClearExpectations(sock); // Then received some server packet, which won't trigger another close - EXPECT_CALL(*sock, write(_, _)).Times(0); + EXPECT_CALL(*sock, write(_, _, _)).Times(0); auto firstData = folly::IOBuf::copyBuffer( "I got a room full of your posters and your pictures, man"); deliverDataWithoutErrorCheck(packetToBuf(createStreamPacket( @@ -4892,7 +4913,9 @@ TEST_F(QuicClientTransportAfterStartTest, OneCloseFramePerRtt) { conn.lastCloseSentTime = Clock::now() - 10s; conn.lossState.srtt = 1us; // Receive another server packet - EXPECT_CALL(*sock, write(_, _)).Times(AtLeast(1)).WillRepeatedly(Return(10)); + EXPECT_CALL(*sock, write(_, _, _)) + .Times(AtLeast(1)) + .WillRepeatedly(Return(10)); auto secondData = folly::IOBuf::copyBuffer( "Dear Slim, I wrote to you but you still ain't callin'"); deliverDataWithoutErrorCheck(packetToBuf(createStreamPacket( @@ -5424,13 +5447,14 @@ TEST_F( [&](const Optional&, const Buf&) { return true; }, []() -> Buf { return nullptr; }); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillRepeatedly(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(_, _)).Times(0); + EXPECT_CALL(*secondSock, write(_, _, _)).Times(0); startClient(); socketWrites.clear(); auto& conn = client->getConn(); @@ -5460,20 +5484,21 @@ TEST_F( // Manually expire loss timeout to trigger write to both first and second // socket - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .Times(2) .WillRepeatedly(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { socketWrites.push_back( - folly::IOBuf::copyBuffer(buf->data(), buf->length(), 0, 0)); - return buf->length(); + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .Times(2) .WillRepeatedly(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); @@ -5515,11 +5540,12 @@ TEST_F( [&](const Optional&, const Buf&) { return true; }, []() -> Buf { return nullptr; }); - EXPECT_CALL(*sock, write(firstAddress, _)) + EXPECT_CALL(*sock, write(firstAddress, _, _)) .WillOnce(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); startClient(); socketWrites.clear(); @@ -5529,12 +5555,11 @@ TEST_F( ASSERT_TRUE(client->happyEyeballsConnAttemptDelayTimeout() .isTimerCallbackScheduled()); - EXPECT_CALL(*sock, write(firstAddress, _)) - .WillOnce(Invoke( - [&](const SocketAddress&, const std::unique_ptr&) { - errno = EBADF; - return -1; - })); + EXPECT_CALL(*sock, write(firstAddress, _, _)) + .WillOnce(Invoke([&](const SocketAddress&, const struct iovec*, size_t) { + errno = EBADF; + return -1; + })); auto streamId = client->createBidirectionalStream().value(); client->writeChain(streamId, IOBuf::copyBuffer("hello"), true); loopForWrites(); @@ -5546,12 +5571,13 @@ TEST_F( EXPECT_FALSE(client->happyEyeballsConnAttemptDelayTimeout() .isTimerCallbackScheduled()); - EXPECT_CALL(*secondSock, write(secondAddress, _)) + EXPECT_CALL(*secondSock, write(secondAddress, _, _)) .Times(2) .WillRepeatedly(Invoke( - [&](const SocketAddress&, const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); + [&](const SocketAddress&, const struct iovec* vec, size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); })); client->lossTimeout().cancelTimerCallback(); client->lossTimeout().timeoutExpired(); diff --git a/quic/fizz/client/test/QuicClientTransportTestUtil.h b/quic/fizz/client/test/QuicClientTransportTestUtil.h index a4c8a9fe0..23bdeaada 100644 --- a/quic/fizz/client/test/QuicClientTransportTestUtil.h +++ b/quic/fizz/client/test/QuicClientTransportTestUtil.h @@ -489,13 +489,14 @@ class QuicClientTransportTestBase : public virtual testing::Test { void startTransport() { client->addNewPeerAddress(serverAddr); client->setHostname(hostname_); - ON_CALL(*sock, write(testing::_, testing::_)) - .WillByDefault( - testing::Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf) { - socketWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); - })); + ON_CALL(*sock, write(testing::_, testing::_, testing::_)) + .WillByDefault(testing::Invoke([&](const folly::SocketAddress&, + const struct iovec* vec, + size_t iovec_len) { + socketWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); + })); ON_CALL(*sock, address()).WillByDefault(testing::ReturnRef(serverAddr)); setupCryptoLayer(); @@ -575,7 +576,7 @@ class QuicClientTransportTestBase : public virtual testing::Test { EXPECT_CALL(*sock, setErrMessageCallback(client.get())); EXPECT_CALL(*sock, resumeRead(client.get())); EXPECT_CALL(*sock, setErrMessageCallback(nullptr)); - EXPECT_CALL(*sock, write(testing::_, testing::_)) + EXPECT_CALL(*sock, write(testing::_, testing::_, testing::_)) .Times(testing::AtLeast(1)); } diff --git a/quic/server/test/QuicServerTransportTestUtil.h b/quic/server/test/QuicServerTransportTestUtil.h index 5fcfb8ec8..3b90f85da 100644 --- a/quic/server/test/QuicServerTransportTestUtil.h +++ b/quic/server/test/QuicServerTransportTestUtil.h @@ -144,13 +144,14 @@ class QuicServerTransportTestBase : public virtual testing::Test { std::make_unique>( qEvb_); socket = sock.get(); - EXPECT_CALL(*sock, write(testing::_, testing::_)) - .WillRepeatedly( - testing::Invoke([&](const folly::SocketAddress&, - const std::unique_ptr& buf) { - serverWrites.push_back(buf->clone()); - return buf->computeChainDataLength(); - })); + EXPECT_CALL(*sock, write(testing::_, testing::_, testing::_)) + .WillRepeatedly(testing::Invoke([&](const folly::SocketAddress&, + const struct iovec* vec, + size_t iovec_len) { + serverWrites.push_back( + copyChain(folly::IOBuf::wrapIov(vec, iovec_len))); + return getTotalIovecLen(vec, iovec_len); + })); EXPECT_CALL(*sock, address()) .WillRepeatedly(testing::ReturnRef(serverAddr)); supportedVersions = {QuicVersion::MVFST};