libcoap icon indicating copy to clipboard operation
libcoap copied to clipboard

can coap uri resource contains multi-level paths?

Open kingjie0210 opened this issue 3 years ago • 3 comments

when I set the coap uri path not include '/' the example can run ok. eg: coap_str_const_t *ruri = coap_make_str_const("v1"); resource = coap_resource_init(ruri, 0);

but when I set the coap uri path include '/' the example can not run ok. eg: coap_str_const_t *ruri = coap_make_str_const("v1/d"); resource = coap_resource_init(ruri, 0);

who can tell me why cause this and how to write?Thank you!

the whole demo belows: server.c #include <stdio.h> #include "common.h"

static void post_handler(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { size_t size; const uint8_t *data; size_t offset; size_t total; coap_binary_t body_data = NULL; coap_show_pdu(LOG_WARNING, request); if (coap_get_data_large(request, &size, &data, &offset, &total)) { / COAP_BLOCK_SINGLE_BODY is set, so single body should be given */ assert(size == total); body_data = coap_new_binary(total); if (!body_data) { coap_log(LOG_DEBUG, "body build memory error\n"); return COAP_RESPONSE_OK; } memcpy(body_data->s, data, size); data = body_data->s; } printf("receiced data is %s\n",data); coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); coap_add_data(response, 5,(const uint8_t *)"world"); coap_show_pdu(LOG_WARNING, response); coap_delete_binary(body_data); }

int main(void) { coap_context_t *ctx = NULL; coap_address_t dst; coap_resource_t *resource = NULL; coap_endpoint_t *endpoint = NULL; int result = EXIT_FAILURE;;

coap_str_const_t *ruri = coap_make_str_const("v1/d");
coap_startup();

if (resolve_address("localhost", "5683", &dst) < 0) {
	coap_log(LOG_CRIT, "failed to resolve address\n");
	goto finish;
}
ctx = coap_new_context(NULL);

if (!ctx || !(endpoint = coap_new_endpoint(ctx, &dst, COAP_PROTO_UDP))) {
	coap_log(LOG_EMERG, "cannot initialize context\n");
	goto finish;
}
printf("ruri->s is %s,ruri->length is %d\n",ruri->s,ruri->length);

resource = coap_resource_init(ruri, 0);

coap_register_handler(resource, COAP_REQUEST_POST, post_handler);
coap_add_resource(ctx, resource);

while (1) { 
	coap_io_process(ctx, COAP_IO_WAIT); 
}

result = EXIT_SUCCESS;

finish:

coap_free_context(ctx);
coap_cleanup();

return result;

}

client.c

#include <stdio.h> #include "common.h"

static coap_response_t response_handler(coap_session_t *session COAP_UNUSED, const coap_pdu_t *sent, const coap_pdu_t *received, const coap_mid_t id COAP_UNUSED) { size_t size; const uint8_t *data; coap_binary_t *body_data = NULL; coap_show_pdu(LOG_WARNING, received); if (coap_get_data(received, &size, &data)) { body_data = coap_new_binary(size); if (!body_data) { coap_log(LOG_DEBUG, "body build memory error\n"); return COAP_RESPONSE_OK; } memcpy(body_data->s, data, size); data = body_data->s; } printf("receiced data is %s\n",data); coap_delete_binary(body_data); return COAP_RESPONSE_OK; }

int main(void) { coap_context_t *ctx = NULL; coap_session_t session = NULL; coap_address_t dst; coap_pdu_t pdu = NULL; const char server_uri = "coap://localhost/v1/d"; const char post_data = "Hello World!"; static coap_uri_t uri; int result = EXIT_FAILURE;;

coap_startup();

if (resolve_address("localhost", "5683", &dst) < 0) {
	coap_log(LOG_CRIT, "failed to resolve address\n");
	goto finish;
}

/* Prepare the request */
coap_split_uri(server_uri, strlen(server_uri), &uri);

ctx = coap_new_context(NULL);

if (!ctx || !(session = coap_new_client_session(ctx, NULL, &dst,
	                                          COAP_PROTO_UDP))) {
	coap_log(LOG_EMERG, "cannot create client session\n");
	goto finish;
}

coap_register_response_handler(ctx, response_handler);
pdu = coap_pdu_init(COAP_MESSAGE_CON,
	              COAP_REQUEST_CODE_POST,
	              coap_new_message_id(session),
	              coap_session_max_pdu_size(session));
if (!pdu) {
	coap_log( LOG_EMERG, "cannot create PDU\n" );
	goto finish;
}

/* add a Uri-Path option */
printf("uri.path.length is %d, uri.path.s is %s\n",uri.path.length, uri.path.s);
coap_add_option(pdu, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s);

coap_add_data(pdu, strlen(post_data), (unsigned char *)post_data);
coap_show_pdu(LOG_WARNING, pdu);
/* and send the PDU */
coap_send(session, pdu);

coap_io_process(ctx, COAP_IO_WAIT);

result = EXIT_SUCCESS;

finish:

coap_session_release(session);
coap_free_context(ctx);
coap_cleanup();
return result;

}

kingjie0210 avatar Sep 01 '22 03:09 kingjie0210

Yes, libcoap fully supports multilevel paths.

When writing both server and client code at the same time, interactions between them can cause challenges in determining where the actual issue lies, so my recommendations for testing is to use the provided coap-client example to test against your server and your client to test against the coap-server example (with a couple of updates if needed for your specific scenarios) to work out whether it is your client or server is at fault.

In your case, it is an issue with your client - how it is using the Uri-Path option. As per RFC7252 6.4. Decomposing URIs into Options

   8.  If the value of the <path> component of |url| is empty or
       consists of a single slash character (U+002F SOLIDUS "/"), then
       move to the next step.

       Otherwise, for each segment in the <path> component, include a
       Uri-Path Option and let that option's value be the segment (not
       including the delimiting slash characters) after converting each
       percent-encoding ("%" followed by two hexadecimal digits) to the
       corresponding byte.

In other words /v1/d is sent over the wire with two Uri-Path options - v1 and then d, not a single one with v1/d.

Libcoap provides coap_split_path(3) to split to path into different segments which are used to to provide the data for the different Uri-Path options.

mrdeep1 avatar Sep 01 '22 08:09 mrdeep1

Yes, libcoap fully supports multilevel paths.

When writing both server and client code at the same time, interactions between them can cause challenges in determining where the actual issue lies, so my recommendations for testing is to use the provided coap-client example to test against your server and your client to test against the coap-server example (with a couple of updates if needed for your specific scenarios) to work out whether it is your client or server is at fault.

In your case, it is an issue with your client - how it is using the Uri-Path option. As per RFC7252 6.4. Decomposing URIs into Options

   8.  If the value of the <path> component of |url| is empty or
       consists of a single slash character (U+002F SOLIDUS "/"), then
       move to the next step.

       Otherwise, for each segment in the <path> component, include a
       Uri-Path Option and let that option's value be the segment (not
       including the delimiting slash characters) after converting each
       percent-encoding ("%" followed by two hexadecimal digits) to the
       corresponding byte.

In other words /v1/d is sent over the wire with two Uri-Path options - v1 and then d, not a single one with v1/d.

Libcoap provides coap_split_path(3) to split to path into different segments which are used to to provide the data for the different Uri-Path options.

thank you for your reply.Now I realize there is something wrong with my client program. Can you provice any demo for me? I have no idea how to use Uri-Path option. Thank you again!

kingjie0210 avatar Sep 01 '22 09:09 kingjie0210

See examples/coap-client.c around line 780 where the coap_split_path() function is used.

mrdeep1 avatar Sep 01 '22 09:09 mrdeep1

@kingjie0210 Please see #935 which adds in a new function coap_uri_into_options() which simplifies things / reduces code for an application.

mrdeep1 avatar Oct 06 '22 10:10 mrdeep1

@kingjie0210 Can this be closed now?

mrdeep1 avatar Oct 14 '22 14:10 mrdeep1

Please free to re-open this issue if there still is an issue.

mrdeep1 avatar Nov 04 '22 10:11 mrdeep1