coap_net.c: Do not send an Empty message as a separate response - #2141
Conversation
A request handler that leaves the response code unset asks for no response to be sent. That is honoured for a piggybacked ACK (the token gets stripped, so an empty ACK goes out) and for a non-confirmable or reliable response (dropped in no_response()), but not for a separate response set up with coap_register_async(). There the response is turned into a CON, and a 0.00 CON was put on the wire. An Empty message is a ping (RFC 7252 4.2), not a response, and the PDU still carries the request's token, which RFC 7252 4.1 does not allow. Being confirmable, it was then retransmitted up to MAX_RETRANSMIT times, usually at a client port that had long gone away. Drop it like the other two cases, delete it without logging it as a dropped response since the application asked for this, and document in coap_handler(3) that a separate response is not sent either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Your changes make sense as a safety net. But, when a request handler is called, the provided response packet type is set to ACK (if request is CON), and so when the request handler returns, providing it does not set a response code, an ACK 0.0 is returned so the client knows it is a separate response and does not retry sending the request. Then when the async (separate handler) triggers, it will send the response packet with a CON 2.0x. (for NON request, the response is set to NON, which then gets dropped if there is no response code). So, I am not sure how the request handler is returning a CON 0.0 after calling |
|
A debug log may help here. |
sequenceDiagram
participant C as Client
participant S as libcoap server
C->>S: CON POST /announce (mid=47ad, token=94ec…)
Note over S: handle_request(), async == NULL<br/>handler #1: registers async, sets no code
S-->>C: ✓ ACK 0.00, mid=47ad, token stripped (:4288)
Note over C: receipt — stops retransmitting
Note over S: async fires → coap_check_async() (:5405)<br/>handle_request() again, async != NULL<br/>handler #2: nothing to answer, sets no code
Note over S: type flipped ACK→CON (:4038)<br/>request token added (:4046)<br/>no_response() covers NON/reliable only (:3674)
S-->>C: ✗ CON 0.00, mid=47ad, token=94ec… (malformed)
C-->>S: RST — a 0.00 CON reads as a ping
S-->>C: retransmit × MAX_RETRANSMIT
|
|
Makes sense that when the async fires, the second call to the request handler does not update response code (unexpected). Yes, the Libcoap library does not like empty packets that are not empty. |
A request handler that leaves the response code unset asks for no response to be sent. That is honoured for a piggybacked ACK (the token gets stripped, so an empty ACK goes out) and for a non-confirmable or reliable response (dropped in no_response()), but not for a separate response set up with coap_register_async().
There the response is turned into a CON, and a 0.00 CON was put on the wire. An Empty message is a ping (RFC 7252 4.2), not a response, and the PDU still carries the request's token, which RFC 7252 4.1 does not allow. Being confirmable, it was then retransmitted up to MAX_RETRANSMIT times, usually at a client port that had long gone away.
Drop it like the other two cases, delete it without logging it as a dropped response since the application asked for this, and document in coap_handler(3) that a separate response is not sent either.