Incorrect GET response after POST

Hello, I’m in the process of reviving the golioth-rs repo and rebasing it on Embassy and nrf-modem for async support in Rust :slight_smile: I’ve run into a small problem and I’m not sure where to go from here. I’m using CoAP and DTLS.

I’m using an atomic u16 type that is incremented and used for the message header id anytime I make a CoAP request, just like the original repo. If I make a GET request I get a good response:

60 45 00 00 48 C2 2E 52 8D 07 04 8E 9D 80 FF 7B 22 73 74 61 74 61 74 61 74 65 22 3A 74 72 75 65 7D

which decodes into

UDP

Message:
{
  "type": "Acknowledgement",
  "code": "Content",
  "id": 0,
  "token": 0,
  "options": [
    "ETag(etag=C2 2E 52 8D 07 04 8E 9D",
    "Content-Format: text/plain; charset=utf-8"
  ]
}

Payload:
{"state":true}

If I attempt to GET after I perform a POST, I get this instead

60 44 00 00 48 00 00 00 00 00 00 CA 0C 80 FF 4F 4B

which decodes to

Message:
{
  "type": "Acknowledgement",
  "code": "Changed",
  "id": 0,
  "token": 0,
  "options": [
    "ETag(etag=00 00 00 00 00 00 CA 0C",
    "Content-Format: text/plain; charset=utf-8"
  ]
}

Payload:
OK

It seems that I am picking up a response from the previous POST? I’m not a networking guru, so I’m a little unsure of what is going on, should I use PUT instead of POST or should I wait a certain amount of time before doing a GET after PUT/POST?

Ha, I figured it out. I was right, the response is from the previous POST and not the GET (the id is the tale tell sign). I solved this, by making POSTs Nonconfirmable types so they don’t interfere with the responses of GETs.

Successful GET after LightDB Stream and State POSTs:

UDP

Message:
{
  "type": "Acknowledgement",
  "code": "Content",
  "id": 2,
  "token": 0,
  "options": [
    "ETag(etag=C2 2E 52 8D 07 04 8E 9D",
    "Content-Format: text/plain; charset=utf-8"
  ]
}

Payload:
{"state":true}
2 Likes

I’m back working on the golioth-rs repo :slight_smile: , and I wanted to update this post. After learning more about CoAP, it seems that making my POSTs Nonconfirmable was just a hack, not the solution to my previous problem.

The real answer is to make sure to include a request token to match on once a response is received. I’ve added a non-cryptographically secure PRNG function to my code that creates random tokens to include in my packets.

@dkhayes117 great to hear from you and thanks for following up! Sounds like you found the solution here :slight_smile: Message ID and Token can sometimes be conflated when using CoAP, which can cause confusion. As you have identified, the Message ID is useful for correlating Acknowledgement to a message, whereas the Token is useful for correlating the response to a request. In the event that the response is piggybacked onto an Acknowledgement then then it would include both a matching Message ID and a matching Token.

Thanks for all the awesome work you are doing on golioth-rs!

1 Like

Just in the case, someone read this with similar problems:

These two artifacts are very important in processing CoAP (RFC7252) in a well defined way.

There are two layers, the messaging layer controlled by the MID. And the request/response layer controlled by the token.

The request/response layer is simpler, the client is required to use a token in order to identify the response with it. A empty token is also a token. If every request carries the same token (e.g. the empty token), it will get hard to sort out the right response in logs and for the client, especially, if retransmissions occurs. Using observe/notify (RFC7641) it even requires to use unique tokens for longer time periods.

The MID is used to control CONfirmable transmissions. The sender of the CON message must ensure, that the MID is unique for a certain period of time. The common one, derived from the standard parameters in RFC7252 is the EXCHANGE_LIFETIME of 247s. That also limits a single endpoint in sending more than 65536 messages to the same peer within that time. If the message sender violates that, the behavior of the server is undefined. e.g. if a client sends a CON request with MID a, the server will keep the ACK response for that in order to resend it, if the client resend it’s request. That mechanism is defined to be based on the MID. If no the client sends a different message, e.g. a next CON request, the server may choose to retransmit the kept ACK response to the first request. That’s “in spec”, and the client is “out spec”.

So:
Always use a new MID for a new request towards the same server, usually incrementing the MID and wrap on an upper layer works pretty well.
I recommend to also use a different token, just in order to have it easier to debug the traffic.

2 Likes