Skip to content

fix(server): treat a client disconnect as a normal end, not an error - #847

Open
Poseidonas wants to merge 1 commit into
gijzelaerr:masterfrom
Poseidonas:fix-cotp-disconnect
Open

fix(server): treat a client disconnect as a normal end, not an error#847
Poseidonas wants to merge 1 commit into
gijzelaerr:masterfrom
Poseidonas:fix-cotp-disconnect

Conversation

@Poseidonas

Copy link
Copy Markdown

Fixes #846.

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 an error:

ERROR  Error handling client ('127.0.0.1', 58143): Expected COTP DT, got 0x80

COTP_DR and COTP_DC were already defined in ServerISOConnection 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 — 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:

DEBUG  Received COTP DR from client
INFO   Client ('127.0.0.1', 58156) disconnected

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. pytest is 1774 passed, and mypy and ruff report the same counts as master.

One thing I did not touch: snap7/connection.py and snap7/async_client.py have the same Expected COTP DT check 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.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pure-Python server logs a normal client disconnect as an error

1 participant