Description
When creating an async TCP/IP connection, the pending client socket has to be monitored to detect a successful connection or a "Connection refused" error. Unix-like operating systems use a writable event for both events (which is what ReactPHP currently assumes), whereas Windows uses a writable event for successful connections, but an exceptional event for connection refused events (https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select?redirectedfrom=MSDN).
We currently only provide interfaces to monitor readable and writable events, with no way to monitor "exceptional" events (i.e. the exceptfds
for the select
call). This means that we can not currently detect refused connections on Windows using the default StreamSelectLoop
. Higher-level components (like react/socket) may detect this as a timeout condition, but the immediate events is not currently reported.
It's not too hard to expose the exceptfds
through our interfaces, but this would introduce a BC break and given its limited use cases is probably not worth it. Additionally, poll
/epoll
expose a POLLPRI
/EPOLLPRI
flag, but the higher-level event loop implementations do not seem to expose this.
As an alternative, I've researched ways to automatically add a connecting socket to both the writefds
and the exceptfds
structures automatically on Windows. This matches with how ExtEventLoop
reports a writable events on Windows for this situation and thus matches behavior on other platforms. The major problem with this approach is that there does not appear to be reliable way to detect whether a socket is in a "pending connection" state. If a socket that is not in a pending connection state is added to the exceptfds
structure, the select
call may also report out-of-band TCP/IP data ("urgent data"). While somewhat rarely used in practice, this may affect normal program execution with specially crafted network packages.
- https://docs.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select?redirectedfrom=MSDN
- https://docs.microsoft.com/en-us/windows/win32/winsock/protocol-independent-out-of-band-data-2
- http://man7.org/linux/man-pages/man2/select.2.html
- http://man7.org/linux/man-pages/man2/poll.2.html
- http://man7.org/linux/man-pages/man7/tcp.7.html
I've stumbled upon this while writing some test cases to solve #188. I'm still working on a work-around for this and appreciate any input 👍