pub fn poll_with_response_handler<ST>(
    stack: &mut ST,
    socket: &mut ST::UdpSocket,
    handler: &mut impl Handler,
    response_handler: impl for<'a> FnOnce(u16, &'a [u8], &'a Message<'a>) -> bool
) -> Result<(), ST::Error>
where ST: UdpFullStack + ?Sized,
Expand description

Like poll, but allowing a callback for response messages.

The response_handler will be called on every received CoAP response (including empty ACKs), with message ID, token and the message. It should return true if the response was expected, and false if not; this influences whether a CON will be responded to with an ACK or an RST.

Users should not rely on the message argument to the handler to be precisely the indicated type; it may change to any implementation of coap_message::ReadableMessage.

Use in multiple locations

This function can be suitable to be used in multiple spots, especially when an application is built in a single-threaded way and wants to use the same stack to process requests and responses.

From a stack usage perspective, this should be fine as far as this function is concerned: The CoAP processing, especially any heavy cryptographic processing, will only ever happen on one message buffer that is kept. If a CoAP request is sent from a place with large stack usage that can tolerate encryption of a client-side request but not the server-side handling, a suitable handler could just return 5.03 with a Max-Age indicating the time for which the client would wait for responses (i.e., until it can be expected that the server side handler is available again). Note that this is not compatible with the behavior prescribed for a Resource Directory at https://datatracker.ietf.org/doc/html/rfc9176#section-5.1-6 – to get that behavior, at least the .well-known/core handler needs to be in place. This implementation can not, at the arrival of a request, decide to err out from the poll step, notify the application of the request’s cancellation, and then take up the most recent incoming request again (because it would need to store it somewhere or merely peek at the stack). A possible workaround (that is not following the letter but the spirit of the RFC) is for the stub handler to respons 5.03 with a Max-Age of 0, and make the application cancel the request to return to the idle loop; that would be suitable primarily for the simple registration procedure itself. This is a big FIXME, but may best be addressed by a more asynchronous processing model.

<brooding>

If we were worried about how different response_handler caused this code to be monomorphized multiple times (same handler but different response handlers, as would be the case if this were called both from an idle-loop context and from different responses being expected), we could factor out a workhorse function that takes a &mut dyn for<'a> FnMut(...). The monomorphization wrapper would then just put the actual Fn into an Option<>, store it on stack, and build a FnMut with a reference to the option that panics if it’s empty (or no-ops so we don’t need all_responses_are_unexpected) – that weirdness will be required because we can only use dyn through references, and there’s no &owned T (it’s called Box<>, but that only works with a heap, and I don’t know of a type that behaves the same just on the stack and dropping the reference drops the data in-place with out-of-stack-frame cleanups disabled). A plain no_alloc Box will probably not work yet.

</brooding>