fix(server): treat a client disconnect as a normal end, not an error - #847
Conversation
Client.disconnect() sends a COTP Disconnect Request, which receive_data()
rejected as an unexpected PDU, so an ordinary goodbye from the library's
own client was logged as
ERROR Error handling client ('127.0.0.1', 58143): Expected COTP DT, got 0x80
COTP_DR and COTP_DC were already defined but never used on the receiving
side. A DR is now confirmed with a DC and ends the connection through the
path _handle_client already treats as a normal disconnect. Sending the
confirmation is best effort, since a client that closes right after the
request may already be gone.
A peer that goes away before the ISO handshake completes was logged the
same way; that is routine (port scans, health checks, a cancelled connect)
and is now reported at info level.
Measured against the library's own client and a raw socket: a clean
disconnect after a request, a clean disconnect without one, a plain TCP
close and an abrupt reset all logged an error before, and none do now.
gijzelaerr
left a comment
There was a problem hiding this comment.
There is one remaining noisy path in the same scenario. accept_connection() now logs an early peer departure at info and returns False, but _handle_client() immediately turns that result into logger.warning("Failed to establish ISO connection ..."). Port scans, health checks, and cancelled handshakes therefore still produce a warning even though the new comment says they are routine.
Please change that outer log to the intended normal severity (or return enough information to distinguish routine departure from a malformed handshake), and add a log-level regression test for the complete _handle_client() path. The existing tests call receive_data() directly and do not cover the remaining warning.
|
Done — the outer Every path that makes Added Unrelated, noticed while running the checks: |
gijzelaerr
left a comment
There was a problem hiding this comment.
The duplicate outer warning is gone, malformed handshakes still retain their error log, and the new end-to-end socket tests cover all four requested paths. I also ran the complete server test module locally: 72 passed.
Fixes #846.
Client.disconnect()sends a COTP Disconnect Request, whichreceive_data()rejected as an unexpected PDU, so an ordinary goodbye from the library's own client was logged as an error:COTP_DRandCOTP_DCwere already defined inServerISOConnectionbut never used on the receiving side. A DR is now confirmed with a DC and ends the connection through the path_handle_client()already treats as a normal disconnect. Sending the confirmation is best effort — a client that closes right after the request may already be gone, which is what the library's own client does.accept_connection()logged a peer that leaves before the handshake completes the same way; that is routine, so it is reported at info level now.Measured, before and after, on the four ways a client can leave — a clean disconnect after a request, a clean disconnect without one, a plain TCP close and an abrupt reset. All four logged an error before; none do now, and the normal disconnect reads:
Three tests cover it: the DC framing, that a DR ends the connection and is confirmed, and that it still ends cleanly when the confirmation cannot be sent. Each fails without the change.
pytestis 1774 passed, andmypyandruffreport the same counts as master.One thing I did not touch:
snap7/connection.pyandsnap7/async_client.pyhave the sameExpected COTP DTcheck on the client side, so a device that sends a DR would be reported as an error there too. I have no hardware to see whether a real CPU does that, so I left it alone rather than guess.