diff --git a/examples/coap-server.c b/examples/coap-server.c index 51fa05f4..0474ea69 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -249,6 +249,7 @@ release_resource_data(coap_session_t *session COAP_UNUSED, if (!transient_value) return; + assert(transient_value->ref_cnt); if (--transient_value->ref_cnt > 0) return; coap_delete_binary(transient_value->value); @@ -722,16 +723,11 @@ hnd_reverse_proxy_uri(coap_resource_t *resource, #endif /* COAP_PROXY_SUPPORT */ typedef struct dynamic_resource_t { - coap_string_t *uri_path; transient_value_t *value; - coap_resource_t *resource; int created; uint16_t media_type; } dynamic_resource_t; -static int dynamic_count = 0; -static dynamic_resource_t *dynamic_entry = NULL; - /* * Regular DELETE handler - used by resources created by the * Unknown Resource PUT handler @@ -740,37 +736,10 @@ static dynamic_resource_t *dynamic_entry = NULL; static void hnd_delete(coap_resource_t *resource, coap_session_t *session COAP_UNUSED, - const coap_pdu_t *request, + const coap_pdu_t *request COAP_UNUSED, const coap_string_t *query COAP_UNUSED, coap_pdu_t *response) { - int i; - coap_string_t *uri_path; - - /* get the uri_path */ - uri_path = coap_get_uri_path(request); - if (!uri_path) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); - return; - } - - for (i = 0; i < dynamic_count; i++) { - if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) { - /* Dynamic entry no longer required - delete it */ - release_resource_data(session, dynamic_entry[i].value); - coap_delete_string(dynamic_entry[i].uri_path); - if (dynamic_count-i > 1) { - memmove(&dynamic_entry[i], - &dynamic_entry[i+1], - (dynamic_count-i-1) * sizeof(dynamic_entry[0])); - } - dynamic_count--; - break; - } - } - - /* Dynamic resource no longer required - delete it */ coap_delete_resource(NULL, resource); - coap_delete_string(uri_path); coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED); } @@ -785,34 +754,15 @@ hnd_get(coap_resource_t *resource, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { - coap_str_const_t *uri_path; - int i; dynamic_resource_t *resource_entry = NULL; coap_binary_t body; - /* - * request will be NULL if an Observe triggered request, so the uri_path, - * if needed, must be abstracted from the resource. - * The uri_path string is a const pointer - */ - uri_path = coap_resource_get_uri_path(resource); - if (!uri_path) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); + resource_entry = coap_resource_get_userdata(resource); + if (!resource_entry) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); return; } - for (i = 0; i < dynamic_count; i++) { - if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) { - break; - } - } - if (i == dynamic_count) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); - return; - } - - resource_entry = &dynamic_entry[i]; - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); body = reference_resource_data(resource_entry->value); coap_add_data_large_response(resource, session, request, response, @@ -833,81 +783,36 @@ hnd_put_post(coap_resource_t *resource, const coap_pdu_t *request, const coap_string_t *query COAP_UNUSED, coap_pdu_t *response) { - coap_string_t *uri_path; - int i; size_t size; const uint8_t *data; size_t offset; size_t total; dynamic_resource_t *resource_entry = NULL; - unsigned char buf[6]; /* space to hold encoded/decoded uints */ coap_opt_iterator_t opt_iter; coap_opt_t *option; coap_binary_t *data_so_far; transient_value_t *transient_value; - /* get the uri_path */ - uri_path = coap_get_uri_path(request); - if (!uri_path) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); - return; - } - - /* - * Locate the correct dynamic block for this request - */ - for (i = 0; i < dynamic_count; i++) { - if (coap_string_equal(uri_path, dynamic_entry[i].uri_path)) { - break; - } - } - if (i == dynamic_count) { - if (dynamic_count >= support_dynamic) { - /* Should have been caught hnd_put_post_unknown() */ - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_ACCEPTABLE); - coap_delete_string(uri_path); - return; - } - dynamic_count++; - dynamic_entry = realloc(dynamic_entry, - dynamic_count * sizeof(dynamic_entry[0])); - if (dynamic_entry) { - dynamic_entry[i].uri_path = uri_path; - dynamic_entry[i].value = NULL; - dynamic_entry[i].resource = resource; - dynamic_entry[i].created = 1; - if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT, - &opt_iter)) != NULL) { - dynamic_entry[i].media_type = - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option)); - } else { - dynamic_entry[i].media_type = COAP_MEDIATYPE_TEXT_PLAIN; - } - /* Store media type of new resource in ct. We can use buf here - * as coap_add_attr() will copy the passed string. */ - memset(buf, 0, sizeof(buf)); - snprintf((char *)buf, sizeof(buf), "%d", dynamic_entry[i].media_type); - /* ensure that buf is always zero-terminated */ - assert(buf[sizeof(buf) - 1] == '\0'); - buf[sizeof(buf) - 1] = '\0'; - coap_add_attr(resource, - coap_make_str_const("ct"), - coap_make_str_const((char *)buf), - 0); - } else { - dynamic_count--; + resource_entry = coap_resource_get_userdata(resource); + if (!resource_entry) { + resource_entry = malloc(sizeof(dynamic_resource_t)); + if (!resource_entry) { coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - coap_delete_string(uri_path); return; } - } else { - /* Need to do this as coap_get_uri_path() created it */ - coap_delete_string(uri_path); + memset(resource_entry, 0, sizeof(dynamic_resource_t)); + resource_entry->created = 1; + if ((option = coap_check_option(request, COAP_OPTION_CONTENT_FORMAT, + &opt_iter)) != NULL) { + resource_entry->media_type = + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + } else { + resource_entry->media_type = COAP_MEDIATYPE_TEXT_PLAIN; + } + coap_resource_set_userdata(resource, resource_entry); } - resource_entry = &dynamic_entry[i]; - if (coap_get_data_large(request, &size, &data, &offset, &total) && size != total) { coap_binary_t *old_data_in_cache; @@ -1023,7 +928,7 @@ hnd_put_post(coap_resource_t *resource, } } else { coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); - coap_resource_notify_observers(resource_entry->resource, NULL); + coap_resource_notify_observers(resource, NULL); } if (echo_back) { @@ -1044,29 +949,19 @@ fail: } /* - * Unknown Resource PUT handler + * Dynamic Resource PUT / POST handler + * Create the appropriate resource and pass it back. */ -static void -hnd_put_post_unknown(coap_resource_t *resource COAP_UNUSED, - coap_session_t *session, - const coap_pdu_t *request, - const coap_string_t *query, - coap_pdu_t *response) { +static coap_resource_t * +dyn_create_handler(coap_session_t *session, const coap_pdu_t *request) { coap_resource_t *r; coap_string_t *uri_path; - /* check if creating a new resource is allowed */ - if (dynamic_count >= support_dynamic) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_ACCEPTABLE); - return; - } - /* get the uri_path - will get used by coap_resource_init() */ uri_path = coap_get_uri_path(request); if (!uri_path) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); - return; + return NULL; } /* @@ -1075,6 +970,9 @@ hnd_put_post_unknown(coap_resource_t *resource COAP_UNUSED, */ r = coap_resource_init((coap_str_const_t *)uri_path, COAP_RESOURCE_FLAGS_RELEASE_URI | resource_flags); + if (!r) { + return NULL; + } coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Dynamic\""), 0); coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_post); coap_register_request_handler(r, COAP_REQUEST_POST, hnd_put_post); @@ -1084,9 +982,7 @@ hnd_put_post_unknown(coap_resource_t *resource COAP_UNUSED, coap_resource_set_get_observable(r, 1); coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get); coap_add_resource(coap_session_get_context(session), r); - - /* Do the PUT/POST for this first call */ - hnd_put_post(r, session, request, query, response); + return r; } #if COAP_PROXY_SUPPORT @@ -1160,6 +1056,15 @@ proxy_nack_handler(coap_session_t *session COAP_UNUSED, #endif /* COAP_PROXY_SUPPORT */ +static void +resource_data_delete(void *ptr) { + dynamic_resource_t *resource_entry = (dynamic_resource_t *)ptr; + if (resource_entry->value) { + release_resource_data(NULL, resource_entry->value); + } + free(resource_entry); +} + static void init_resources(coap_context_t *ctx) { coap_resource_t *r; @@ -1200,11 +1105,8 @@ init_resources(coap_context_t *ctx) { time_resource = r; if (support_dynamic > 0) { - /* Create a resource to handle PUTs to unknown URIs */ - r = coap_resource_unknown_init2(hnd_put_post_unknown, 0); - /* Add in handling POST as well */ - coap_register_handler(r, COAP_REQUEST_POST, hnd_put_post_unknown); - coap_add_resource(ctx, r); + coap_register_dynamic_resource_handler(ctx, dyn_create_handler, + support_dynamic); } if (coap_async_is_supported()) { @@ -1239,6 +1141,7 @@ init_resources(coap_context_t *ctx) { coap_register_nack_handler(ctx, proxy_nack_handler); } #endif /* COAP_PROXY_SUPPORT */ + coap_resource_release_userdata_handler(ctx, resource_data_delete); } static int @@ -2916,11 +2819,6 @@ finish: if (valid_pki_snis.count) free(valid_pki_snis.pki_sni_list); - for (i = 0; i < (size_t)dynamic_count; i++) { - coap_delete_string(dynamic_entry[i].uri_path); - release_resource_data(NULL, dynamic_entry[i].value); - } - free(dynamic_entry); release_resource_data(NULL, example_data_value); #if COAP_PROXY_SUPPORT free(reverse_proxy.entry); diff --git a/include/coap3/coap_net.h b/include/coap3/coap_net.h index bb953773..67ba5552 100644 --- a/include/coap3/coap_net.h +++ b/include/coap3/coap_net.h @@ -106,6 +106,16 @@ typedef void (*coap_pong_handler_t)(coap_session_t *session, const coap_pdu_t *received, const coap_mid_t mid); +/** + * Definition of resource dynamic creation handler function + * + * @param session The CoAP session. + * @param request The request PDU. + * + * @return A pointer to the new resource or @c NULL on error. + */ +typedef coap_resource_t *(*coap_resource_dynamic_create_t)(coap_session_t *session, + const coap_pdu_t *request); /** * Registers a new message handler that is called whenever a response is * received. @@ -148,6 +158,24 @@ void coap_register_ping_handler(coap_context_t *context, void coap_register_pong_handler(coap_context_t *context, coap_pong_handler_t handler); +/** + * Sets up a handler for calling when an unknown resource is requested. + * The @p create_handler is expected to create the required resource to handle + * the request which is then used to call the appropriate method handler. + * + * Doing this stops multiple threads asking for the same unknown resource and + * means that a resource unknown handler does not need to be thread safe. + * + * @param context The context to add this support to. + * @param create_handler Called to create a new resource. + * @param dynamic_max Maximum number of currently created dynamic resources. If + * 0, unlimited. + * + */ +void coap_register_dynamic_resource_handler(coap_context_t *context, + coap_resource_dynamic_create_t create_handler, + uint32_t dynamic_max); + /** * Registers the option number @p number with the given context object @p context. * diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index 988a7da3..e94cda7d 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -215,6 +215,9 @@ struct coap_context_t { uint8_t testing_cids; /**< Change client's source port every testing_cids */ #endif /* COAP_CLIENT_SUPPORT */ uint32_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */ + coap_resource_dynamic_create_t dyn_create_handler; /**< Dynamc resource create handler */ + uint32_t dynamic_cur; /* Current number of dynamic resources */ + uint32_t dynamic_max; /* Max number of dynamic resources or 0 is unlimited */ }; /** diff --git a/include/coap3/coap_resource_internal.h b/include/coap3/coap_resource_internal.h index 0873018a..f88310f5 100644 --- a/include/coap3/coap_resource_internal.h +++ b/include/coap3/coap_resource_internal.h @@ -60,7 +60,9 @@ struct coap_resource_t { unsigned int is_proxy_uri:1; /**< resource created for proxy URI handler */ unsigned int is_reverse_proxy:1; /**< resource created for reverse proxy URI handler */ unsigned int list_being_traversed:1; /**< resource subscriber list being traversed */ + unsigned int is_dynamic:1; /**< create unknown resource dynamically */ + uint32_t ref; /**< Resource reference count */ /** * Used to store handlers for the seven coap methods @c GET, @c POST, @c PUT, * @c DELETE, @c FETCH, @c PATCH and @c IPATCH. @@ -147,6 +149,26 @@ int coap_delete_resource_lkd(coap_context_t *context, coap_resource_t *resource) */ void coap_delete_all_resources(coap_context_t *context); +/** + * Increment reference counter on a resource. + * + * Note: This function must be called in the locked state. + * + * @param resource The CoAP resource. + */ +void coap_resource_reference_lkd(coap_resource_t *resource); + +/** + * Decrement reference counter on a resource. + * Note that the resource storage may be deleted as a result and should not be + * used after this call. + * + * Note: This function must be called in the locked state. + * + * @param resource The CoAP resource. + */ +void coap_resource_release_lkd(coap_resource_t *resource); + #define RESOURCES_ADD(r, obj) \ HASH_ADD(hh, (r), uri_path->s[0], (obj)->uri_path->length, (obj)) diff --git a/libcoap-3.map b/libcoap-3.map index 5a7347d9..d2247eca 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -224,6 +224,7 @@ global: coap_query_into_optlist; coap_realloc_type; coap_register_async; + coap_register_dynamic_resource_handler; coap_register_event_handler; coap_register_handler; coap_register_nack_handler; diff --git a/libcoap-3.sym b/libcoap-3.sym index 76e12868..25efcc83 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -222,6 +222,7 @@ coap_q_block_is_supported coap_query_into_optlist coap_realloc_type coap_register_async +coap_register_dynamic_resource_handler coap_register_event_handler coap_register_handler coap_register_nack_handler diff --git a/man/coap_handler.txt.in b/man/coap_handler.txt.in index bc12cd29..22bdad1e 100644 --- a/man/coap_handler.txt.in +++ b/man/coap_handler.txt.in @@ -16,7 +16,8 @@ coap_register_response_handler, coap_register_nack_handler, coap_register_ping_handler, coap_register_pong_handler, -coap_register_event_handler +coap_register_event_handler, +coap_register_dynamic_resource_handler - Work with CoAP handlers SYNOPSIS @@ -41,6 +42,10 @@ coap_pong_handler_t _handler_)*; *void coap_register_event_handler(coap_context_t *_context_, coap_event_handler_t _handler_)*; +*void coap_register_dynamic_resource_handler(coap_context_t *_context_, +coap_resource_dynamic_create_t _create_handler_, +uint32_t _dynamic_max_)*; + For specific (D)TLS library support, link with *-lcoap-@LIBCOAP_API_VERSION@-notls*, *-lcoap-@LIBCOAP_API_VERSION@-gnutls*, *-lcoap-@LIBCOAP_API_VERSION@-openssl*, *-lcoap-@LIBCOAP_API_VERSION@-mbedtls*, @@ -122,7 +127,8 @@ _incoming_pdu_'s data must not be used if calling taken. *NOTE:* A request callback handler can be called with a generic resource (i.e. -set up using *coap_resource_unknown_init2*(3)), so +set up using *coap_resource_unknown_init2*(3)), or by using +*coap_register_dynamic_resource_handler*(3), so *coap_resource_get_uri_path*(3) can be used to determine the URI in this case. *Function: coap_register_response_handler()* @@ -396,6 +402,47 @@ typedef enum { closing down, not all the information may be there in the _session_ parameter passed to the application event handler. +*Function: coap_register_dynamic_resource_handler()* + +The *coap_register_dynamic_resource_handler*() is a server side function that +registers _create_handler_ that is invoked whenever a request comes in for a resource +that is not defined and the request type is PUT or POST. + +*NOTE:* This handler takes precedence over any handlers set up by +*coap_resource_unknown_init2*(3) or *coap_resource_proxy_uri_init2*(3). + +It is the responsibility of this handler to create and register all the +requirements the new resource, returning the address of the newly created +resource. Following the creation of the resource, libcoap will then invoke +the appropriate method request handler (if defined). + +The number of concurrent dynamically allocated resources is limited to +_dynamic_max_, or unlimited if _dynamic_max_ is zero. If a new request comes in +and all the available dynamic resources are used up, then a 4.06 response +is returned. + +The dynamic resource handler function prototype is defined as: +[source, c] +---- +/** + * Definition of resource dynamic creation handler function + * + * @param session The CoAP session. + * @param request The request PDU. + * + * @return A pointer to the new resource or @c NULL on error. + */ +typedef coap_resource_t *(*coap_resource_dynamic_create_t)(coap_session_t *session, + const coap_pdu_t *request); +---- + +In _create_handler_, data from _request_ can be abstracted as described in +*coap_pdu_access*(3) for analysis and then resource creation. + +*NOTE:* If the returned value is NULL, then a 4.06 packet will get sent back to the +client by libcoap. +>>>>>>> 10fc1b89 (coap_resource.c: Make dynamic resource handling thread safe) + EXAMPLES -------- *GET Resource Callback Handler* diff --git a/man/coap_resource.txt.in b/man/coap_resource.txt.in index 51e2cc0f..616646ad 100644 --- a/man/coap_resource.txt.in +++ b/man/coap_resource.txt.in @@ -231,6 +231,11 @@ more of the COAP_RESOURCE_FLAGS MCAST definitions. *NOTE:* There can only be one reverse-proxy or unknown resource handler per context - attaching a new one overrides the previous definition. +*NOTE:* If resources are created in the request handlers accociated with this +resource, as multiple threads can be running in the same handler either lock +protection needs to be provided in the handler, or consider using +*coap_register_dynamic_resource_handler*(3) instead for multi-thread environments. + *Function: coap_resource_proxy_uri_init()* The *coap_resource_proxy_uri_init*() function returns a newly created @@ -491,7 +496,150 @@ init_resources(coap_context_t *ctx) { } ---- -*Dynamic Resources Set Up* +*Dynamic Resources Set Up using coap_register_dynamic_resource_handler()* + +[source, c] +---- +#include + +/* Regular DELETE handler - used by resources created by the + * Dynamic Resource PUT/POST handler */ + +static void +hnd_delete(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + /* Remove (void) definition if variable is used */ + (void)session; + (void)request; + (void)query; + (void)response; + + /* .. code .. */ + + /* Dynamic resource no longer required - delete it */ + coap_delete_resource(NULL, resource); + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED); +} + +/* Regular GET handler - used by resources created by the + * Dynamic Resource PUT/POST handler */ + +static void +hnd_get(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + coap_str_const_t *get_uri_path; + + /* Remove (void) definition if variable is used */ + (void)resource; + (void)session; + (void)request; + (void)query; + + /* + * Get the specific resource being requested to determine what the response is + * The uri_path string is a const pointer + */ + + get_uri_path = coap_resource_get_uri_path(resource); + + /* .. code .. */ + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); +} + +/* Regular PUT handler - used by resources created by the + * Dynamic Resource PUT/POST handler */ + +static void +hnd_put_post(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + /* Remove (void) definition if variable is used */ + (void)resource; + (void)session; + (void)query; + + coap_string_t *put_uri_path; + size_t length; + const uint8_t *data; + size_t offset; + size_t total; + int new_resource = 0; + + /* get the uri_path */ + put_uri_path = coap_get_uri_path(request); + if (!put_uri_path) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND); + return; + } + coap_get_data_large(request, &length, &data, &offset, &total); + + /* .. code .. */ + + /* Need to do this as coap_get_uri_path() created it */ + coap_delete_string(put_uri_path); + + if (length + offset < total) + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTINUE); + else if (new_resource) + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED); + else + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); +} + +/* + * Dynamic Resource PUT / POST handler + * Create the appropriate resource and pass it back. + */ + +static coap_resource_t * +dyn_create_handler(coap_session_t *session, const coap_pdu_t *request) { + coap_resource_t *r; + coap_string_t *uri_path; + + /* get the uri_path - will get used by coap_resource_init() */ + uri_path = coap_get_uri_path(request); + if (!uri_path) { + return NULL; + } + + /* + * Create a resource to handle the new URI + * uri_path will get deleted when the resource is removed. + * It may be that he resource information is held in the POST data. + */ + r = coap_resource_init((coap_str_const_t *)uri_path, + COAP_RESOURCE_FLAGS_RELEASE_URI | COAP_RESOURCE_FLAGS_NOTIFY_NON); + if (!r) { + return NULL; + } + coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Dynamic\""), 0); + coap_register_request_handler(r, COAP_REQUEST_PUT, hnd_put_post); + coap_register_request_handler(r, COAP_REQUEST_POST, hnd_put_post); + coap_register_request_handler(r, COAP_REQUEST_DELETE, hnd_delete); + coap_register_request_handler(r, COAP_REQUEST_FETCH, hnd_get); + /* We possibly want to Observe the GETs */ + coap_resource_set_get_observable(r, 1); + coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get); + coap_add_resource(coap_session_get_context(session), r); + return r; +} + +/* Initialize single Dynamic Resource PUT / POST handler */ + +static void +init_resources(coap_context_t *ctx) { + + coap_register_dynamic_resource_handler(ctx, dyn_create_handler, 20); + +} +---- + + +*Dynamic Resources Set Up using coap_resource_unknown_init2()* [source, c] ---- diff --git a/src/coap_net.c b/src/coap_net.c index d0eae635..b79a9ce0 100644 --- a/src/coap_net.c +++ b/src/coap_net.c @@ -3605,6 +3605,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu /* Cannot handle critical option */ pdu->crit_opt = 0; resp = 402; + resource = NULL; goto fail_response; } is_proxy_uri = 0; @@ -3614,6 +3615,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } resource = NULL; } + assert(resource == NULL); if (!skip_hop_limit_check) { opt = coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter); @@ -3726,6 +3728,28 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu uri_path->s); resp = 202; goto fail_response; + } else if ((context->dyn_create_handler != NULL) && + (pdu->code == COAP_REQUEST_CODE_PUT || pdu->code == COAP_REQUEST_CODE_POST)) { + /* Above test must be the same as in coap_op_dyn_resource_load_disk() */ + if (context->dynamic_cur < context->dynamic_max || context->dynamic_max == 0) { +#if COAP_WITH_OBSERVE_PERSIST + /* If we are maintaining Observe persist */ + context->unknown_pdu = pdu; + context->unknown_session = session; +#endif /* COAP_WITH_OBSERVE_PERSIST */ + coap_lock_callback_ret(resource, context->dyn_create_handler(session, pdu)); +#if COAP_WITH_OBSERVE_PERSIST + /* If we are maintaining Observe persist */ + context->unknown_pdu = NULL; + context->unknown_session = NULL; +#endif /* COAP_WITH_OBSERVE_PERSIST */ + } + if (!resource) { + resp = 406; + goto fail_response; + } + context->dynamic_cur++; + resource->is_dynamic = 1; } else { /* request for any another resource, return 4.04 */ coap_log_debug("request for unknown resource '%*.*s', return 4.04\n", @@ -3736,6 +3760,8 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu } + coap_resource_reference_lkd(resource); + #if COAP_OSCORE_SUPPORT if ((resource->flags & COAP_RESOURCE_FLAGS_OSCORE_ONLY) && !session->oscore_encryption) { coap_log_debug("request for OSCORE only resource '%*.*s', return 4.04\n", @@ -3920,7 +3946,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu resource->uri_path->s); coap_lock_callback_release(h(resource, session, pdu, query, response), /* context is being freed off */ - coap_delete_string(query); goto finish); + goto finish); } /* Check validity of response code */ @@ -4072,6 +4098,8 @@ drop_it_no_debug: #endif /* COAP_Q_BLOCK_SUPPORT */ finish: + if (resource) + coap_resource_release_lkd(resource); coap_delete_string(uri_path); return; @@ -4082,6 +4110,8 @@ fail_response: &opt_filter); if (response) goto skip_handler; + if (resource) + coap_resource_release_lkd(resource); coap_delete_string(uri_path); } #endif /* COAP_SERVER_SUPPORT */ @@ -5087,6 +5117,7 @@ coap_startup(void) { (const uint8_t *)".well-known/core" }; memset(&resource_uri_wellknown, 0, sizeof(resource_uri_wellknown)); + resource_uri_wellknown.ref = 1; resource_uri_wellknown.handler[COAP_REQUEST_GET-1] = hnd_get_wellknown_lkd; resource_uri_wellknown.flags = COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT; resource_uri_wellknown.uri_path = &well_known; @@ -5158,6 +5189,15 @@ coap_register_pong_handler(coap_context_t *context, context->pong_handler = handler; } +void +coap_register_dynamic_resource_handler(coap_context_t *context, + coap_resource_dynamic_create_t dyn_create_handler, + uint32_t dynamic_max) { + context->dyn_create_handler = dyn_create_handler; + context->dynamic_max = dynamic_max; + return; +} + COAP_API void coap_register_option(coap_context_t *ctx, uint16_t type) { coap_lock_lock(return); diff --git a/src/coap_resource.c b/src/coap_resource.c index 7020ea41..6227887e 100644 --- a/src/coap_resource.c +++ b/src/coap_resource.c @@ -265,6 +265,7 @@ coap_resource_init(coap_str_const_t *uri_path, int flags) { r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t)); if (r) { memset(r, 0, sizeof(coap_resource_t)); + r->ref = 1; if (!(flags & COAP_RESOURCE_FLAGS_RELEASE_URI)) { /* Need to take a copy if caller is not providing a release request */ @@ -299,6 +300,7 @@ coap_resource_unknown_init2(coap_method_handler_t put_handler, int flags) { r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t)); if (r) { memset(r, 0, sizeof(coap_resource_t)); + r->ref = 1; r->is_unknown = 1; /* Something unlikely to be used, but it shows up in the logs */ r->uri_path = coap_new_str_const(coap_unknown_resource_uri, sizeof(coap_unknown_resource_uri)-1); @@ -329,6 +331,7 @@ coap_resource_proxy_uri_init2(coap_method_handler_t handler, if (r) { size_t i; memset(r, 0, sizeof(coap_resource_t)); + r->ref = 1; r->is_proxy_uri = 1; /* Something unlikely to be used, but it shows up in the logs */ r->uri_path = coap_new_str_const(coap_proxy_resource_uri, sizeof(coap_proxy_resource_uri)-1); @@ -381,6 +384,7 @@ coap_resource_reverse_proxy_init(coap_method_handler_t handler, int flags) { r = (coap_resource_t *)coap_malloc_type(COAP_RESOURCE, sizeof(coap_resource_t)); if (r) { memset(r, 0, sizeof(coap_resource_t)); + r->ref = 1; r->is_unknown = 1; r->is_reverse_proxy = 1; /* Something unlikely to be used, but it shows up in the logs */ @@ -513,6 +517,17 @@ coap_free_resource(coap_resource_t *resource, coap_deleting_resource_t deleting) LL_FOREACH_SAFE(resource->subscribers, obs, otmp) { coap_delete_observer_internal(resource, obs->session, obs); } + coap_resource_release_lkd(resource); +} + +void +coap_resource_release_lkd(coap_resource_t *resource) { + + assert(resource->ref); + resource->ref--; + if (resource->ref) + return; + if (resource->proxy_name_count && resource->proxy_name_list) { size_t i; @@ -580,13 +595,9 @@ coap_delete_resource(coap_context_t *context, coap_resource_t *resource) { return 0; context = resource->context; - if (context) { - coap_lock_lock(return 0); - ret = coap_delete_resource_lkd(context, resource); - coap_lock_unlock(); - } else { - ret = coap_delete_resource_lkd(context, resource); - } + coap_lock_lock(return 0); + ret = coap_delete_resource_lkd(context, resource); + coap_lock_unlock(); return ret; } @@ -595,13 +606,12 @@ coap_delete_resource(coap_context_t *context, coap_resource_t *resource) { */ int coap_delete_resource_lkd(coap_context_t *context, coap_resource_t *resource) { + (void)context; + if (!resource) return 0; - context = resource->context; - if (context) { - coap_lock_check_locked(); - } + coap_lock_check_locked(); if (resource->is_unknown) { if (context && context->unknown_resource == resource) { @@ -615,6 +625,12 @@ coap_delete_resource_lkd(coap_context_t *context, coap_resource_t *resource) { /* remove resource from list */ RESOURCES_DELETE(context->resources, resource); } + if (resource->is_dynamic) { + if (context) { + assert(context->dynamic_cur); + context->dynamic_cur--; + } + } /* and free its allocated memory */ coap_free_resource(resource, COAP_DELETING_RESOURCE); @@ -1119,6 +1135,8 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, return; r->list_being_traversed = 1; + coap_resource_reference_lkd(r); + r->partiallydirty = 0; LL_FOREACH_SAFE(r->subscribers, obs, otmp) { @@ -1234,6 +1252,7 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, coap_session_release_lkd(obs_session); coap_pdu_release_lkd(obs_pdu); r->list_being_traversed = 0; + coap_resource_release_lkd(r); return); /* Check validity of response code */ @@ -1246,6 +1265,7 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, coap_session_release_lkd(obs_session); coap_pdu_release_lkd(obs_pdu); r->list_being_traversed = 0; + coap_resource_release_lkd(r); return; } @@ -1335,6 +1355,7 @@ cleanup: coap_session_release_lkd(obs_session); coap_pdu_release_lkd(obs_pdu); } + coap_resource_release_lkd(r); r->list_being_traversed = 0; } r->dirty = 0; @@ -1501,4 +1522,8 @@ coap_handle_failed_notify(coap_context_t *context, } } +void +coap_resource_reference_lkd(coap_resource_t *resource) { + resource->ref++; +} #endif /* ! COAP_SERVER_SUPPORT */ diff --git a/src/coap_subscribe.c b/src/coap_subscribe.c index 5cb5f638..2f29034b 100644 --- a/src/coap_subscribe.c +++ b/src/coap_subscribe.c @@ -714,7 +714,8 @@ coap_op_obs_cnt_load_disk(coap_context_t *context) { resource_key.length = strlen(buf); r = coap_get_resource_from_uri_path_lkd(context, &resource_key); if (r) { - coap_log_debug("persist: Initial observe number being updated\n"); + coap_log_debug("persist: Initial observe number being updated '%s' %u\n", + buf, observe_num); coap_persist_set_observe_num(r, observe_num); } } @@ -933,7 +934,7 @@ coap_op_dyn_resource_load_disk(coap_context_t *ctx) { coap_pdu_t *response = NULL; coap_string_t *query = NULL; - if (!ctx->unknown_resource) + if (!ctx->unknown_resource && !ctx->dyn_create_handler) return; fp_orig = fopen((const char *)ctx->dyn_resource_save_file->s, "r"); @@ -954,7 +955,7 @@ coap_op_dyn_resource_load_disk(coap_context_t *ctx) { if (!r) { /* Create the new resource using the application logic */ - coap_log_debug("persist: dynamic resource being re-created\n"); + coap_log_debug("persist: dynamic resource '%s' being re-created\n", name->s); /* * Need max space incase PDU is updated with updated token, * huge size etc. @@ -968,24 +969,38 @@ coap_op_dyn_resource_load_disk(coap_context_t *ctx) { raw_packet->length, request)) { goto fail; } - if (!ctx->unknown_resource->handler[request->code-1]) - goto fail; + r = ctx->unknown_resource; + if ((ctx->dyn_create_handler != NULL) && + (request->code == COAP_REQUEST_CODE_PUT || request->code == COAP_REQUEST_CODE_POST)) { + /* Above test must be the same as in handle_request() */ + if (ctx->dynamic_cur < ctx->dynamic_max || ctx->dynamic_max == 0) { + ctx->unknown_pdu = request; + ctx->unknown_session = session; + coap_lock_callback_ret(r, ctx->dyn_create_handler(session, request)); + ctx->unknown_pdu = NULL; + ctx->unknown_session = NULL; + } + } + if (!r || !r->handler[request->code-1]) + goto next; + response = coap_pdu_init(0, 0, 0, 0); if (!response) goto fail; query = coap_get_query(request); /* Call the application handler to set up this dynamic resource */ - coap_lock_callback_release(ctx->unknown_resource->handler[request->code-1](ctx->unknown_resource, - session, request, - query, response), + coap_lock_callback_release(r->handler[request->code-1](r, + session, request, + query, response), /* context is being freed off */ goto fail); coap_delete_string(query); query = NULL; - coap_delete_pdu_lkd(request); - request = NULL; coap_delete_pdu_lkd(response); response = NULL; +next: + coap_delete_pdu_lkd(request); + request = NULL; } coap_delete_string(name); coap_delete_binary(raw_packet);