Simplified Erbium support for separate responses.

This commit is contained in:
Matthias Kovatsch 2012-01-31 04:38:07 +01:00
parent b44d125d80
commit 4a02790c76
10 changed files with 238 additions and 77 deletions

View file

@ -206,7 +206,7 @@ handle_incoming_data(void)
}
} else {
error = MEMORY_ALLOC_ERR;
error = MEMORY_ALLOCATION_ERROR;
}
}
else

View file

@ -226,7 +226,7 @@ typedef enum
NO_ERROR,
/* Memory errors */
MEMORY_ALLOC_ERR,
MEMORY_ALLOCATION_ERROR,
MEMORY_BOUNDARY_EXCEEDED,
/* CoAP errors */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ LIST(restful_periodic_services);
void
rest_init_framework(void)
rest_init_engine(void)
{
list_init(restful_services);

View file

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

View file

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