QUIC sockets return WRITE_STATUS_BLOCKED on ENOBUFS.
Protected by FLAGS_quic_reloadable_flag_quic_enobufs_blocekd.
PiperOrigin-RevId: 765182479
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 09e6b2a..de37bdf 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -34,6 +34,7 @@
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_mtu_discovery_at_server, false, false, "If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_server_on_wire_ping, true, true, "If true, enable server retransmittable on wire PING.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_version_rfcv2, false, false, "When true, support RFC9369.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enobufs_blocked, false, false, "If true, ENOBUFS socket errors are reported as socket blocked instead of socket failure.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fin_before_completed_http_headers, false, true, "If true, close the connection with error if FIN is received before finish receiving the whole HTTP headers.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_fix_timeouts, true, true, "If true, postpone setting handshake timeout to infinite to handshake complete.")
QUICHE_FLAG(bool, quiche_reloadable_flag_quic_heapless_key_derivation, false, false, "If true, QUIC key derivation uses heapless crypto utils.")
diff --git a/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc b/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc
index 1d5a80f..6e0574d 100644
--- a/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc
+++ b/quiche/quic/core/batch_writer/quic_gso_batch_writer.cc
@@ -12,7 +12,7 @@
#include "quiche/quic/core/flow_label.h"
#include "quiche/quic/core/quic_linux_socket_utils.h"
-#include "quiche/quic/platform/api/quic_server_stats.h"
+#include "quiche/quic/platform/api/quic_flags.h"
namespace quic {
@@ -32,7 +32,8 @@
supports_release_time_(
GetQuicRestartFlag(quic_support_release_time_for_gso) &&
QuicLinuxSocketUtils::EnableReleaseTime(fd,
- clockid_for_release_time)) {
+ clockid_for_release_time)),
+ enobufs_blocked_(GetQuicReloadableFlag(quic_enobufs_blocked)) {
if (supports_release_time_) {
QUIC_RESTART_FLAG_COUNT(quic_support_release_time_for_gso);
}
diff --git a/quiche/quic/core/batch_writer/quic_gso_batch_writer.h b/quiche/quic/core/batch_writer/quic_gso_batch_writer.h
index 5e82952..b2d560f 100644
--- a/quiche/quic/core/batch_writer/quic_gso_batch_writer.h
+++ b/quiche/quic/core/batch_writer/quic_gso_batch_writer.h
@@ -85,7 +85,8 @@
cmsg_builder(&hdr, first.self_address, gso_size, first.release_time,
first.params.ecn_codepoint, first.params.flow_label);
- write_result = QuicLinuxSocketUtils::WritePacket(fd(), hdr);
+ write_result =
+ QuicLinuxSocketUtils::WritePacket(fd(), hdr, enobufs_blocked_);
QUIC_DVLOG(1) << "Write GSO packet result: " << write_result
<< ", fd: " << fd()
<< ", self_address: " << first.self_address.ToString()
@@ -118,6 +119,8 @@
const clockid_t clockid_for_release_time_;
const bool supports_release_time_;
+
+ const bool enobufs_blocked_ = false;
};
} // namespace quic
diff --git a/quiche/quic/core/quic_linux_socket_utils.cc b/quiche/quic/core/quic_linux_socket_utils.cc
index 8134eea..c56d939 100644
--- a/quiche/quic/core/quic_linux_socket_utils.cc
+++ b/quiche/quic/core/quic_linux_socket_utils.cc
@@ -7,11 +7,13 @@
#include <linux/net_tstamp.h>
#include <netinet/in.h>
+#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <string>
#include "quiche/quic/core/quic_syscall_wrapper.h"
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_flag_utils.h"
#include "quiche/quic/platform/api/quic_ip_address.h"
#include "quiche/quic/platform/api/quic_logging.h"
@@ -263,7 +265,8 @@
}
// static
-WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr) {
+WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr,
+ bool enobufs_blocked) {
int rc;
do {
rc = GetGlobalSyscallWrapper()->Sendmsg(fd, hdr.hdr(), 0);
@@ -273,7 +276,11 @@
}
if (errno == ENOBUFS) {
QUIC_CODE_COUNT(quic_sendmsg_enobufs);
- errno = ENOBUFS;
+ if (enobufs_blocked) {
+ return WriteResult(WRITE_STATUS_BLOCKED, errno);
+ } else {
+ errno = ENOBUFS;
+ }
}
return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK)
? WRITE_STATUS_BLOCKED
diff --git a/quiche/quic/core/quic_linux_socket_utils.h b/quiche/quic/core/quic_linux_socket_utils.h
index f125cba..1c0b86d 100644
--- a/quiche/quic/core/quic_linux_socket_utils.h
+++ b/quiche/quic/core/quic_linux_socket_utils.h
@@ -281,7 +281,8 @@
cmsghdr* cmsg);
// Writes the packet in |hdr| to the socket, using ::sendmsg.
- static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr);
+ static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr,
+ bool enobufs_blocked);
// Writes the packets in |mhdr| to the socket, using ::sendmmsg if available.
static WriteResult WriteMultiplePackets(int fd, QuicMMsgHdr* mhdr,
diff --git a/quiche/quic/core/quic_linux_socket_utils_test.cc b/quiche/quic/core/quic_linux_socket_utils_test.cc
index 93ebf01..1a910fe 100644
--- a/quiche/quic/core/quic_linux_socket_utils_test.cc
+++ b/quiche/quic/core/quic_linux_socket_utils_test.cc
@@ -7,11 +7,13 @@
#include <netinet/in.h>
#include <stdint.h>
+#include <cerrno>
#include <cstddef>
#include <sstream>
#include <string>
#include <vector>
+#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/quic_mock_syscall_wrapper.h"
#include "quiche/common/quiche_circular_deque.h"
@@ -19,6 +21,7 @@
using testing::_;
using testing::InSequence;
using testing::Invoke;
+using testing::SetErrnoAndReturn;
namespace quic {
namespace test {
@@ -323,6 +326,16 @@
}
}
+TEST_F(QuicLinuxSocketUtilsTest, WriteReturnsEnobufs) {
+ QuicMsgHdr hdr(nullptr, 0, nullptr, 0);
+ EXPECT_CALL(mock_syscalls_, Sendmsg)
+ .WillRepeatedly(SetErrnoAndReturn(ENOBUFS, -1));
+ EXPECT_EQ(WriteResult(WRITE_STATUS_BLOCKED, ENOBUFS),
+ QuicLinuxSocketUtils::WritePacket(/*fd=*/1, hdr, true));
+ EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, ENOBUFS),
+ QuicLinuxSocketUtils::WritePacket(/*fd=*/1, hdr, false));
+}
+
} // namespace
} // namespace test
} // namespace quic