1
0
mirror of https://github.com/obgm/libcoap.git synced 2025-10-14 02:19:34 +08:00

coap_resource.c: Make dynamic resource handling thread safe

Let libcoap do the necessary multi-thread locking during resource
generation, lookup and deletion.

Supported using a new handler set up by coap_register_dynamic_resource_handler().
This commit is contained in:
Jon Shallow
2025-06-15 13:26:26 +01:00
committed by Jon Shallow
parent 33b3f02006
commit f62c836ece
11 changed files with 397 additions and 169 deletions

View File

@@ -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);

View File

@@ -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.
*

View File

@@ -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 */
};
/**

View File

@@ -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))

View File

@@ -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;

View File

@@ -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

View File

@@ -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*

View File

@@ -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 <coap@LIBCOAP_API_VERSION@/coap.h>
/* 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]
----

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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);