Proxy Destination Timeout Does Not Produce 5.04 In Runtime
Result
RFC 7252 requires a proxy to return 5.04 Gateway Timeout when the request to the destination times out. libcoap defines the 5.04 response code, but the reviewed proxy implementation does not show a generic timeout-to-5.04 mapping, and a runtime client -> libcoap proxy -> blackhole upstream experiment confirmed that a true destination timeout ends in downstream client timeout rather than a 5.04 response.
Standard Basis
RFC link: RFC 7252 Section 5.7.1, Proxy Operation
If the request to the destination times out, then a 5.04 (Gateway Timeout)
response MUST be returned. If the request to the destination returns a
response that cannot be processed by the proxy (e.g, due to unrecognized
critical options or message format errors), then a 5.02 (Bad Gateway)
response MUST be returned. Otherwise, the proxy returns the response to the
client.
RFC link: RFC 7252 Section 10.1, CoAP-HTTP Proxying
If the proxy is unable or unwilling to service a request with an HTTP URI, a
5.05 (Proxying Not Supported) response is returned to the client. If the
proxy services the request by interacting with a third party (such as the
HTTP origin server) and is unable to obtain a result within a reasonable time
frame, a 5.04 (Gateway Timeout) response is returned; if a result can be
obtained but is not understood, a 5.02 (Bad Gateway) response is returned.
Source Evidence
Source file: include/coap3/coap_pdu.h
COAP_RESPONSE_CODE_GATEWAY_TIMEOUT = COAP_RESPONSE_CODE(504),
Source file: include/coap3/coap_proxy_internal.h
/**
* Idle timeout inactive proxy sessions as well as return in @p tim_rem the time
* to remaining to timeout the inactive proxy.
*/
Source file: src/coap_proxy.c
if (proxy_entry->ongoing && proxy_entry->idle_timeout_ticks) {
if (proxy_entry->last_used + proxy_entry->idle_timeout_ticks <= now) {
/* Drop session to upstream server (which may remove proxy entry) */
if (coap_proxy_remove_association(proxy_entry->ongoing, 0))
i--;
}
}
Source file: src/coap_proxy.c
if (send_failure) {
coap_pdu_t *response;
/* Need to send back a gateway failure */
response = coap_pdu_init(proxy_req->pdu->type,
COAP_RESPONSE_CODE(502),
coap_new_message_id_lkd(proxy_entry->incoming),
coap_session_max_pdu_size_lkd(proxy_entry->incoming));
}
Source file: src/coap_proxy.c
if (proxy_entry->ongoing == session) {
coap_session_t *ongoing;
coap_proxy_cleanup_entry(proxy_entry, send_failure);
ongoing = proxy_entry->ongoing;
coap_session_release_lkd(ongoing);
return 1;
}
Comparison
The RFC distinguishes two cases:
- A true destination timeout, which must map to
5.04.
- A bad or unprocessable upstream result, which must map to
5.02.
The reviewed source does not show one generic branch that converts upstream timeout into 5.04. The coap_proxy_check_timeouts() path is explicitly documented as idle timeout cleanup for inactive proxy sessions, so it should not by itself be treated as proof of request-timeout semantics. However, it is still relevant because it shows cleanup without a 5.04 response.
The explicit proxy failure response path that is present uses 5.02, not 5.04. In addition, reviewed session-removal paths call coap_proxy_remove_association(..., 0), which removes pending proxy state without sending a timeout response.
So the corrected source-level conclusion is:
- The earlier claim that idle-timeout cleanup alone proves RFC timeout mismatch was too strong.
- But the source still does not demonstrate a clear universal timeout-to-
5.04 mapping.
Runtime Experiment
Runtime harness: test-libcoap/runtime/id286_proxy_timeout/run_id286_proxy_timeout.py
This experiment exercised two cases using the built libcoap binaries:
client -> libcoap forward proxy -> blackhole upstream
The upstream endpoint was a UDP server that received the forwarded CoAP request but intentionally never replied, avoiding closed-port ICMP behavior and creating a true destination-timeout condition.
client -> libcoap forward proxy -> unresolvable upstream
This served as a control case for a bad-gateway-style forwarding failure.
Runtime summary file:
test-libcoap/runtime/id286_proxy_timeout/logs/summary.txt
Observed blackhole result:
- The downstream client sent a proxied GET to
coap://127.0.0.1:5699/slow.
- The proxy created an upstream association and forwarded the request.
- The blackhole upstream received 3 request packets from the proxy, confirming real destination delivery and retransmission.
- The proxy later deleted the proxied request and released the proxy entry.
- The downstream client received only an empty ACK and then timed out locally with
No response received within the timeout.
- No
5.04, 5.02, or 5.00 response was returned to the downstream client.
Observed unresolvable result:
- The downstream client received an empty ACK followed quickly by
5.02.
- This confirms libcoap does produce a
5.02 mapping for at least one bad-gateway-style forwarding failure.
Representative blackhole log evidence from proxy_blackhole.log:
Proxy URI 'coap://127.0.0.1:5699/slow'
... proxy_entry 127.0.0.1:5699 ... created ...
... proxy add ...
... proxy fwd ...
... netif: sent 19 bytes
... retransmission #1 ...
... retransmission #2 ...
... proxy del ...
... proxy_entry ... released ...
Representative downstream client evidence from client_blackhole.log:
v:1 t:ACK c:0.00 ...
INFO coap_send_recv: No response received within the timeout
Representative control evidence from client_unresolvable.log:
v:1 t:CON c:5.02 ...
5.02
Conclusion
The runtime test now confirms the meaningful part of the mismatch claim:
- For a true destination timeout, libcoap proxying did not return the RFC-mandated
5.04 Gateway Timeout.
- Instead, the proxy forwarded the request, retransmitted upstream, and then cleaned up local proxy state while the downstream client eventually timed out.
- A separate control case showed that libcoap does emit
5.02 for at least one bad-gateway-style failure, so the absence of 5.04 is not just a failure to emit all proxy errors.
This should therefore remain a partial result, but with stronger wording than before: the original static argument was imprecise, while the runtime blackhole experiment dynamically confirms that a real destination-timeout case is not mapped to 5.04.
Test Result
source_assertions_251_300.py
PASS payload_marker_followed_by_zero_length_rejected
PASS nonzero_payload_encoder_adds_marker
PASS payload_pointer_derived_after_marker
PASS proxy_uri_parser_uses_proxy_mode
PASS uri_split_options_supported
PASS proxy_unavailable_returns_505
PASS proxy_endpoint_authority_can_be_local
PASS delete_unknown_resource_returns_202
PASS bad_option_error_response_path
PASS reserved_response_class_3_accepted
PASS etag_match_large_response_sets_203_without_payload
PASS proxy_forward_response_uses_large_response_helper
PASS proxy_observe_cache_insertion_present
PASS proxy_observe_cache_has_no_response_code_guard
PASS proxy_cleanup_uses_502_not_504
PASS gateway_timeout_code_defined
PASS cn_authority_validation_delegated_to_application
ALL ASSERTIONS PASSED
repro_251_300_protocol_checks.exe
PASS payload marker followed by zero-length payload is rejected
PASS payload marker followed by payload is accepted
PASS payload length is derived from remaining datagram bytes
PASS non-zero payload is encoded with 0xff marker
PASS zero-length payload does not encode payload marker
PASS Proxy-Uri parser accepts HTTP URI for proxy use
PASS origin URI parser rejects proxy-only HTTP URI
PASS origin URI parser splits coap URI authority/path/query
ALL PROTOCOL CHECKS PASSED
runtime id286 proxy timeout experiment
PASS blackhole upstream received forwarded request packets
PASS proxy created and later released upstream association
PASS downstream client received no 5.04 in true timeout case
PASS downstream client timed out after empty ACK in true timeout case
PASS unresolvable upstream control case returned 5.02
Proxy Destination Timeout Does Not Produce 5.04 In Runtime
Result
RFC 7252 requires a proxy to return
5.04 Gateway Timeoutwhen the request to the destination times out. libcoap defines the5.04response code, but the reviewed proxy implementation does not show a generic timeout-to-5.04mapping, and a runtimeclient -> libcoap proxy -> blackhole upstreamexperiment confirmed that a true destination timeout ends in downstream client timeout rather than a5.04response.Standard Basis
RFC link: RFC 7252 Section 5.7.1, Proxy Operation
RFC link: RFC 7252 Section 10.1, CoAP-HTTP Proxying
Source Evidence
Source file:
include/coap3/coap_pdu.hSource file:
include/coap3/coap_proxy_internal.hSource file:
src/coap_proxy.cSource file:
src/coap_proxy.cSource file:
src/coap_proxy.cComparison
The RFC distinguishes two cases:
5.04.5.02.The reviewed source does not show one generic branch that converts upstream timeout into
5.04. Thecoap_proxy_check_timeouts()path is explicitly documented as idle timeout cleanup for inactive proxy sessions, so it should not by itself be treated as proof of request-timeout semantics. However, it is still relevant because it shows cleanup without a5.04response.The explicit proxy failure response path that is present uses
5.02, not5.04. In addition, reviewed session-removal paths callcoap_proxy_remove_association(..., 0), which removes pending proxy state without sending a timeout response.So the corrected source-level conclusion is:
5.04mapping.Runtime Experiment
Runtime harness:
test-libcoap/runtime/id286_proxy_timeout/run_id286_proxy_timeout.pyThis experiment exercised two cases using the built libcoap binaries:
client -> libcoap forward proxy -> blackhole upstreamThe upstream endpoint was a UDP server that received the forwarded CoAP request but intentionally never replied, avoiding closed-port ICMP behavior and creating a true destination-timeout condition.
client -> libcoap forward proxy -> unresolvable upstreamThis served as a control case for a bad-gateway-style forwarding failure.
Runtime summary file:
Observed blackhole result:
coap://127.0.0.1:5699/slow.No response received within the timeout.5.04,5.02, or5.00response was returned to the downstream client.Observed unresolvable result:
5.02.5.02mapping for at least one bad-gateway-style forwarding failure.Representative blackhole log evidence from
proxy_blackhole.log:Representative downstream client evidence from
client_blackhole.log:Representative control evidence from
client_unresolvable.log:Conclusion
The runtime test now confirms the meaningful part of the mismatch claim:
5.04 Gateway Timeout.5.02for at least one bad-gateway-style failure, so the absence of5.04is not just a failure to emit all proxy errors.This should therefore remain a
partialresult, but with stronger wording than before: the original static argument was imprecise, while the runtime blackhole experiment dynamically confirms that a real destination-timeout case is not mapped to5.04.Test Result