Simplified Erbium support for separate responses.
This commit is contained in:
parent
b44d125d80
commit
4a02790c76
10 changed files with 238 additions and 77 deletions
|
@ -206,7 +206,7 @@ handle_incoming_data(void)
|
|||
}
|
||||
|
||||
} else {
|
||||
error = MEMORY_ALLOC_ERR;
|
||||
error = MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -226,7 +226,7 @@ typedef enum
|
|||
NO_ERROR,
|
||||
|
||||
/* Memory errors */
|
||||
MEMORY_ALLOC_ERR,
|
||||
MEMORY_ALLOCATION_ERROR,
|
||||
MEMORY_BOUNDARY_EXCEEDED,
|
||||
|
||||
/* CoAP errors */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -60,7 +60,7 @@ LIST(restful_periodic_services);
|
|||
|
||||
|
||||
void
|
||||
rest_init_framework(void)
|
||||
rest_init_engine(void)
|
||||
{
|
||||
list_init(restful_services);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) */
|
||||
|
|
Loading…
Reference in a new issue