|
|
@@ -2020,6 +2020,10 @@ private:
|
|
|
|
|
|
int close_socket(socket_t sock) noexcept;
|
|
|
|
|
|
+bool is_accept_resource_error();
|
|
|
+
|
|
|
+bool is_accept_transient_error();
|
|
|
+
|
|
|
ssize_t write_headers(Stream &strm, const Headers &headers);
|
|
|
|
|
|
bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
|
|
|
@@ -6925,6 +6929,36 @@ inline bool is_connection_error() {
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+// accept() failed because the process or the network stack is temporarily out
|
|
|
+// of resources. The listening socket is still usable, so back off briefly and
|
|
|
+// try again.
|
|
|
+inline bool is_accept_resource_error() {
|
|
|
+#ifdef _WIN32
|
|
|
+ auto err = WSAGetLastError();
|
|
|
+ return err == WSAEMFILE || err == WSAENOBUFS;
|
|
|
+#else
|
|
|
+ auto err = errno;
|
|
|
+ return err == EMFILE || err == ENFILE || err == ENOBUFS || err == ENOMEM;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+// accept() failed for a reason that says nothing about the listening socket:
|
|
|
+// the pending connection went away before it could be accepted, or the call
|
|
|
+// was interrupted. Retry immediately. WSAAccept()'s own documentation omits
|
|
|
+// WSAECONNRESET, but the accept() it wraps reports an aborted pending
|
|
|
+// connection that way.
|
|
|
+inline bool is_accept_transient_error() {
|
|
|
+#ifdef _WIN32
|
|
|
+ auto err = WSAGetLastError();
|
|
|
+ return err == WSAEINTR || err == WSAEWOULDBLOCK || err == WSAECONNRESET ||
|
|
|
+ err == WSAECONNABORTED;
|
|
|
+#else
|
|
|
+ auto err = errno;
|
|
|
+ return err == EINTR || err == EAGAIN || err == EWOULDBLOCK ||
|
|
|
+ err == ECONNABORTED;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
inline bool bind_ip_address(socket_t sock, const std::string &host) {
|
|
|
struct addrinfo hints;
|
|
|
struct addrinfo *result;
|
|
|
@@ -13424,12 +13458,16 @@ inline bool Server::listen_internal() {
|
|
|
#endif
|
|
|
|
|
|
if (sock == INVALID_SOCKET) {
|
|
|
- if (errno == EMFILE) {
|
|
|
- // The per-process limit of open file descriptors has been reached.
|
|
|
- // Try to accept new connections after a short sleep.
|
|
|
+ // NOTE: Winsock reports failures through WSAGetLastError() and never
|
|
|
+ // touches the CRT errno, so the two have to be asked platform by
|
|
|
+ // platform rather than by testing errno here.
|
|
|
+ if (detail::is_accept_resource_error()) {
|
|
|
+ // The per-process descriptor limit or the network stack's buffer
|
|
|
+ // space has been reached. Try to accept new connections after a
|
|
|
+ // short sleep.
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds{1});
|
|
|
continue;
|
|
|
- } else if (errno == EINTR || errno == EAGAIN) {
|
|
|
+ } else if (detail::is_accept_transient_error()) {
|
|
|
continue;
|
|
|
}
|
|
|
// Take the descriptor out of svr_sock_ before closing it: a later
|