net/gcoap: Use nanocoap pkt/options API#9156
Conversation
ecc7cc4 to
2aea999
Compare
|
I think code complete, but I'll give it a little time before removing WIP. I was able to remove most of the gcoap attributes in coap_pkt_t, with the nice reduction in size. I partially converted use of the observe value attribute, but removal will require a little work in nanocoap. I'll leave that for a follow-on PR. |
2aea999 to
1bdc3f8
Compare
1bdc3f8 to
d047a58
Compare
|
Rebased and ready for review now that #9085 has been merged. |
sys/include/net/nanocoap.h
Outdated
| * Not intended for use in a transmitted packet. Must be a 3-byte unsigned | ||
| * value. | ||
| */ | ||
| #define COAP_FORMAT_NO_PAYLOAD (65536) |
There was a problem hiding this comment.
Could you please right align all format values here?
| } | ||
|
|
||
| int res = strcmp((char *)&pdu->url[0], resource->path); | ||
| int res = strcmp((char *)&uri[0], resource->path); |
There was a problem hiding this comment.
Does it make sense to use strncmp here with NANOCOAP_URI_MAX as an extra safety precaution?
There was a problem hiding this comment.
coap_get_uri() inserts the terminator, but I agree it's better to be safe.
There was a problem hiding this comment.
Small note here, I'm not sure whether strncmp is in the C99 libc. (It's not the case with strnlen by default)
There was a problem hiding this comment.
Use of strncmp() adds 40 bytes to the samr21-xpro executable text space. Since coap_get_uri() documentation asserts that it inserts the terminator, would you be OK with beefing up the unit tests for that function as an alternative way to accomplish the goal?
There was a problem hiding this comment.
Agreed, especially since it is a string build in our own functions.
There was a problem hiding this comment.
Perfect. I'm working up a PR in my nanocoap/test_get_uri_path branch.
| int gcoap_add_qstring(coap_pkt_t *pdu, const char *key, const char *val) | ||
| { | ||
| size_t qs_len = strlen((char *)pdu->qs); | ||
| char qs[NANOCOAP_URI_MAX]; |
There was a problem hiding this comment.
Is there a reason for reusing NANOCOAP_URI_MAX and not keeping and using the NANOCOAP_QS_MAX here?
There was a problem hiding this comment.
NANOCOAP_QS_MAX definition goes away in nanocoap.h.
There was a problem hiding this comment.
Why not keep NANOCOAP_QS_MAX and use it here? To me it makes sense to separate these two defines, even if they have the same value.
There was a problem hiding this comment.
I am inclined to leave as-is until we have a reason to differentiate max length of query string. Why add a constant we don't need? The use is internal/invisible to the user, so we can add it whenever necessary.
There was a problem hiding this comment.
To me it feels as if the define is forcefully removed with this PR while there was still a use case for the define. For me defines are a way to make code more readable by giving a meaning to a constant.
Using the NANOCOAP_URI_MAX define here where the use case is not to fix the size of a uri array feels dirty.
So my opinion would be to keep the NANOCOAP_QS_MAX, maybe define the value as NANOCOAP_URI_MAX, but to at least keep it here for clarity reasons.
There was a problem hiding this comment.
I understand your motivation. I removed the macro because nanocoap removed it from generic use in #8772, and left if for gcoap for historical reasons. I just pushed a fixup for gcoap only, as it was before this PR. We can reinstate NANOCOAP_QS_MAX generically for nanocoap when we work on merging #8920.
|
|
||
| /* make sure if url_len + the new query string fit into the url buffer */ | ||
| if ((qs_len + key_len + val_len + 2) >= NANOCOAP_QS_MAX) { | ||
| if ((key_len + val_len + 2) >= NANOCOAP_URI_MAX) { |
There was a problem hiding this comment.
Could you please add a comment what the + 2 here is for. The NULL terminator and something else I'm missing I guess :)
There was a problem hiding this comment.
Actually it's the query punctuation, but yes, let's describe the magic.
| * length in the buffer. Allows us to reconstruct buffer length later. */ | ||
| pdu->payload_len = len - (pdu->payload - buf); | ||
| pdu->content_type = COAP_FORMAT_NONE; | ||
| unsigned header_len = sizeof(coap_hdr_t) + coap_get_token_len(pdu); |
There was a problem hiding this comment.
This looks like a coap_get_total_hdr_len(pdu);
There was a problem hiding this comment.
I agree. It looks like I don't even need the header_len variable itself. I'll take a closer look.
|
|
||
| pdu->options_len = 0; | ||
| pdu->payload = buf + header_len; | ||
| pdu->payload_len = len - (pdu->payload - buf); |
There was a problem hiding this comment.
pdu->payload - buf is equal to header_len right?
There was a problem hiding this comment.
That looks right to me. I'll work to simplify this section.
| coap_pkt_init(pdu, buf, len, hdrlen); | ||
| coap_opt_add_string(pdu, COAP_OPT_URI_PATH, path, '/'); | ||
| /* placeholder until gcoap_finish() */ | ||
| coap_opt_add_uint(pdu, COAP_OPT_CONTENT_FORMAT, COAP_FORMAT_NO_PAYLOAD); |
There was a problem hiding this comment.
Is this to guarantee space for the content format in the buffer?
There was a problem hiding this comment.
Yes, I'll make that clearer.
| { | ||
| /* reconstruct full PDU buffer length */ | ||
| size_t len = pdu->payload_len + (pdu->payload - (uint8_t *)pdu->hdr); | ||
| unsigned format_offset = UINT_MAX; |
There was a problem hiding this comment.
The use of this variable confuses me. It looks to me as if it is only used to remember if there was a content-format option
There was a problem hiding this comment.
Yes, that's right. I will generally add more comments to make the logic in gcoap_finish() clearer. This tortured nature of this code is why I plan to deprecate gcoap_finish() in favor of gcoap_opt_format_finish(), which is called after all options have been entered but before the payload has been written. See API Options page for details.
| if (format == COAP_FORMAT_NONE) { | ||
| if (!move_pos) { | ||
| move_pos = (uint8_t *)pdu->hdr + pdu->options[i].offset; | ||
| } |
There was a problem hiding this comment.
These snippet of 3 lines is in both branches of the if. Is it possible to move it outside of the if statement
There was a problem hiding this comment.
I agree -- looks like an oversight.
| uint8_t *move_pos = 0; /* buffer position to move forward */ | ||
|
|
||
| /* Remove or adjust Content-Format option for request, if any. | ||
| * Assumes current payload format is COAP_FORMAT_NO_PAYLOAD */ |
There was a problem hiding this comment.
I had a hard time understanding the code below. Maybe some additional comments would help the next person. Something like:
/* Remove or adjust Content-Format option for request, if any.
* Assumes current payload format is COAP_FORMAT_NO_PAYLOAD
* First the content format is updated and the option size difference is
* stored in len_delta. Then the Option array is updated. Finally the options as
* placed in the buffer are moved to their updated position (offset) */
Feel free to adjust to your liking
There was a problem hiding this comment.
Yes, as I described in a reply above.
|
Thanks for the detailed review, @bergzand! I plan to implement the adjustments above soon. |
f556b98 to
7eb1ec6
Compare
|
Added fixups, except for a couple of items with replies above. |
|
Addressed latest comments, including a fixup. |
|
I plan to rebase on #9474. Actually I have rebased locally with no conflicts. I'd like to test a little to be sure there are no regressions. |
c03b533 to
0078e6d
Compare
|
Rebased; tests OK. |
| { | ||
| /* reconstruct full PDU buffer length */ | ||
| size_t len = pdu->payload_len + (pdu->payload - (uint8_t *)pdu->hdr); | ||
| if (pdu->options_len && payload_len) { |
There was a problem hiding this comment.
Actually, we can make this friendlier by adding another condition here. We won't check the assertion if the user specifies there is not a Content-Format option (format is COAP_FORMAT_NONE). Let me know if you disagree.
There was a problem hiding this comment.
sounds valid, so +1
Side note: I think this check should be written as a single logical expression inside the assert() (intentional bad formatting...:
assert( !(pdu->options_len) ||
!(payload_len) ||
(pdu->options[pdu->options_len-1].opt_num < COAP_OPT_CONTENT_FORMAT));hope I did the logic correctly here...
There was a problem hiding this comment.
OK, I'll rework the assert. Your construct is more idiomatic although as you say the logic is a little harder to follow.
|
Thanks, @haukepetersen. Let me know your reactions on |
|
done. |
71c0269 to
cd15b97
Compare
|
Rebased on master for #10223. |
|
All comments addressed. |
Observe still uses coap_pkt_t attribute.
haukepetersen
left a comment
There was a problem hiding this comment.
Nothing to add -> ACK
|
feel free to squash :-) |
0e275f9 to
8fa6f10
Compare
|
Done, and all code bits accounted for! |
done that :-) |
quote "@haukepetersen Feel free to dismiss my review if it is blocking"
|
now let's see what Murdock is saying, but seems we are pretty much ready to go (fingers crossed). |
|
all green -> let's go! |
|
That was a big one. Thanks @haukepetersen and @bergzand! |
Contribution description
Updates gcoap to use the nanocoap pkt/options API in #9085. This work allows removal of the gcoap-specific attributes in coap_pkt_t, which add a painful 130 bytes. (Now you know why I've been working so hard lately!)
Use of this API allows gcoap to fully build on nanocoap as a higher-level API. gcoap has always used a coap_pkt_t to build a message, and now nanocoap does too. See how gcoap_req_init() prepares for coap_build_hdr(), and then calls coap_pkt_init(), coap_opt_add_string() for the path, and coap_opt_add_uint() for the content format.
2018-10-23 Update
Simplified implementation of gcoap_finish(), which results in a minor API change. gcoap_finish() adds a Content-Format option to the packet after writing the payload. The original implementation of this PR accommodated options with any other option number that may have been added to the packet. Today I updated the implementation to accept options only with an option number less than Content-Format.
Fundamentally, the gcoap_finish() function has become obsolete as we have increased the use of CoAP options. The updated CoAP API now uses coap_opt_finish() to finalize the packet header before writing the payload. We plan to deprecate gcoap_finish() soon.
This change significantly simplifies gcoap_finish() until we deprecate it, and reduces the text space in the executable for this function by half, around 120 bytes. The cost is that existing code breaks when setting a Uri-Query option and then setting the format in gcoap_finish(). (Uri-Query is the only option with an option number greater than Content-Format that gcoap used before this PR.) In fact, the new Resource Directory implementation, cord_ep, had this problem, so I added a commit to this PR to fix it.
Issues/PRs references
Depends on #10223
See CoAP API Options Update wiki page for background, including how gcoap uses it.