diff --git a/apps/er-coap-03/er-coap-03-engine.c b/apps/er-coap-03/er-coap-03-engine.c index 610209f13..048261349 100644 --- a/apps/er-coap-03/er-coap-03-engine.c +++ b/apps/er-coap-03/er-coap-03-engine.c @@ -206,7 +206,7 @@ handle_incoming_data(void) } } else { - error = MEMORY_ALLOC_ERR; + error = MEMORY_ALLOCATION_ERROR; } } else diff --git a/apps/er-coap-03/er-coap-03.h b/apps/er-coap-03/er-coap-03.h index 9b5f3a887..f6a2639ba 100644 --- a/apps/er-coap-03/er-coap-03.h +++ b/apps/er-coap-03/er-coap-03.h @@ -226,7 +226,7 @@ typedef enum NO_ERROR, /* Memory errors */ - MEMORY_ALLOC_ERR, + MEMORY_ALLOCATION_ERROR, MEMORY_BOUNDARY_EXCEEDED, /* CoAP errors */ diff --git a/apps/er-coap-07/er-coap-07-engine.c b/apps/er-coap-07/er-coap-07-engine.c index 1d27b6760..df86d5719 100644 --- a/apps/er-coap-07/er-coap-07-engine.c +++ b/apps/er-coap-07/er-coap-07-engine.c @@ -100,7 +100,7 @@ handle_incoming_data(void) if (coap_error_code==NO_ERROR) { - /*TODO duplicates suppression, if required */ + /*TODO duplicates suppression, if required by application */ PRINTF(" Parsed: v %u, t %u, oc %u, c %u, mid %u\n", message->version, message->type, message->option_count, message->code, message->mid); PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path); @@ -112,10 +112,10 @@ handle_incoming_data(void) /* Use transaction buffer for response to confirmable request. */ if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) ) { - static uint32_t block_num = 0; - static uint16_t block_size = REST_MAX_CHUNK_SIZE; - static uint32_t block_offset = 0; - static int32_t new_offset = 0; + uint32_t block_num = 0; + uint16_t block_size = REST_MAX_CHUNK_SIZE; + uint32_t block_offset = 0; + int32_t new_offset = 0; /* prepare response */ if (message->type==COAP_TYPE_CON) @@ -143,11 +143,6 @@ handle_incoming_data(void) block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); new_offset = block_offset; } - else - { - block_size = REST_MAX_CHUNK_SIZE; - new_offset = 0; - } /* Invoke resource handler. */ if (service_cbk) @@ -155,42 +150,55 @@ handle_incoming_data(void) /* Call REST framework and check if found and allowed. */ if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset)) { - /* Apply blockwise transfers. */ - if ( IS_OPTION(message, COAP_OPTION_BLOCK2) ) + if (coap_error_code==NO_ERROR) { - /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ - if (new_offset==block_offset) + /* Apply blockwise transfers. */ + if ( IS_OPTION(message, COAP_OPTION_BLOCK2) ) { - PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size); - if (block_offset >= response->payload_len) + /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ + if (new_offset==block_offset) { - PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n"); + PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size); + if (block_offset >= response->payload_len) + { + PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n"); - response->code = BAD_OPTION_4_02; - coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */ + response->code = BAD_OPTION_4_02; + coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */ + } + else + { + coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size); + coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size)); + } /* if (valid offset) */ } else { - coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size); - coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size)); - } /* if (valid offset) */ + /* resource provides chunk-wise data */ + PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset); + coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size); + if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size); + } /* if (resource aware of blockwise) */ } - else + else if (new_offset!=0) { - /* resource provides chunk-wise data */ - PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset); - coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size); - if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size); - } /* if (resource aware of blockwise) */ - } - else if (new_offset!=0) - { - PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE); + PRINTF("Blockwise: no block option for blockwise resource, using block size %u\n", REST_MAX_CHUNK_SIZE); - coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE); - coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE)); - } /* if (blockwise request) */ + coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE); + coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE)); + } /* if (blockwise request) */ + } /* no errors/hooks */ + } /* successful service callback */ + + /* Serialize response. */ + if (coap_error_code==NO_ERROR) + { + if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0) + { + coap_error_code = PACKET_SERIALIZATION_ERROR; + } } + } else { @@ -198,14 +206,8 @@ handle_incoming_data(void) coap_error_message = "Service callback undefined"; } /* if (service callback) */ - /* serialize Response. */ - if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0) - { - coap_error_code = PACKET_SERIALIZATION_ERROR; - } - } else { - coap_error_code = MEMORY_ALLOC_ERR; + coap_error_code = MEMORY_ALLOCATION_ERROR; coap_error_message = "Transaction buffer allocation failed"; } /* if (transaction buffer) */ } @@ -241,12 +243,20 @@ handle_incoming_data(void) } } /* if (ACKed transaction) */ transaction = NULL; - } + + } /* Request or Response */ + } /* if (parsed correctly) */ - if (coap_error_code==NO_ERROR) { + if (coap_error_code==NO_ERROR) + { if (transaction) coap_send_transaction(transaction); } + else if (coap_error_code==MANUAL_RESPONSE) + { + PRINTF("Clearing transaction for manual response"); + coap_clear_transaction(transaction); + } else { PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message); diff --git a/apps/er-coap-07/er-coap-07-separate.c b/apps/er-coap-07/er-coap-07-separate.c index 259398f26..b5f9c5c3a 100644 --- a/apps/er-coap-07/er-coap-07-separate.c +++ b/apps/er-coap-07/er-coap-07-separate.c @@ -40,6 +40,7 @@ #include #include "er-coap-07-separate.h" +#include "er-coap-07-transactions.h" #define DEBUG 0 #if DEBUG @@ -53,20 +54,66 @@ #endif /*-----------------------------------------------------------------------------------*/ -void coap_separate_handler(resource_t *resource, void *request, void *response) +int coap_separate_handler(resource_t *resource, void *request, void *response) { coap_packet_t *const coap_req = (coap_packet_t *) request; coap_packet_t *const coap_res = (coap_packet_t *) response; - PRINTF("Separate response for /%s \n", resource->url); - /* send separate ACK. */ - coap_packet_t ack[1]; - /* ACK with empty code (0) */ - coap_init_message(ack, COAP_TYPE_ACK, 0, coap_req->mid); - /* Should only overwrite Header which is already parsed to request. */ - coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, (uip_appdata + uip_ext_len), coap_serialize_message(ack, (uip_appdata + uip_ext_len))); + PRINTF("Separate response for /%s MID %u\n", resource->url, coap_res->mid); - /* Change response to separate response. */ - coap_res->type = COAP_TYPE_CON; - coap_res->mid = coap_get_mid(); + /* Only ack CON requests. */ + if (coap_req->type==COAP_TYPE_CON) + { + coap_transaction_t *const t = coap_get_transaction_by_mid(coap_res->mid); + + /* send separate ACK. */ + coap_packet_t ack[1]; + /* ACK with empty code (0) */ + coap_init_message(ack, COAP_TYPE_ACK, 0, coap_req->mid); + /* Serializing into IPBUF: Only overwrites header parts that are already parsed into the request struct. */ + coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, (uip_appdata), coap_serialize_message(ack, uip_appdata)); + + /* Change response to separate response. */ + coap_res->type = COAP_TYPE_CON; + coap_res->mid = coap_get_mid(); + + /* Update MID in transaction for identification. */ + t->mid = coap_res->mid; + } + + /* Pre-handlers could skip the handling by returning 0. */ + return 1; +} + +int +coap_separate_response(void *response, coap_separate_t *separate_store) +{ + coap_packet_t *const coap_res = (coap_packet_t *) response; + coap_transaction_t *const t = coap_get_transaction_by_mid(coap_res->mid); + + if (t) + { + uip_ipaddr_copy(&separate_store->addr, &t->addr); + separate_store->port = t->port; + + separate_store->mid = coap_res->mid; + separate_store->type = coap_res->type; + + memcpy(separate_store->token, coap_res->token, coap_res->token_len); + separate_store->token_len = coap_res->token_len; + + separate_store->block2_num = coap_res->block2_num; + separate_store->block2_more = coap_res->block2_more; + separate_store->block2_size = coap_res->block2_size; + separate_store->block2_offset = coap_res->block2_offset; + + /* Signal the engine to skip automatic response and clear transaction by engine. */ + coap_error_code = MANUAL_RESPONSE; + + return 1; + } + else + { + return 0; + } } diff --git a/apps/er-coap-07/er-coap-07-separate.h b/apps/er-coap-07/er-coap-07-separate.h index ace2388f5..8d677ea0d 100644 --- a/apps/er-coap-07/er-coap-07-separate.h +++ b/apps/er-coap-07/er-coap-07-separate.h @@ -41,6 +41,28 @@ #include "er-coap-07.h" -void coap_separate_handler(resource_t *resource, void *request, void *response); +typedef struct coap_separate { + + uip_ipaddr_t addr; + uint16_t port; + + coap_message_type_t type; + uint16_t mid; + + uint8_t token_len; + uint8_t token[COAP_TOKEN_LEN]; + + uint32_t block2_num; + uint8_t block2_more; + uint16_t block2_size; + uint32_t block2_offset; + + /* Add fields for addition information to be saved here, e.g.: */ + char buffer[17]; + +} coap_separate_t; + +int coap_separate_handler(resource_t *resource, void *request, void *response); +int coap_separate_response(void *response, coap_separate_t *separate_store); #endif /* COAP_SEPARATE_H_ */ diff --git a/apps/er-coap-07/er-coap-07-transactions.c b/apps/er-coap-07/er-coap-07-transactions.c index 4dfff6553..10070a29e 100644 --- a/apps/er-coap-07/er-coap-07-transactions.c +++ b/apps/er-coap-07/er-coap-07-transactions.c @@ -87,6 +87,8 @@ coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port) /* save client address */ uip_ipaddr_copy(&t->addr, addr); t->port = port; + + list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */ } return t; @@ -122,8 +124,6 @@ coap_send_transaction(coap_transaction_t *t) etimer_restart(&t->retrans_timer); /* interval updated above */ process_current = process_actual; - list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */ - t = NULL; } else diff --git a/apps/er-coap-07/er-coap-07.h b/apps/er-coap-07/er-coap-07.h index 62f943898..2284f0a00 100644 --- a/apps/er-coap-07/er-coap-07.h +++ b/apps/er-coap-07/er-coap-07.h @@ -140,15 +140,13 @@ typedef enum { GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - /* Memory errors */ - IMPLEMENTATION_ERROR = 192, - MEMORY_ALLOC_ERR = 193, - MEMORY_BOUNDARY_EXCEEDED = 194, + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, + PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE - /* CoAP errors */ - UNIMPLEMENTED_CRITICAL_OPTION, - UNKNOWN_CRITICAL_OPTION, - PACKET_SERIALIZATION_ERROR } coap_status_t; /* CoAP header options */ diff --git a/apps/erbium/erbium.c b/apps/erbium/erbium.c index d2708616c..f624dd594 100644 --- a/apps/erbium/erbium.c +++ b/apps/erbium/erbium.c @@ -60,7 +60,7 @@ LIST(restful_periodic_services); void -rest_init_framework(void) +rest_init_engine(void) { list_init(restful_services); diff --git a/apps/erbium/erbium.h b/apps/erbium/erbium.h index f550deeed..1c29ab125 100644 --- a/apps/erbium/erbium.h +++ b/apps/erbium/erbium.h @@ -282,7 +282,7 @@ periodic_resource_t periodic_resource_##name = {NULL, &resource_##name, period, /* * Initializes REST framework and starts HTTP or COAP process */ -void rest_init_framework(void); +void rest_init_engine(void); /* * Resources wanted to be accessible should be activated with the following code. diff --git a/examples/er-rest-example/rest-server-example.c b/examples/er-rest-example/rest-server-example.c index 0195295b3..88dab13a0 100644 --- a/examples/er-rest-example/rest-server-example.c +++ b/examples/er-rest-example/rest-server-example.c @@ -47,11 +47,12 @@ #define REST_RES_HELLO 1 #define REST_RES_MIRROR 0 /* causes largest code size */ #define REST_RES_CHUNKS 1 -#define REST_RES_POLLING 1 +#define REST_RES_POLLING 0 +#define REST_RES_SEPARATE 1 #define REST_RES_EVENT 1 #define REST_RES_LEDS 1 #define REST_RES_TOGGLE 1 -#define REST_RES_LIGHT 1 +#define REST_RES_LIGHT 0 #define REST_RES_BATTERY 1 @@ -149,8 +150,7 @@ mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferre /* The ETag and Token is copied to the header. */ uint8_t opaque[] = {0x0A, 0xBC, 0xDE}; - /* Strings are not copied and should be static or in program memory (char *str = "string in .text";). - * They must be '\0'-terminated as the setters use strlen(). */ + /* Strings are not copied, so use static string buffers or strings in .text memory (char *str = "string in .text";). */ static char location[] = {'/','f','/','a','?','k','&','e', 0}; /* Getter for the header option Content-Type. If the option is not set, text/plain is returned by default. */ @@ -390,6 +390,79 @@ polling_periodic_handler(resource_t *r) } #endif +#if REST_RES_SEPARATE && WITH_COAP > 3 +/* Required to manually (=not by the engine) handle the response transaction. */ +#include "er-coap-07-separate.h" +#include "er-coap-07-transactions.h" +/* + * CoAP-specific example for separate responses. + * This resource is . + */ +RESOURCE(separate, METHOD_GET, "debug/separate", "title=\"Separate demo\""); + +static uint8_t separate_active = 0; +static coap_separate_t separate_store[1]; + +void +separate_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* + * Example allows only one open separate response. + * For multiple, the application must manage the list of stores. + */ + if (separate_active) + { + REST.set_response_status(response, REST.status.SERVICE_UNAVAILABLE); + const char msg[] = "AlreadyInUse"; + REST.set_response_payload(response, (uint8_t *)msg, sizeof(msg)-1); + } + else + { + separate_active = 1; + + /* Take over and skip response by engine. */ + coap_separate_response(response, separate_store); + + /* + * At the moment, only the minimal information is stored in the store (client address, port, token, MID, type, and Block2). + * Extend the store, if the application requires additional information from this handler. + * buffer is an example field for custom information. + */ + snprintf(separate_store->buffer, sizeof(separate_store->buffer), "StoredInfo"); + } +} + +void +separate_finalize_handler() +{ + if (separate_active) + { + coap_transaction_t *transaction = NULL; + if ( (transaction = coap_new_transaction(separate_store->mid, &separate_store->addr, separate_store->port)) ) + { + coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ + coap_init_message(response, separate_store->type, CONTENT_2_05, separate_store->mid); + + coap_set_payload(response, separate_store->buffer, strlen(separate_store->buffer)); + + /* Warning: No check for serialization error. */ + transaction->packet_len = coap_serialize_message(response, transaction->packet); + coap_send_transaction(transaction); + /* The engine will clear the transaction (right after send for NON, after acked for CON). */ + + separate_active = 0; + } + else + { + /* + * Set timer for retry, send error message, ... + * The example simply waits for another button press. + */ + } + } /* if (separate_active) */ +} +#endif + #if defined (PLATFORM_HAS_BUTTON) && REST_RES_EVENT /* * Example for an event resource. @@ -594,8 +667,8 @@ PROCESS_THREAD(rest_server_example, ev, data) configure_routing(); #endif - /* Initialize the REST framework. */ - rest_init_framework(); + /* Initialize the REST engine. */ + rest_init_engine(); /* Activate the application-specific resources. */ #if REST_RES_HELLO @@ -610,6 +683,11 @@ PROCESS_THREAD(rest_server_example, ev, data) #if REST_RES_POLLING rest_activate_periodic_resource(&periodic_resource_polling); #endif +#if REST_RES_SEPARATE && WITH_COAP > 3 + rest_set_pre_handler(&resource_separate, coap_separate_handler); + rest_activate_resource(&resource_separate); +#endif + #if defined (PLATFORM_HAS_BUTTON) && REST_RES_EVENT SENSORS_ACTIVATE(button_sensor); rest_activate_event_resource(&resource_event); @@ -634,11 +712,17 @@ PROCESS_THREAD(rest_server_example, ev, data) /* Define application-specific events here. */ while(1) { PROCESS_WAIT_EVENT(); -#if defined (PLATFORM_HAS_BUTTON) && REST_RES_EVENT +#if defined (PLATFORM_HAS_BUTTON) if (ev == sensors_event && data == &button_sensor) { PRINTF("BUTTON\n"); +#if REST_RES_EVENT /* Call the event_handler for this application-specific event. */ event_event_handler(&resource_event); +#endif +#if REST_RES_SEPARATE && WITH_COAP>3 + /* Also call the separate response example handler. */ + separate_finalize_handler(); +#endif } #endif /* PLATFORM_HAS_BUTTON */ } /* while (1) */