Loading src/crypto/tls.cpp +15 −2 Original line number Diff line number Diff line Loading @@ -3480,8 +3480,21 @@ namespace netplus { sent = _socket->sendRaw(raw_buf, 0); } catch (NetException& e) { if (e.getErrorType() == NetException::Note) { // IOCP write already pending — stop flushing; // records remain queued for the next write completion. if (_socket->isBlocking()) { // A blocking socket can only report EAGAIN/EWOULDBLOCK here if a // SO_SNDTIMEO deadline was configured and just expired -- there is no // event loop that will ever come back and retry this write for us // (unlike the non-blocking case below), so silently returning here // would leave send_record/send_queue's bytes stuck forever while every // caller up the stack (tls::sendData()'s own Note-swallowing catch, // ultimately the application) believes the write already succeeded. // Surface it as a real failure instead. NetException timeout; timeout[NetException::Error] << "tls::flush_out: send timed out"; throw timeout; } // Non-blocking socket not yet writable (or an IOCP write already pending) -- // stop flushing; records remain queued for the next write completion. return; } throw; Loading src/posix/tcp.cpp +12 −0 Original line number Diff line number Diff line Loading @@ -188,10 +188,12 @@ void tcp::listen() { void tcp::setNonBlock() { setFlag(O_NONBLOCK, 1); _blocking = false; } void tcp::setBlock() { setFlag(O_NONBLOCK, 0); _blocking = true; } int tcp::getMaxconnections() { Loading Loading @@ -239,6 +241,16 @@ void tcp::accept(std::unique_ptr<socket>& csock, bool nonblock) { csock->_Addr = peer; csock->_AddrLen = peerLen; // Both branches above set the OS-level flag directly on the raw fd (accept4's flag, or a // manual fcntl on `cfd` before csock even owns it) rather than through csock's own // setNonBlock() -- keep its tracked isBlocking() state consistent with what the fd actually // is, now that csock does own it. Matters beyond bookkeeping: tls::flush_out() relies on // isBlocking() to tell a genuine send timeout (blocking) apart from "not writable yet, event // loop will retry" (non-blocking) -- every accepted connection is non-blocking by // construction (this is the event loop's own accept path), so getting this wrong here would // make flush_out() treat completely normal EAGAINs as hard failures. if (nonblock) csock->setNonBlock(); // Disable Nagle's algorithm — reduces latency for small HTTP frames int one = 1; ::setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); Loading src/posix/udp.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -192,10 +192,12 @@ void udp::listen() { void udp::setNonBlock() { setFlag(O_NONBLOCK, 1); _blocking = false; } void udp::setBlock() { setFlag(O_NONBLOCK, 0); _blocking = true; } int udp::getMaxconnections() { Loading src/socket.h +10 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,15 @@ namespace netplus { bool _Bound = false; bool _Listening = false; // Tracked by every setBlock()/setNonBlock() override so callers -- notably // tls::flush_out() -- can tell whether an EAGAIN/EWOULDBLOCK just observed on this fd // means "try again once the event loop says writable" (non-blocking: expected, normal) // or "the SO_SNDTIMEO/SO_RCVTIMEO deadline just expired" (blocking: a genuinely blocking // socket without a timeout configured cannot return EAGAIN at all, so for a blocking // socket this is unambiguously a timeout, not a retry hint). Defaults to true since a // freshly created fd is blocking until something calls setNonBlock(). bool isBlocking() const { return _blocking; } // Mark this socket as associated with an IOCP handle. // Only IOCP-bound sockets should use overlapped WSASend/WSARecv. void setIocpAssociated(bool v) { _iocpAssociated = v; } Loading @@ -266,6 +275,7 @@ namespace netplus { protected: std::atomic<bool> _pendingIocpWrite{false}; bool _iocpAssociated = false; bool _blocking = true; // see isBlocking() above; kept up to date by setBlock()/setNonBlock() // ✅ legacy helper still available -- copies from an addrinfo owned by // the caller (e.g. still-linked into getaddrinfo()'s result list), no // allocation/ownership of its own. See setAddrFromAI() below, which Loading src/windows/tcp.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,7 @@ void tcp::listen() { void tcp::setNonBlock() { u_long mode = 1; ::ioctlsocket(_Socket, FIONBIO, &mode); _blocking = false; } int tcp::getMaxconnections() { Loading Loading @@ -435,6 +436,7 @@ void tcp::setBlock() { e[NetException::Error] << "tcp::setBlock failed"; throw e; } _blocking = true; } void tcp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) { Loading Loading
src/crypto/tls.cpp +15 −2 Original line number Diff line number Diff line Loading @@ -3480,8 +3480,21 @@ namespace netplus { sent = _socket->sendRaw(raw_buf, 0); } catch (NetException& e) { if (e.getErrorType() == NetException::Note) { // IOCP write already pending — stop flushing; // records remain queued for the next write completion. if (_socket->isBlocking()) { // A blocking socket can only report EAGAIN/EWOULDBLOCK here if a // SO_SNDTIMEO deadline was configured and just expired -- there is no // event loop that will ever come back and retry this write for us // (unlike the non-blocking case below), so silently returning here // would leave send_record/send_queue's bytes stuck forever while every // caller up the stack (tls::sendData()'s own Note-swallowing catch, // ultimately the application) believes the write already succeeded. // Surface it as a real failure instead. NetException timeout; timeout[NetException::Error] << "tls::flush_out: send timed out"; throw timeout; } // Non-blocking socket not yet writable (or an IOCP write already pending) -- // stop flushing; records remain queued for the next write completion. return; } throw; Loading
src/posix/tcp.cpp +12 −0 Original line number Diff line number Diff line Loading @@ -188,10 +188,12 @@ void tcp::listen() { void tcp::setNonBlock() { setFlag(O_NONBLOCK, 1); _blocking = false; } void tcp::setBlock() { setFlag(O_NONBLOCK, 0); _blocking = true; } int tcp::getMaxconnections() { Loading Loading @@ -239,6 +241,16 @@ void tcp::accept(std::unique_ptr<socket>& csock, bool nonblock) { csock->_Addr = peer; csock->_AddrLen = peerLen; // Both branches above set the OS-level flag directly on the raw fd (accept4's flag, or a // manual fcntl on `cfd` before csock even owns it) rather than through csock's own // setNonBlock() -- keep its tracked isBlocking() state consistent with what the fd actually // is, now that csock does own it. Matters beyond bookkeeping: tls::flush_out() relies on // isBlocking() to tell a genuine send timeout (blocking) apart from "not writable yet, event // loop will retry" (non-blocking) -- every accepted connection is non-blocking by // construction (this is the event loop's own accept path), so getting this wrong here would // make flush_out() treat completely normal EAGAINs as hard failures. if (nonblock) csock->setNonBlock(); // Disable Nagle's algorithm — reduces latency for small HTTP frames int one = 1; ::setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); Loading
src/posix/udp.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -192,10 +192,12 @@ void udp::listen() { void udp::setNonBlock() { setFlag(O_NONBLOCK, 1); _blocking = false; } void udp::setBlock() { setFlag(O_NONBLOCK, 0); _blocking = true; } int udp::getMaxconnections() { Loading
src/socket.h +10 −0 Original line number Diff line number Diff line Loading @@ -258,6 +258,15 @@ namespace netplus { bool _Bound = false; bool _Listening = false; // Tracked by every setBlock()/setNonBlock() override so callers -- notably // tls::flush_out() -- can tell whether an EAGAIN/EWOULDBLOCK just observed on this fd // means "try again once the event loop says writable" (non-blocking: expected, normal) // or "the SO_SNDTIMEO/SO_RCVTIMEO deadline just expired" (blocking: a genuinely blocking // socket without a timeout configured cannot return EAGAIN at all, so for a blocking // socket this is unambiguously a timeout, not a retry hint). Defaults to true since a // freshly created fd is blocking until something calls setNonBlock(). bool isBlocking() const { return _blocking; } // Mark this socket as associated with an IOCP handle. // Only IOCP-bound sockets should use overlapped WSASend/WSARecv. void setIocpAssociated(bool v) { _iocpAssociated = v; } Loading @@ -266,6 +275,7 @@ namespace netplus { protected: std::atomic<bool> _pendingIocpWrite{false}; bool _iocpAssociated = false; bool _blocking = true; // see isBlocking() above; kept up to date by setBlock()/setNonBlock() // ✅ legacy helper still available -- copies from an addrinfo owned by // the caller (e.g. still-linked into getaddrinfo()'s result list), no // allocation/ownership of its own. See setAddrFromAI() below, which Loading
src/windows/tcp.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,7 @@ void tcp::listen() { void tcp::setNonBlock() { u_long mode = 1; ::ioctlsocket(_Socket, FIONBIO, &mode); _blocking = false; } int tcp::getMaxconnections() { Loading Loading @@ -435,6 +436,7 @@ void tcp::setBlock() { e[NetException::Error] << "tcp::setBlock failed"; throw e; } _blocking = true; } void tcp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) { Loading