diff --git a/apps/er-coap-03/Makefile.er-coap-03 b/apps/er-coap-03/Makefile.er-coap-03 deleted file mode 100644 index c308c18d5..000000000 --- a/apps/er-coap-03/Makefile.er-coap-03 +++ /dev/null @@ -1 +0,0 @@ -er-coap-03_src = er-coap-03-engine.c er-coap-03.c er-coap-03-transactions.c er-coap-03-observing.c diff --git a/apps/er-coap-03/er-coap-03-engine.c b/apps/er-coap-03/er-coap-03-engine.c deleted file mode 100644 index 8e21d30d3..000000000 --- a/apps/er-coap-03/er-coap-03-engine.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP implementation of the REST Engine - * \author - * Matthias Kovatsch - */ - -#include -#include -#include -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-03-engine.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#define PRINTBITS(buf,len) { \ - int i,j=0; \ - for (i=0; i=0; --j) { \ - PRINTF("%c", (((char *)buf)[i] & 1<srcipaddr); - PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() ); - PRINTBITS(uip_appdata, uip_datalen()); - PRINTF("\n"); - - coap_packet_t message[1]; - coap_transaction_t *transaction = NULL; - - error = coap_parse_message(message, uip_appdata, uip_datalen()); - - if (error==NO_ERROR) - { - - /*TODO duplicates suppression, if required */ - - PRINTF(" Parsed: v %u, t %u, oc %u, c %u, tid %u\n", message->version, message->type, message->option_count, message->code, message->tid); - PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path); - PRINTF(" Payload: %.*s\n", message->payload_len, message->payload); - - /* Handle requests. */ - if (message->code >= COAP_GET && message->code <= COAP_DELETE) - { - /* Use transaction buffer for response to confirmable request. */ - if ( (transaction = coap_new_transaction(message->tid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) ) - { - 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 */ - coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ - if (message->type==COAP_TYPE_CON) - { - /* Reliable CON requests are answered with an ACK. */ - coap_init_message(response, COAP_TYPE_ACK, OK_200, message->tid); - } - else - { - /* Unreliable NON requests are answered with a NON as well. */ - coap_init_message(response, COAP_TYPE_NON, OK_200, coap_get_tid()); - } - - /* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */ - if (IS_OPTION(message, COAP_OPTION_TOKEN)) - { - coap_set_header_token(response, message->token, message->token_len); - SET_OPTION(response, COAP_OPTION_TOKEN); - } - - /* get offset for blockwise transfers */ - if (coap_get_header_block(message, &block_num, NULL, &block_size, &block_offset)) - { - PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); - block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); - new_offset = block_offset; - } - - /*------------------------------------------*/ - /* call application-specific handler */ - /*------------------------------------------*/ - if (service_cbk) { - service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset); - } - /*------------------------------------------*/ - - - /* apply blockwise transfers */ - if ( IS_OPTION(message, COAP_OPTION_BLOCK) ) - { - /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ - if (new_offset==block_offset) - { - PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size); - if (block_offset >= response->payload_len) - { - response->code = BAD_REQUEST_400; - coap_set_payload(response, (uint8_t*)"Block out of scope", 18); - } - else - { - coap_set_header_block(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 - { - /* resource provides chunk-wise data */ - PRINTF("Blockwise: blockwise resource, new offset %ld\n", new_offset); - coap_set_header_block(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); - - coap_set_header_block(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) */ - - if ((transaction->packet_len = coap_serialize_message(response, transaction->packet))==0) - { - error = PACKET_SERIALIZATION_ERROR; - } - - } else { - error = MEMORY_ALLOCATION_ERROR; - } - } - else - { - /* Responses */ - coap_transaction_t *t; - - if (message->type==COAP_TYPE_ACK) - { - PRINTF("Received ACK\n"); - } - else if (message->type==COAP_TYPE_RST) - { - PRINTF("Received RST\n"); - /* Cancel possible subscriptions. */ - if (IS_OPTION(message, COAP_OPTION_TOKEN)) - { - PRINTF(" Token 0x%02X%02X\n", message->token[0], message->token[1]); - coap_remove_observer_by_token(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->token, message->token_len); - } - } - - if ( (t = coap_get_transaction_by_tid(message->tid)) ) - { - /* Free transaction memory before callback, as it may create a new transaction. */ - restful_response_handler callback = t->callback; - void *callback_data = t->callback_data; - coap_clear_transaction(t); - - /* Check if someone registered for the response */ - if (callback) { - callback(callback_data, message); - } - } /* if (transaction) */ - } - } /* if (parsed correctly) */ - - if (error==NO_ERROR) { - if (transaction) coap_send_transaction(transaction); - } - else - { - PRINTF("ERROR %u: %s\n", error, error_messages[error]); - - /* reuse input buffer */ - coap_init_message(message, COAP_TYPE_ACK, INTERNAL_SERVER_ERROR_500, message->tid); - coap_set_payload(message, (uint8_t *) error_messages[error], strlen(error_messages[error])); - coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata)); - } - } /* if (new data) */ - - return error; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_receiver_init() -{ - process_start(&coap_receiver, NULL); -} -/*-----------------------------------------------------------------------------------*/ -void -coap_set_service_callback(service_callback_t callback) -{ - service_cbk = callback; -} -/*-----------------------------------------------------------------------------------*/ -rest_resource_flags_t -coap_get_rest_method(void *packet) -{ - return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1)); -} -/*-----------------------------------------------------------------------------------*/ -int -coap_set_rest_status(void *packet, unsigned int code) -{ - if (code <= 0xFF) - { - ((coap_packet_t *)packet)->code = (uint8_t) code; - return 1; - } - else - { - return 0; - } -} -/*-----------------------------------------------------------------------------------*/ -/*- Server part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -/* The discover resource should be included when using CoAP. */ -RESOURCE(well_known_core, METHOD_GET, ".well-known/core", ""); -void -well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - /* Response might be NULL for non-confirmable requests. */ - if (response) - { - size_t strpos = 0; - size_t bufpos = 0; - resource_t* resource = NULL; - - for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next) - { - strpos += snprintf((char *) buffer + bufpos, REST_MAX_CHUNK_SIZE - bufpos + 1, - "%s%s%s", - resource->url, - resource->attributes[0] ? ";" : "", - resource->attributes, - resource->next ? "," : "" ); - - PRINTF("discover: %s\n", resource->url); - - if (strpos <= *offset) - { - /* Discard output before current block */ - PRINTF(" if %d <= %ld B\n", strpos, *offset); - PRINTF(" %s\n", buffer); - bufpos = 0; - } - else /* (strpos > *offset) */ - { - /* output partly in block */ - size_t len = MIN(strpos - *offset, preferred_size); - - PRINTF(" el %d/%d @ %ld B\n", len, preferred_size, *offset); - - /* Block might start in the middle of the output; align with buffer start. */ - if (bufpos == 0) - { - memmove(buffer, buffer+strlen((char *)buffer)-strpos+*offset, len); - } - - bufpos = len; - PRINTF(" %s\n", buffer); - - if (bufpos >= preferred_size) - { - break; - } - } - } - - if (bufpos>0) { - coap_set_payload(response, buffer, bufpos ); - coap_set_header_content_type(response, APPLICATION_LINK_FORMAT); - } - else - { - coap_set_rest_status(response, BAD_REQUEST_400); - coap_set_payload(response, (uint8_t*)"Block out of scope", 18); - } - - if (resource==NULL) { - *offset = -1; - } - else - { - *offset += bufpos; - } - } -} -/*-----------------------------------------------------------------------------------*/ -PROCESS_THREAD(coap_receiver, ev, data) -{ - PROCESS_BEGIN(); - PRINTF("Starting CoAP-03 receiver...\n"); - - rest_activate_resource(&resource_well_known_core); - - coap_register_as_transaction_handler(); - coap_init_connection(SERVER_LISTEN_PORT); - - while(1) { - PROCESS_YIELD(); - - if(ev == tcpip_event) { - handle_incoming_data(); - } else if (ev == PROCESS_EVENT_TIMER) { - /* retransmissions are handled here */ - coap_check_transactions(); - } - } /* while (1) */ - - PROCESS_END(); -} -/*-----------------------------------------------------------------------------------*/ -/*- Client part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void blocking_request_callback(void *callback_data, void *response) { - struct request_state_t *state = (struct request_state_t *) callback_data; - state->response = (coap_packet_t*) response; - process_poll(state->process); -} -/*-----------------------------------------------------------------------------------*/ -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)) { - PT_BEGIN(&state->pt); - - static uint8_t more; - static uint32_t res_block; - static uint8_t block_error; - - state->block_num = 0; - state->response = NULL; - state->process = PROCESS_CURRENT(); - - more = 0; - res_block = 0; - block_error = 0; - - do { - request->tid = coap_get_tid(); - if ((state->transaction = coap_new_transaction(request->tid, remote_ipaddr, remote_port))) - { - state->transaction->callback = blocking_request_callback; - state->transaction->callback_data = state; - - if (state->block_num>0) - { - coap_set_header_block(request, state->block_num, 0, REST_MAX_CHUNK_SIZE); - } - - state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet); - - coap_send_transaction(state->transaction); - PRINTF("Requested #%lu (TID %u)\n", state->block_num, request->tid); - - PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); - - if (!state->response) - { - PRINTF("Server not responding\n"); - PT_EXIT(&state->pt); - } - - coap_get_header_block(state->response, &res_block, &more, NULL, NULL); - - PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len); - - if (res_block==state->block_num) - { - request_callback(state->response); - ++(state->block_num); - } - else - { - PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num); - ++block_error; - } - } - else - { - PRINTF("Could not allocate transaction buffer"); - PT_EXIT(&state->pt); - } - } while (more && block_errorpt); -} -/*-----------------------------------------------------------------------------------*/ -/*- Engine Interface ----------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -const struct rest_implementation coap_rest_implementation = { - "CoAP-03", - - coap_receiver_init, - coap_set_service_callback, - - coap_get_header_uri_path, - coap_set_header_uri_path, - coap_get_rest_method, - coap_set_rest_status, - - coap_get_header_content_type, - coap_set_header_content_type, - NULL, - NULL, - NULL, - coap_get_header_max_age, - coap_set_header_max_age, - coap_set_header_etag, - NULL, - NULL, - coap_get_header_uri_host, - coap_set_header_location, - - coap_get_payload, - coap_set_payload, - - coap_get_header_uri_query, - coap_get_query_variable, - coap_get_post_variable, - - coap_notify_observers, - (restful_post_handler) coap_observe_handler, - - NULL, - NULL, - - { - OK_200, - CREATED_201, - OK_200, - OK_200, - NOT_MODIFIED_304, - - BAD_REQUEST_400, - METHOD_NOT_ALLOWED_405, - BAD_REQUEST_400, - METHOD_NOT_ALLOWED_405, - NOT_FOUND_404, - METHOD_NOT_ALLOWED_405, - UNSUPPORTED_MEDIA_TYPE_415, - BAD_REQUEST_400, - UNSUPPORTED_MEDIA_TYPE_415, - - INTERNAL_SERVER_ERROR_500, - CRITICAL_OPTION_NOT_SUPPORTED, - BAD_GATEWAY_502, - SERVICE_UNAVAILABLE_503, - GATEWAY_TIMEOUT_504, - INTERNAL_SERVER_ERROR_500 - }, - - { - TEXT_PLAIN, - TEXT_XML, - TEXT_CSV, - TEXT_HTML, - IMAGE_GIF, - IMAGE_JPEG, - IMAGE_PNG, - IMAGE_TIFF, - AUDIO_RAW, - VIDEO_RAW, - APPLICATION_LINK_FORMAT, - APPLICATION_XML, - APPLICATION_OCTET_STREAM, - APPLICATION_RDF_XML, - APPLICATION_SOAP_XML, - APPLICATION_ATOM_XML, - APPLICATION_XMPP_XML, - APPLICATION_EXI, - APPLICATION_FASTINFOSET, - APPLICATION_SOAP_FASTINFOSET, - APPLICATION_JSON, - APPLICATION_OCTET_STREAM - } -}; diff --git a/apps/er-coap-03/er-coap-03-observing.c b/apps/er-coap-03/er-coap-03-observing.c deleted file mode 100644 index fe23f5d03..000000000 --- a/apps/er-coap-03/er-coap-03-observing.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#include -#include - -#include "er-coap-03-observing.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS); -LIST(observers_list); - -/*-----------------------------------------------------------------------------------*/ -coap_observer_t * -coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url) -{ - coap_observer_t *o = memb_alloc(&observers_memb); - - if (o) - { - o->url = url; - uip_ipaddr_copy(&o->addr, addr); - o->port = port; - o->token_len = token_len; - memcpy(o->token, token, token_len); - - stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL); - - PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - list_add(observers_list, o); - } - - return o; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_remove_observer(coap_observer_t *o) -{ - PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - - memb_free(&observers_memb, o); - list_remove(observers_list, o); -} - -int -coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port) -{ - int removed = 0; - coap_observer_t* obs = NULL; - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check Port %u\n", port); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} -int -coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len) -{ - int removed = 0; - coap_observer_t* obs = NULL; - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && memcmp(obs->token, token, token_len)==0) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification) -{ - coap_packet_t *const coap_res = (coap_packet_t *) notification; - coap_observer_t* obs = NULL; - uint8_t preferred_type = coap_res->type; - - PRINTF("Observing: Notification from %s\n", resource->url); - - /* Iterate over observers. */ - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - if (obs->url==resource->url) /* using RESOURCE url pointer as handle */ - { - coap_transaction_t *transaction = NULL; - - /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */ - - if ( (transaction = coap_new_transaction(coap_get_tid(), &obs->addr, obs->port)) ) - { - PRINTF(" Observer "); - PRINT6ADDR(&obs->addr); - PRINTF(":%u\n", obs->port); - - /* Prepare response */ - coap_res->tid = transaction->tid; - coap_set_header_observe(coap_res, obs_counter); - coap_set_header_token(coap_res, obs->token, obs->token_len); - - /* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */ - if (stimer_expired(&obs->refresh_timer)) - { - PRINTF(" Refreshing with CON\n"); - coap_res->type = COAP_TYPE_CON; - stimer_restart(&obs->refresh_timer); - } - else - { - coap_res->type = preferred_type; - } - - transaction->packet_len = coap_serialize_message(coap_res, transaction->packet); - - coap_send_transaction(transaction); - } - } - } -} -/*-----------------------------------------------------------------------------------*/ -void -coap_observe_handler(resource_t *resource, void *request, void *response) -{ - static char content[26]; - - if (response && ((coap_packet_t *)response)->code<128) /* response without error code */ - { - if (IS_OPTION((coap_packet_t *)request, COAP_OPTION_OBSERVE)) - { - if (IS_OPTION((coap_packet_t *)request, COAP_OPTION_TOKEN)) - { - if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, ((coap_packet_t *)request)->token, ((coap_packet_t *)request)->token_len, resource->url)) - { - coap_set_header_observe(response, 0); - coap_set_payload(response, (uint8_t *)content, snprintf(content, sizeof(content), "Added as observer %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS)); - } - else - { - ((coap_packet_t *)response)->code = SERVICE_UNAVAILABLE_503; - coap_set_payload(response, (uint8_t *)"Too many observers", 18); - } /* if (added observer) */ - } - else /* if (token) */ - { - ((coap_packet_t *)response)->code = TOKEN_OPTION_REQUIRED; - coap_set_payload(response, (uint8_t *)"Observing requires token", 24); - } /* if (token) */ - } - else /* if (observe) */ - { - /* Remove client if it is currently observing. */ - coap_remove_observer_by_client(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport); - } /* if (observe) */ - } -} diff --git a/apps/er-coap-03/er-coap-03-observing.h b/apps/er-coap-03/er-coap-03-observing.h deleted file mode 100644 index b9baad377..000000000 --- a/apps/er-coap-03/er-coap-03-observing.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_OBSERVING_H_ -#define COAP_OBSERVING_H_ - -#include "sys/stimer.h" -#include "er-coap-03.h" -#include "er-coap-03-transactions.h" - -#ifndef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS 4 -#endif /* COAP_MAX_OBSERVERS */ - -/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */ -#define COAP_OBSERVING_REFRESH_INTERVAL 60 - -#if COAP_MAX_OPEN_TRANSACTIONS - */ - -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-03-transactions.h" -#include "er-coap-03-observing.h" - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS); -LIST(transactions_list); - - -static struct process *transaction_handler_process = NULL; - -void -coap_register_as_transaction_handler() -{ - transaction_handler_process = PROCESS_CURRENT(); -} - -coap_transaction_t * -coap_new_transaction(uint16_t tid, uip_ipaddr_t *addr, uint16_t port) -{ - coap_transaction_t *t = memb_alloc(&transactions_memb); - - if (t) - { - t->tid = tid; - t->retrans_counter = 0; - - /* save client address */ - uip_ipaddr_copy(&t->addr, addr); - t->port = port; - } - - return t; -} - -void -coap_send_transaction(coap_transaction_t *t) -{ - PRINTF("Sending transaction %u\n", t->tid); - - coap_send_message(&t->addr, t->port, t->packet, t->packet_len); - - if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION)) - { - if (t->retrans_countertid); - - /*FIXME hack, maybe there is a better way, but avoid posting everything to the process */ - struct process *process_actual = PROCESS_CURRENT(); - process_current = transaction_handler_process; - etimer_set(&t->retrans_timer, CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (1<<(t->retrans_counter))); - process_current = process_actual; - - list_add(transactions_list, t); /* list itself makes sure same element is not added twice */ - - t = NULL; - } - else - { - /* timeout */ - PRINTF("Timeout\n"); - restful_response_handler callback = t->callback; - void *callback_data = t->callback_data; - - /* handle observers */ - coap_remove_observer_by_client(&t->addr, t->port); - - coap_clear_transaction(t); - - if (callback) { - callback(callback_data, NULL); - } - } - } - else - { - coap_clear_transaction(t); - } -} - -void -coap_clear_transaction(coap_transaction_t *t) -{ - if (t) - { - PRINTF("Freeing transaction %u: %p\n", t->tid, t); - - etimer_stop(&t->retrans_timer); - list_remove(transactions_list, t); - memb_free(&transactions_memb, t); - } -} - -coap_transaction_t * -coap_get_transaction_by_tid(uint16_t tid) -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (t->tid==tid) - { - PRINTF("Found transaction for TID %u: %p\n", t->tid, t); - return t; - } - } - return NULL; -} - -void -coap_check_transactions() -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (etimer_expired(&t->retrans_timer)) - { - ++(t->retrans_counter); - PRINTF("Retransmitting %u (%u)\n", t->tid, t->retrans_counter); - coap_send_transaction(t); - } - } -} diff --git a/apps/er-coap-03/er-coap-03.c b/apps/er-coap-03/er-coap-03.c deleted file mode 100644 index 8a770282b..000000000 --- a/apps/er-coap-03/er-coap-03.c +++ /dev/null @@ -1,769 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 03) - * \author - * Matthias Kovatsch - */ - -#ifdef CONTIKI_TARGET_NETSIM - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#else - #include "contiki.h" - #include "contiki-net.h" - #include - #include -#endif - -#include "er-coap-03.h" -#include "er-coap-03-transactions.h" - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -static struct uip_udp_conn *udp_conn = NULL; -static uint16_t current_tid = 0; - -/*-----------------------------------------------------------------------------------*/ -/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static -uint16_t -log_2(uint16_t value) -{ - uint16_t result = 0; - do { - value = value >> 1; - result++; - } while (value); - - return result ? result - 1 : result; -} -/*-----------------------------------------------------------------------------------*/ -static -uint32_t -bytes_2_uint32(uint8_t *bytes, uint16_t length) -{ - uint32_t var = 0; - int i = 0; - while (i>8; - bytes[i++] = 0xFF & var; - - return i; -} -*/ -/*-----------------------------------------------------------------------------------*/ -static -int -uint32_2_bytes(uint8_t *bytes, uint32_t var) -{ - int i = 0; - if (0xFF000000 & var) bytes[i++] = (0xFF & var>>24); - if (0xFFFF0000 & var) bytes[i++] = (0xFF & var>>16); - if (0xFFFFFF00 & var) bytes[i++] = (0xFF & var>>8); - bytes[i++] = 0xFF & var; - - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -int -coap_get_variable(const char *buffer, size_t length, const char *name, const char **output) -{ - const char *start = NULL; - const char *end = NULL; - const char *value_end = NULL; - size_t name_len = 0; - - /*initialize the output buffer first*/ - *output = 0; - - name_len = strlen(name); - end = buffer + length; - - for (start = buffer; start + name_len < end; ++start){ - if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && - strncmp(name, start, name_len)==0) { - - /* Point start to variable value */ - start += name_len + 1; - - /* Point end to the end of the value */ - value_end = (const char *) memchr(start, '&', end - start); - if (value_end == NULL) { - value_end = end; - } - - *output = start; - - return (value_end - start); - } - } - - return 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE SENDING ----------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_connection(uint16_t port) -{ - /* new connection with remote host */ - udp_conn = udp_new(NULL, 0, NULL); - udp_bind(udp_conn, port); - PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport)); - - /* Initialize transaction ID. */ - current_tid = random_rand(); -} -/*-----------------------------------------------------------------------------------*/ -uint16_t -coap_get_tid() -{ - ++current_tid; - PRINTF("Get TID %u\n", current_tid); - return current_tid; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_send_message(uip_ipaddr_t *addr, uint16_t port, const uint8_t *data, uint16_t length) -{ - /*configure connection to reply to client*/ - uip_ipaddr_copy(&udp_conn->ripaddr, addr); - udp_conn->rport = port; - - uip_udp_packet_send(udp_conn, data, length); - PRINTF("-sent UDP datagram------\n Length: %u\n -----------------------\n", length); - - /* Restore server connection to allow data from any node */ - memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr)); - udp_conn->rport = 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE PROCESSING -------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t tid) -{ - memset(packet, 0, sizeof(coap_packet_t)); - - ((coap_packet_t *)packet)->type = type; - ((coap_packet_t *)packet)->code = code; - ((coap_packet_t *)packet)->tid = tid; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_serialize_message(void *packet, uint8_t *buffer) -{ - ((coap_packet_t *)packet)->buffer = buffer; - ((coap_packet_t *)packet)->version = 1; - ((coap_packet_t *)packet)->option_count = 0; - - /* serialize options */ - uint8_t *option = ((coap_packet_t *)packet)->buffer + COAP_HEADER_LEN; - size_t option_len = 0; - int index = 0; - - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_CONTENT_TYPE)) { - ((coap_header_option_t *)option)->s.delta = 1; - ((coap_header_option_t *)option)->s.length = 1; - *(++option) = ((coap_packet_t *)packet)->content_type; - PRINTF("OPTION %u (type %u, len 1, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_CONTENT_TYPE, COAP_OPTION_CONTENT_TYPE - index); - PRINTF("Content-Type [%u]\n", ((coap_packet_t *)packet)->content_type); - index = COAP_OPTION_CONTENT_TYPE; - option += 1; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE)) { - option_len = uint32_2_bytes(option+1, ((coap_packet_t *)packet)->max_age); - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_MAX_AGE - index; - ((coap_header_option_t *)option)->s.length = option_len; - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_MAX_AGE, option_len, COAP_OPTION_MAX_AGE - index); - PRINTF("Max-Age [%lu]\n", ((coap_packet_t *)packet)->max_age); - index = COAP_OPTION_MAX_AGE; - option += 1 + option_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG)) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_ETAG - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->etag_len; - memcpy(++option, ((coap_packet_t *)packet)->etag, ((coap_packet_t *)packet)->etag_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_ETAG, ((coap_packet_t *)packet)->etag_len, COAP_OPTION_ETAG - index); - PRINTF("ETag %u [0x%02X", ((coap_packet_t *)packet)->etag_len, ((coap_packet_t *)packet)->etag[0]); /*FIXME always prints 4 bytes */ - PRINTF("%02X", ((coap_packet_t *)packet)->etag[1]); - PRINTF("%02X", ((coap_packet_t *)packet)->etag[2]); - PRINTF("%02X", ((coap_packet_t *)packet)->etag[3]); - PRINTF("]\n"); - index = COAP_OPTION_ETAG; - option += ((coap_packet_t *)packet)->etag_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST)) { - if (((coap_packet_t *)packet)->uri_host_len<15) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_HOST - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_host_len; - option += 1; - } else { - ((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_HOST - index; - ((coap_header_option_t *)option)->s.length = 15; - ((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_host_len - 15; - option += 2; - } - memcpy(option, ((coap_packet_t *)packet)->uri_host, ((coap_packet_t *)packet)->uri_host_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_HOST, ((coap_packet_t *)packet)->uri_host_len, COAP_OPTION_URI_HOST - index); - PRINTF("Uri-Auth [%.*s]\n", ((coap_packet_t *)packet)->uri_host_len, ((coap_packet_t *)packet)->uri_host); - index = COAP_OPTION_URI_HOST; - option += ((coap_packet_t *)packet)->uri_host_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH)) { - if (((coap_packet_t *)packet)->location_path_len<15) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_LOCATION_PATH - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->location_path_len; - option += 1; - } else { - ((coap_header_option_t *)option)->l.delta = COAP_OPTION_LOCATION_PATH - index; - ((coap_header_option_t *)option)->s.length = 15; - ((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->location_path_len - 15; - option += 2; - } - memcpy(option, ((coap_packet_t *)packet)->location_path, ((coap_packet_t *)packet)->location_path_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_LOCATION_PATH, ((coap_packet_t *)packet)->location_path_len, COAP_OPTION_LOCATION_PATH - index); - PRINTF("Location [%.*s]\n", ((coap_packet_t *)packet)->location_path_len, ((coap_packet_t *)packet)->location_path); - index = COAP_OPTION_LOCATION_PATH; - option += ((coap_packet_t *)packet)->location_path_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH)) { - if (((coap_packet_t *)packet)->uri_path_len<15) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_PATH - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_path_len; - option += 1; - } else { - ((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_PATH - index; - ((coap_header_option_t *)option)->s.length = 15; - ((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_path_len - 15; - option += 2; - } - memcpy(option, ((coap_packet_t *)packet)->uri_path, ((coap_packet_t *)packet)->uri_path_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_PATH, ((coap_packet_t *)packet)->uri_path_len, COAP_OPTION_URI_PATH - index); - PRINTF("Uri-Path [%.*s]\n", ((coap_packet_t *)packet)->uri_path_len, ((coap_packet_t *)packet)->uri_path); - index = COAP_OPTION_URI_PATH; - option += ((coap_packet_t *)packet)->uri_path_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE)) { - option_len = uint32_2_bytes(option+1, ((coap_packet_t *)packet)->observe); - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_OBSERVE - index; - ((coap_header_option_t *)option)->s.length = option_len; - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_OBSERVE, option_len, COAP_OPTION_OBSERVE - index); - PRINTF("Observe [%lu]\n", ((coap_packet_t *)packet)->observe); - index = COAP_OPTION_OBSERVE; - option += 1 + option_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN)) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_TOKEN - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->token_len; - memcpy(++option, ((coap_packet_t *)packet)->token, ((coap_packet_t *)packet)->token_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_TOKEN, ((coap_packet_t *)packet)->token_len, COAP_OPTION_TOKEN - index); - PRINTF("Token %u [0x%02X%02X]\n", ((coap_packet_t *)packet)->token_len, ((coap_packet_t *)packet)->token[0], ((coap_packet_t *)packet)->token[1]); /*FIXME always prints 2 bytes */ - index = COAP_OPTION_TOKEN; - option += ((coap_packet_t *)packet)->token_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK)) { - uint32_t block = ((coap_packet_t *)packet)->block_num << 4; - if (((coap_packet_t *)packet)->block_more) block |= 0x8; - block |= 0xF & log_2(((coap_packet_t *)packet)->block_size/16); - option_len = uint32_2_bytes(option+1, block); - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_BLOCK - index; - ((coap_header_option_t *)option)->s.length = option_len; - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_BLOCK, option_len, COAP_OPTION_BLOCK - index); - PRINTF("Block [%lu%s (%u B/blk)]\n", ((coap_packet_t *)packet)->block_num, ((coap_packet_t *)packet)->block_more ? "+" : "", ((coap_packet_t *)packet)->block_size); - index = COAP_OPTION_BLOCK; - option += 1 + option_len; - ++(((coap_packet_t *)packet)->option_count); - } - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) { - if (((coap_packet_t *)packet)->uri_query_len<15) { - ((coap_header_option_t *)option)->s.delta = COAP_OPTION_URI_QUERY - index; - ((coap_header_option_t *)option)->s.length = ((coap_packet_t *)packet)->uri_query_len; - option += 1; - } else { - ((coap_header_option_t *)option)->l.delta = COAP_OPTION_URI_QUERY - index; - ((coap_header_option_t *)option)->s.length = 15; - ((coap_header_option_t *)option)->l.length = ((coap_packet_t *)packet)->uri_query_len - 15; - option += 2; - } - memcpy(option, ((coap_packet_t *)packet)->uri_query, ((coap_packet_t *)packet)->uri_query_len); - PRINTF("OPTION %u (type %u, len %u, delta %u): ", ((coap_packet_t *)packet)->option_count, COAP_OPTION_URI_QUERY, ((coap_packet_t *)packet)->uri_query_len, COAP_OPTION_URI_QUERY - index); - PRINTF("Uri-Query [%.*s]\n", ((coap_packet_t *)packet)->uri_query_len, ((coap_packet_t *)packet)->uri_query); - index = COAP_OPTION_URI_QUERY; - option += ((coap_packet_t *)packet)->uri_query_len; - ++(((coap_packet_t *)packet)->option_count); - } - - /* pack payload */ - if ((option - ((coap_packet_t *)packet)->buffer)<=COAP_MAX_HEADER_SIZE) - { - memmove(option, ((coap_packet_t *)packet)->payload, ((coap_packet_t *)packet)->payload_len); - } - else - { - /* An error occured. Caller must check for !=0. */ - return 0; - } - - /* set header fields */ - ((coap_packet_t *)packet)->buffer[0] = 0x00; - ((coap_packet_t *)packet)->buffer[0] |= COAP_HEADER_VERSION_MASK & (((coap_packet_t *)packet)->version)<buffer[0] |= COAP_HEADER_TYPE_MASK & (((coap_packet_t *)packet)->type)<buffer[0] |= COAP_HEADER_OPTION_COUNT_MASK & (((coap_packet_t *)packet)->option_count)<buffer[1] = ((coap_packet_t *)packet)->code; - ((coap_packet_t *)packet)->buffer[2] = 0xFF & (((coap_packet_t *)packet)->tid)>>8; - ((coap_packet_t *)packet)->buffer[3] = 0xFF & ((coap_packet_t *)packet)->tid; - - PRINTF("Serialized %u options, header len %u, payload len %u\n", ((coap_packet_t *)packet)->option_count, option - ((coap_packet_t *)packet)->buffer, ((coap_packet_t *)packet)->payload_len); - - return (option - ((coap_packet_t *)packet)->buffer) + ((coap_packet_t *)packet)->payload_len; /* packet length */ -} -/*-----------------------------------------------------------------------------------*/ -error_t -coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) -{ - /* Initialize packet */ - memset(packet, 0, sizeof(coap_packet_t)); - - /* pointer to packet bytes */ - ((coap_packet_t *)packet)->buffer = data; - - /* parse header fields */ - ((coap_packet_t *)packet)->version = (COAP_HEADER_VERSION_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_VERSION_POSITION; - ((coap_packet_t *)packet)->type = (COAP_HEADER_TYPE_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_TYPE_POSITION; - ((coap_packet_t *)packet)->option_count = (COAP_HEADER_OPTION_COUNT_MASK & ((coap_packet_t *)packet)->buffer[0])>>COAP_HEADER_OPTION_COUNT_POSITION; - ((coap_packet_t *)packet)->code = ((coap_packet_t *)packet)->buffer[1]; - ((coap_packet_t *)packet)->tid = ((coap_packet_t *)packet)->buffer[2]<<8 | ((coap_packet_t *)packet)->buffer[3]; - - /* parse options */ - ((coap_packet_t *)packet)->options = 0x0000; - coap_header_option_t *current_option = (coap_header_option_t *) (data + COAP_HEADER_LEN); - - if (((coap_packet_t *)packet)->option_count) { - uint8_t option_index = 0; - uint8_t option_type = 0; - - uint16_t option_len = 0; - uint8_t *option_data = NULL; - - uint8_t *last_option = NULL; - - for (option_index=0; option_index < ((coap_packet_t *)packet)->option_count; ++option_index) { - - option_type += current_option->s.delta; - - if (current_option->s.length<15) { - option_len = current_option->s.length; - option_data = ((uint8_t *) current_option) + 1; - } else { - option_len = current_option->l.length + 15; - option_data = ((uint8_t *) current_option) + 2; - } - - PRINTF("OPTION %u (type %u, len %u, delta %u): ", option_index, option_type, option_len, current_option->s.delta); - - SET_OPTION((coap_packet_t *)packet, option_type); - - switch (option_type) { - case COAP_OPTION_CONTENT_TYPE: - ((coap_packet_t *)packet)->content_type = option_data[0]; - PRINTF("Content-Type [%u]\n", ((coap_packet_t *)packet)->content_type); - break; - case COAP_OPTION_MAX_AGE: - ((coap_packet_t *)packet)->max_age = bytes_2_uint32(option_data, option_len); - PRINTF("Max-Age [%lu]\n", ((coap_packet_t *)packet)->max_age); - break; - case COAP_OPTION_ETAG: - ((coap_packet_t *)packet)->etag_len = MIN(COAP_ETAG_LEN, option_len); - memcpy(((coap_packet_t *)packet)->etag, option_data, ((coap_packet_t *)packet)->etag_len); - PRINTF("ETag %u [0x%02X", ((coap_packet_t *)packet)->etag_len, ((coap_packet_t *)packet)->etag[0]); /*FIXME always prints 4 bytes */ - PRINTF("%02X", ((coap_packet_t *)packet)->etag[1]); - PRINTF("%02X", ((coap_packet_t *)packet)->etag[2]); - PRINTF("%02X", ((coap_packet_t *)packet)->etag[3]); - PRINTF("]\n"); - break; - case COAP_OPTION_URI_HOST: - ((coap_packet_t *)packet)->uri_host = (char *) option_data; - ((coap_packet_t *)packet)->uri_host_len = option_len; - PRINTF("Uri-Auth [%.*s]\n", ((coap_packet_t *)packet)->uri_host_len, ((coap_packet_t *)packet)->uri_host); - break; - case COAP_OPTION_LOCATION_PATH: - ((coap_packet_t *)packet)->location_path = (char *) option_data; - ((coap_packet_t *)packet)->location_path_len = option_len; - PRINTF("Location [%.*s]\n", ((coap_packet_t *)packet)->location_path_len, ((coap_packet_t *)packet)->location_path); - break; - case COAP_OPTION_URI_PATH: - ((coap_packet_t *)packet)->uri_path = (char *) option_data; - ((coap_packet_t *)packet)->uri_path_len = option_len; - PRINTF("Uri-Path [%.*s]\n", ((coap_packet_t *)packet)->uri_path_len, ((coap_packet_t *)packet)->uri_path); - break; - case COAP_OPTION_OBSERVE: - ((coap_packet_t *)packet)->observe = bytes_2_uint32(option_data, option_len); - PRINTF("Observe [%lu]\n", ((coap_packet_t *)packet)->observe); - break; - case COAP_OPTION_TOKEN: - ((coap_packet_t *)packet)->token_len = MIN(COAP_TOKEN_LEN, option_len); - memcpy(((coap_packet_t *)packet)->token, option_data, ((coap_packet_t *)packet)->token_len); - PRINTF("Token %u [0x%02X%02X]\n", ((coap_packet_t *)packet)->token_len, ((coap_packet_t *)packet)->token[0], ((coap_packet_t *)packet)->token[1]); /*FIXME always prints 2 bytes */ - break; - case COAP_OPTION_BLOCK: - ((coap_packet_t *)packet)->block_num = bytes_2_uint32(option_data, option_len); - ((coap_packet_t *)packet)->block_more = (((coap_packet_t *)packet)->block_num & 0x08)>>3; - ((coap_packet_t *)packet)->block_size = 16 << (((coap_packet_t *)packet)->block_num & 0x07); - ((coap_packet_t *)packet)->block_offset = (((coap_packet_t *)packet)->block_num & ~0x0F)<<(((coap_packet_t *)packet)->block_num & 0x07); - ((coap_packet_t *)packet)->block_num >>= 4; - PRINTF("Block [%lu%s (%u B/blk)]\n", ((coap_packet_t *)packet)->block_num, ((coap_packet_t *)packet)->block_more ? "+" : "", ((coap_packet_t *)packet)->block_size); - break; - case COAP_OPTION_NOOP: - PRINTF("Noop-Fencepost\n"); - break; - case COAP_OPTION_URI_QUERY: - ((coap_packet_t *)packet)->uri_query = (char *) option_data; - ((coap_packet_t *)packet)->uri_query_len = option_len; - PRINTF("Uri-Query [%.*s]\n", ((coap_packet_t *)packet)->uri_query_len, ((coap_packet_t *)packet)->uri_query); - break; - default: - PRINTF("unknown (%u)\n", option_type); - if (option_type & 1) - { - return UNKNOWN_CRITICAL_OPTION; - } - } - - /* terminate strings where possible */ - if (last_option) { - last_option[0] = 0x00; - } - - last_option = (uint8_t *) current_option; - current_option = (coap_header_option_t *) (option_data+option_len); - } /* for () */ - } /* if (oc) */ - - ((coap_packet_t *)packet)->payload = (uint8_t *) current_option; - ((coap_packet_t *)packet)->payload_len = data_len - (((coap_packet_t *)packet)->payload - data); - - return NO_ERROR; -} -/*-----------------------------------------------------------------------------------*/ -/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_query_variable(void *packet, const char *name, const char **output) -{ - if (IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) { - return coap_get_variable(((coap_packet_t *)packet)->uri_query, ((coap_packet_t *)packet)->uri_query_len, name, output); - } - return 0; -} - -int -coap_get_post_variable(void *packet, const char *name, const char **output) -{ - if (((coap_packet_t *)packet)->payload_len) { - return coap_get_variable((const char *)((coap_packet_t *)packet)->payload, ((coap_packet_t *)packet)->payload_len, name, output); - } - return 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -unsigned int -coap_get_header_content_type(void *packet) -{ - return ((coap_packet_t *)packet)->content_type; -} - -int -coap_set_header_content_type(void *packet, unsigned int content_type) -{ - ((coap_packet_t *)packet)->content_type = (coap_content_type_t) content_type; - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_CONTENT_TYPE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_max_age(void *packet, uint32_t *age) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE)) { - *age = COAP_DEFAULT_MAX_AGE; - } else { - *age = ((coap_packet_t *)packet)->max_age; - } - return 1; -} - -int -coap_set_header_max_age(void *packet, uint32_t age) -{ - ((coap_packet_t *)packet)->max_age = age; - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_MAX_AGE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_etag(void *packet, const uint8_t **etag) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG)) return 0; - - *etag = ((coap_packet_t *)packet)->etag; - return ((coap_packet_t *)packet)->etag_len; -} - -int -coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) -{ - ((coap_packet_t *)packet)->etag_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(((coap_packet_t *)packet)->etag, etag, ((coap_packet_t *)packet)->etag_len); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_ETAG); - return ((coap_packet_t *)packet)->etag_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_host(void *packet, const char **host) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST)) return 0; - - *host = ((coap_packet_t *)packet)->uri_host; - return ((coap_packet_t *)packet)->uri_host_len; -} - -int -coap_set_header_uri_host(void *packet, const char *host) -{ - ((coap_packet_t *)packet)->uri_host = host; - ((coap_packet_t *)packet)->uri_host_len = strlen(host); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_HOST); - return ((coap_packet_t *)packet)->uri_host_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location(void *packet, const char **location) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH)) return 0; - - *location = ((coap_packet_t *)packet)->location_path; - return ((coap_packet_t *)packet)->location_path_len; -} - -int -coap_set_header_location(void *packet, const char *location) -{ - while (location[0]=='/') ++location; - - ((coap_packet_t *)packet)->location_path = location; - ((coap_packet_t *)packet)->location_path_len = strlen(location); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_LOCATION_PATH); - return ((coap_packet_t *)packet)->location_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_path(void *packet, const char **path) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH)) return 0; - - *path = ((coap_packet_t *)packet)->uri_path; - return ((coap_packet_t *)packet)->uri_path_len; -} - -int -coap_set_header_uri_path(void *packet, const char *path) -{ - while (path[0]=='/') ++path; - - ((coap_packet_t *)packet)->uri_path = path; - ((coap_packet_t *)packet)->uri_path_len = strlen(path); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_PATH); - return ((coap_packet_t *)packet)->uri_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_observe(void *packet, uint32_t *observe) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE)) return 0; - - *observe = ((coap_packet_t *)packet)->observe; - return 1; -} - -int -coap_set_header_observe(void *packet, uint32_t observe) -{ - ((coap_packet_t *)packet)->observe = observe; - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_OBSERVE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_token(void *packet, const uint8_t **token) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN)) return 0; - - *token = ((coap_packet_t *)packet)->token; - return ((coap_packet_t *)packet)->token_len; -} - -int -coap_set_header_token(void *packet, const uint8_t *token, size_t token_len) -{ - ((coap_packet_t *)packet)->token_len = MIN(COAP_TOKEN_LEN, token_len); - memcpy(((coap_packet_t *)packet)->token, token, ((coap_packet_t *)packet)->token_len); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_TOKEN); - return ((coap_packet_t *)packet)->token_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = ((coap_packet_t *)packet)->block_num; - if (more!=NULL) *more = ((coap_packet_t *)packet)->block_more; - if (size!=NULL) *size = ((coap_packet_t *)packet)->block_size; - if (offset!=NULL) *offset = ((coap_packet_t *)packet)->block_offset; - - return 1; -} - -int -coap_set_header_block(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - ((coap_packet_t *)packet)->block_num = num; - ((coap_packet_t *)packet)->block_more = more; - ((coap_packet_t *)packet)->block_size = size; - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_BLOCK); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_query(void *packet, const char **query) -{ - if (!IS_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY)) return 0; - - *query = ((coap_packet_t *)packet)->uri_query; - return ((coap_packet_t *)packet)->uri_query_len; -} - -int -coap_set_header_uri_query(void *packet, const char *query) -{ - while (query[0]=='?') ++query; - - ((coap_packet_t *)packet)->uri_query = query; - ((coap_packet_t *)packet)->uri_query_len = strlen(query); - - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_URI_QUERY); - return ((coap_packet_t *)packet)->uri_query_len; -} -/*-----------------------------------------------------------------------------------*/ -/*- PAYLOAD -------------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_payload(void *packet, const uint8_t **payload) -{ - if (((coap_packet_t *)packet)->payload) { - *payload = ((coap_packet_t *)packet)->payload; - return ((coap_packet_t *)packet)->payload_len; - } else { - *payload = NULL; - return 0; - } -} - -int -coap_set_payload(void *packet, const void *payload, size_t length) -{ - PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE); - - ((coap_packet_t *)packet)->payload = (uint8_t *) payload; - ((coap_packet_t *)packet)->payload_len = MIN(REST_MAX_CHUNK_SIZE, length); - - return ((coap_packet_t *)packet)->payload_len; -} -/*-----------------------------------------------------------------------------------*/ diff --git a/apps/er-coap-03/er-coap-03.h b/apps/er-coap-03/er-coap-03.h deleted file mode 100644 index fb69bd63a..000000000 --- a/apps/er-coap-03/er-coap-03.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 03) - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_03_H_ -#define COAP_03_H_ - -#include /* for size_t */ -#include "contiki-net.h" -#include "erbium.h" - -#define COAP_DEFAULT_PORT 61616 - -#ifndef COAP_SERVER_PORT -#define COAP_SERVER_PORT COAP_DEFAULT_PORT -#endif - -#define COAP_DEFAULT_MAX_AGE 60 -#define COAP_RESPONSE_TIMEOUT 1 -#define COAP_MAX_RETRANSMIT 5 - -#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | tid:0x00FF | tid:0xFF00 | */ -#define COAP_ETAG_LEN 4 /* The maximum number of bytes for the ETag, which is 4 for coap-03 */ -#define COAP_TOKEN_LEN 2 /* The maximum number of bytes for the ETag, which is 4 for coap-03 */ - -#define COAP_HEADER_VERSION_MASK 0xC0 -#define COAP_HEADER_VERSION_POSITION 6 -#define COAP_HEADER_TYPE_MASK 0x30 -#define COAP_HEADER_TYPE_POSITION 4 -#define COAP_HEADER_OPTION_COUNT_MASK 0x0F -#define COAP_HEADER_OPTION_COUNT_POSITION 0 - -#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 -#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F - -/* - * Conservative size limit, as not all options have to be set at the same time. - */ -/* Hdr CoT Age Tag Obs Tok Blo strings */ -#define COAP_MAX_HEADER_SIZE (4 + 2 + 5 + 5 + 5 + 5 + 4 + 0) -#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) -/* 0/14 48 for IPv6 (28 for IPv4) */ -#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN) -#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" -#endif - -/* - * Maximum number of failed request attempts before action - */ -#ifndef COAP_MAX_ATTEMPTS -#define COAP_MAX_ATTEMPTS 4 -#endif /* COAP_MAX_ATTEMPTS */ - -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) - -#define SET_OPTION(packet, opt) ((packet)->options |= 1<options & 1< - */ - -#include -#include -#include -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-07-engine.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#define PRINTBITS(buf,len) { \ - int i,j=0; \ - for (i=0; i=0; --j) { \ - PRINTF("%c", (((char *)buf)[i] & 1<srcipaddr); - PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() ); - PRINTBITS(uip_appdata, uip_datalen()); - PRINTF("\n"); - - coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen()); - - if (coap_error_code==NO_ERROR) - { - - /*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); - PRINTF(" Payload: %.*s\n", message->payload_len, message->payload); - - /* Handle requests. */ - if (message->code >= COAP_GET && message->code <= COAP_DELETE) - { - /* Use transaction buffer for response to confirmable request. */ - if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) ) - { - 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) - { - /* Reliable CON requests are answered with an ACK. */ - coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid); - } - else - { - /* Unreliable NON requests are answered with a NON as well. */ - coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid()); - } - - /* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */ - if (IS_OPTION(message, COAP_OPTION_TOKEN)) - { - coap_set_header_token(response, message->token, message->token_len); - SET_OPTION(response, COAP_OPTION_TOKEN); - } - - /* get offset for blockwise transfers */ - if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset)) - { - PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); - block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); - new_offset = block_offset; - } - - /* Invoke resource handler. */ - if (service_cbk) - { - /* Call REST framework and check if found and allowed. */ - if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset)) - { - if (coap_error_code==NO_ERROR) - { - /* Apply blockwise transfers. */ - if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->codepayload_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 */ - } - 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 - { - /* 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); - - 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 - { - coap_error_code = NOT_IMPLEMENTED_5_01; - coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes - } /* if (service callback) */ - - } else { - coap_error_code = SERVICE_UNAVAILABLE_5_03; - coap_error_message = "NoFreeTraBuffer"; - } /* if (transaction buffer) */ - } - else - { - /* Responses */ - - if (message->type==COAP_TYPE_ACK) - { - PRINTF("Received ACK\n"); - } - else if (message->type==COAP_TYPE_RST) - { - PRINTF("Received RST\n"); - /* Cancel possible subscriptions. */ - coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid); - } - - if ( (transaction = coap_get_transaction_by_mid(message->mid)) ) - { - /* Free transaction memory before callback, as it may create a new transaction. */ - restful_response_handler callback = transaction->callback; - void *callback_data = transaction->callback_data; - coap_clear_transaction(transaction); - - /* Check if someone registered for the response */ - if (callback) { - callback(callback_data, message); - } - } /* if (ACKed transaction) */ - transaction = NULL; - - } /* Request or Response */ - - } /* if (parsed correctly) */ - - 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); - coap_clear_transaction(transaction); - - /* Set to sendable error code. */ - if (coap_error_code >= 192) - { - coap_error_code = INTERNAL_SERVER_ERROR_5_00; - } - /* Reuse input buffer for error message. */ - coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid); - coap_set_payload(message, coap_error_message, strlen(coap_error_message)); - coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata)); - } - } /* if (new data) */ - - return coap_error_code; -} -/*----------------------------------------------------------------------------*/ -void -coap_receiver_init() -{ - process_start(&coap_receiver, NULL); -} -/*----------------------------------------------------------------------------*/ -void -coap_set_service_callback(service_callback_t callback) -{ - service_cbk = callback; -} -/*----------------------------------------------------------------------------*/ -rest_resource_flags_t -coap_get_rest_method(void *packet) -{ - return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1)); -} -/*----------------------------------------------------------------------------*/ -/*- Server part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ - -/* The discover resource is automatically included for CoAP. */ -RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40"); -void -well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - size_t strpos = 0; /* position in overall string (which is larger than the buffer) */ - size_t bufpos = 0; /* position within buffer (bytes written) */ - size_t tmplen = 0; - resource_t* resource = NULL; - - /* For filtering. */ - const char *filter = NULL; - int len = coap_get_query_variable(request, "rt", &filter); - char *rt = NULL; - - for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next) - { - /* Filtering */ - if (len && ((rt=strstr(resource->attributes, "rt=\""))==NULL || memcmp(rt+4, filter, len-1)!=0 || (filter[len-1]!='*' && (filter[len-1]!=rt[3+len] || rt[4+len]!='"')))) - { - continue; - } - - PRINTF("res: /%s (%p)\npos: s%d, o%d, b%d\n", resource->url, resource, strpos, *offset, bufpos); - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '<'; - } - ++strpos; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '/'; - } - ++strpos; - - tmplen = strlen(resource->url); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0)); - /* minimal-net requires these casts */ - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '>'; - } - ++strpos; - - if (resource->attributes[0]) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ';'; - } - ++strpos; - - tmplen = strlen(resource->attributes); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0)); - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - } - - if (resource->next) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ','; - } - ++strpos; - } - - /* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */ - if (bufpos >= preferred_size && strpos-bufpos > *offset) - { - PRINTF("res: BREAK at %s (%p)\n", resource->url, resource); - break; - } - } - - if (bufpos>0) { - PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer); - - coap_set_payload(response, buffer, bufpos ); - coap_set_header_content_type(response, APPLICATION_LINK_FORMAT); - } - else if (strpos>0) - { - PRINTF("well_known_core_handler(): bufpos<=0\n"); - - coap_set_status_code(response, BAD_OPTION_4_02); - coap_set_payload(response, "BlockOutOfScope", 15); - } - - if (resource==NULL) { - PRINTF("res: DONE\n"); - *offset = -1; - } - else - { - PRINTF("res: MORE at %s (%p)\n", resource->url, resource); - *offset += preferred_size; - } -} -/*----------------------------------------------------------------------------*/ -PROCESS_THREAD(coap_receiver, ev, data) -{ - PROCESS_BEGIN(); - PRINTF("Starting CoAP-07 receiver...\n"); - - rest_activate_resource(&resource_well_known_core); - - coap_register_as_transaction_handler(); - coap_init_connection(SERVER_LISTEN_PORT); - PRINTF("Listening on port %u\n", UIP_HTONS(SERVER_LISTEN_PORT)); - - while(1) { - PROCESS_YIELD(); - - if(ev == tcpip_event) { - coap_receive(); - } else if (ev == PROCESS_EVENT_TIMER) { - /* retransmissions are handled here */ - coap_check_transactions(); - } - } /* while (1) */ - - PROCESS_END(); -} -/*----------------------------------------------------------------------------*/ -/*- Client part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -void coap_blocking_request_callback(void *callback_data, void *response) { - struct request_state_t *state = (struct request_state_t *) callback_data; - state->response = (coap_packet_t*) response; - process_poll(state->process); -} -/*----------------------------------------------------------------------------*/ -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)) { - PT_BEGIN(&state->pt); - - static uint8_t more; - static uint32_t res_block; - static uint8_t block_error; - - state->block_num = 0; - state->response = NULL; - state->process = PROCESS_CURRENT(); - - more = 0; - res_block = 0; - block_error = 0; - - do { - request->mid = coap_get_mid(); - if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port))) - { - state->transaction->callback = coap_blocking_request_callback; - state->transaction->callback_data = state; - - if (state->block_num>0) - { - coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE); - } - - state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet); - - coap_send_transaction(state->transaction); - PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid); - - PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); - - if (!state->response) - { - PRINTF("Server not responding\n"); - PT_EXIT(&state->pt); - } - - coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); - - PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len); - - if (res_block==state->block_num) - { - request_callback(state->response); - ++(state->block_num); - } - else - { - PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num); - ++block_error; - } - } - else - { - PRINTF("Could not allocate transaction buffer"); - PT_EXIT(&state->pt); - } - } while (more && block_errorpt); -} -/*----------------------------------------------------------------------------*/ -/*- Engine Interface ---------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -const struct rest_implementation coap_rest_implementation = { - "CoAP-07", - - coap_receiver_init, - coap_set_service_callback, - - coap_get_header_uri_path, - coap_set_header_uri_path, - coap_get_rest_method, - coap_set_status_code, - - coap_get_header_content_type, - coap_set_header_content_type, - coap_get_header_accept, - NULL, - NULL, - coap_get_header_max_age, - coap_set_header_max_age, - coap_set_header_etag, - coap_get_header_if_match, - coap_get_header_if_none_match, - coap_get_header_uri_host, - coap_set_header_location_path, - - coap_get_payload, - coap_set_payload, - - coap_get_header_uri_query, - coap_get_query_variable, - coap_get_post_variable, - - coap_notify_observers, - (restful_post_handler) coap_observe_handler, - - NULL, /* default pre-handler (set separate handler after activation if needed) */ - NULL, /* default post-handler for non-observable resources */ - - { - CONTENT_2_05, - CREATED_2_01, - CHANGED_2_04, - DELETED_2_02, - VALID_2_03, - - BAD_REQUEST_4_00, - UNAUTHORIZED_4_01, - BAD_OPTION_4_02, - FORBIDDEN_4_03, - NOT_FOUND_4_04, - METHOD_NOT_ALLOWED_4_05, - NOT_ACCEPTABLE_4_06, - REQUEST_ENTITY_TOO_LARGE_4_13, - UNSUPPORTED_MEDIA_TYPE_4_15, - - INTERNAL_SERVER_ERROR_5_00, - NOT_IMPLEMENTED_5_01, - BAD_GATEWAY_5_02, - SERVICE_UNAVAILABLE_5_03, - GATEWAY_TIMEOUT_5_04, - PROXYING_NOT_SUPPORTED_5_05 - }, - - { - TEXT_PLAIN, - TEXT_XML, - TEXT_CSV, - TEXT_HTML, - IMAGE_GIF, - IMAGE_JPEG, - IMAGE_PNG, - IMAGE_TIFF, - AUDIO_RAW, - VIDEO_RAW, - APPLICATION_LINK_FORMAT, - APPLICATION_XML, - APPLICATION_OCTET_STREAM, - APPLICATION_RDF_XML, - APPLICATION_SOAP_XML, - APPLICATION_ATOM_XML, - APPLICATION_XMPP_XML, - APPLICATION_EXI, - APPLICATION_FASTINFOSET, - APPLICATION_SOAP_FASTINFOSET, - APPLICATION_JSON, - APPLICATION_X_OBIX_BINARY - } -}; diff --git a/apps/er-coap-07/er-coap-07-engine.h b/apps/er-coap-07/er-coap-07-engine.h deleted file mode 100644 index 9f3f0cbba..000000000 --- a/apps/er-coap-07/er-coap-07-engine.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP implementation of the REST Engine - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_SERVER_H_ -#define COAP_SERVER_H_ - -#if !defined(REST) -#error "Define REST to \"coap_rest_implementation\"" -#endif - -#include "er-coap-07.h" -#include "er-coap-07-transactions.h" -#include "er-coap-07-observing.h" -#include "er-coap-07-separate.h" - -#include "pt.h" - -/* Declare server process */ -PROCESS_NAME(coap_receiver); - -#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT) - -typedef coap_packet_t rest_request_t; -typedef coap_packet_t rest_response_t; - -extern const struct rest_implementation coap_rest_implementation; - -void coap_receiver_init(void); - -/*-----------------------------------------------------------------------------------*/ -/*- Client part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -struct request_state_t { - struct pt pt; - struct process *process; - coap_transaction_t *transaction; - coap_packet_t *response; - uint32_t block_num; -}; - -typedef void (*blocking_response_handler) (void* response); - -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)); - -#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \ -{ \ - static struct request_state_t request_state; \ - PT_SPAWN(process_pt, &request_state.pt, \ - coap_blocking_request(&request_state, ev, \ - server_addr, server_port, \ - request, chunk_handler) \ - ); \ -} -/*-----------------------------------------------------------------------------------*/ - -#endif /* COAP_SERVER_H_ */ diff --git a/apps/er-coap-07/er-coap-07-observing.c b/apps/er-coap-07/er-coap-07-observing.c deleted file mode 100644 index af93492f4..000000000 --- a/apps/er-coap-07/er-coap-07-observing.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#include -#include - -#include "er-coap-07-observing.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS); -LIST(observers_list); - -/*-----------------------------------------------------------------------------------*/ -coap_observer_t * -coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url) -{ - /* Remove existing observe relationship, if any. */ - coap_remove_observer_by_url(addr, port, url); - - coap_observer_t *o = memb_alloc(&observers_memb); - - if (o) - { - o->url = url; - uip_ipaddr_copy(&o->addr, addr); - o->port = port; - o->token_len = token_len; - memcpy(o->token, token, token_len); - o->last_mid = 0; - - stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL); - - PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - list_add(observers_list, o); - } - - return o; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_remove_observer(coap_observer_t *o) -{ - PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - - memb_free(&observers_memb, o); - list_remove(observers_list, o); -} - -int -coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check client "); - PRINT6ADDR(addr); - PRINTF(":%u\n", port); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check URL %p\n", url); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0)) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check MID %u\n", mid); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification) -{ - coap_packet_t *const coap_res = (coap_packet_t *) notification; - coap_observer_t* obs = NULL; - uint8_t preferred_type = coap_res->type; - - PRINTF("Observing: Notification from %s\n", resource->url); - - /* Iterate over observers. */ - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - if (obs->url==resource->url) /* using RESOURCE url pointer as handle */ - { - coap_transaction_t *transaction = NULL; - - /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */ - - if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) ) - { - PRINTF(" Observer "); - PRINT6ADDR(&obs->addr); - PRINTF(":%u\n", obs->port); - - /* Update last MID for RST matching. */ - obs->last_mid = transaction->mid; - - /* Prepare response */ - coap_res->mid = transaction->mid; - coap_set_header_observe(coap_res, obs_counter); - coap_set_header_token(coap_res, obs->token, obs->token_len); - - /* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */ - if (stimer_expired(&obs->refresh_timer)) - { - PRINTF(" Refreshing with CON\n"); - coap_res->type = COAP_TYPE_CON; - stimer_restart(&obs->refresh_timer); - } - else - { - coap_res->type = preferred_type; - } - - transaction->packet_len = coap_serialize_message(coap_res, transaction->packet); - - coap_send_transaction(transaction); - } - } - } -} -/*-----------------------------------------------------------------------------------*/ -void -coap_observe_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; - - static char content[16]; - - if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */ - { - if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) - { - - if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url)) - { - coap_set_header_observe(coap_res, 0); - /* - * For demonstration purposes only. A subscription should return the same representation as a normal GET. - * TODO: Comment the following line for any real application. - */ - coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS)); - } - else - { - coap_res->code = SERVICE_UNAVAILABLE_5_03; - coap_set_payload(coap_res, "TooManyObservers", 16); - } /* if (added observer) */ - } - else /* if (observe) */ - { - /* Remove client if it is currently observing. */ - coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url); - } /* if (observe) */ - } -} diff --git a/apps/er-coap-07/er-coap-07-observing.h b/apps/er-coap-07/er-coap-07-observing.h deleted file mode 100644 index 2fecdaf02..000000000 --- a/apps/er-coap-07/er-coap-07-observing.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_OBSERVING_H_ -#define COAP_OBSERVING_H_ - -#include "sys/stimer.h" -#include "er-coap-07.h" -#include "er-coap-07-transactions.h" - -#ifndef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS 4 -#endif /* COAP_MAX_OBSERVERS */ - -/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */ -#define COAP_OBSERVING_REFRESH_INTERVAL 60 - -#if COAP_MAX_OPEN_TRANSACTIONS - */ - -#include -#include - -#include "er-coap-07-separate.h" -#include "er-coap-07-transactions.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -/*----------------------------------------------------------------------------*/ -void -coap_separate_reject() -{ - coap_error_code = SERVICE_UNAVAILABLE_5_03; - coap_error_message = "AlreadyInUse"; -} -/*----------------------------------------------------------------------------*/ -int -coap_separate_accept(void *request, coap_separate_t *separate_store) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid); - - PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid); - if (t) - { - /* Send separate ACK for CON. */ - if (coap_req->type==COAP_TYPE_CON) - { - 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)); - } - - /* Store remote address. */ - uip_ipaddr_copy(&separate_store->addr, &t->addr); - separate_store->port = t->port; - - /* Store correct response type. */ - separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON; - separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */ - - memcpy(separate_store->token, coap_req->token, coap_req->token_len); - separate_store->token_len = coap_req->token_len; - - separate_store->block2_num = coap_req->block2_num; - separate_store->block2_size = coap_req->block2_size; - - /* Signal the engine to skip automatic response and clear transaction by engine. */ - coap_error_code = MANUAL_RESPONSE; - - return 1; - } - else - { - PRINTF("ERROR: Response transaction for separate request not found!\n"); - return 0; - } -} -/*----------------------------------------------------------------------------*/ -void -coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code) -{ - coap_init_message(response, separate_store->type, code, separate_store->mid); - if (separate_store->token_len) - { - coap_set_header_token(response, separate_store->token, separate_store->token_len); - } -} diff --git a/apps/er-coap-07/er-coap-07-transactions.c b/apps/er-coap-07/er-coap-07-transactions.c deleted file mode 100644 index 2a11002d4..000000000 --- a/apps/er-coap-07/er-coap-07-transactions.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for reliable transport - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-07-transactions.h" -#include "er-coap-07-observing.h" - -/* - * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random - * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR. - */ -#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT) -#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5) - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS); -LIST(transactions_list); - - -static struct process *transaction_handler_process = NULL; - -void -coap_register_as_transaction_handler() -{ - transaction_handler_process = PROCESS_CURRENT(); -} - -coap_transaction_t * -coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port) -{ - coap_transaction_t *t = memb_alloc(&transactions_memb); - - if (t) - { - t->mid = mid; - t->retrans_counter = 0; - - /* 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; -} - -void -coap_send_transaction(coap_transaction_t *t) -{ - PRINTF("Sending transaction %u\n", t->mid); - - coap_send_message(&t->addr, t->port, t->packet, t->packet_len); - - if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION)) - { - if (t->retrans_countermid); - - if (t->retrans_counter==0) - { - t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK); - PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND); - } - else - { - t->retrans_timer.timer.interval <<= 1; /* double */ - PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND); - } - - /*FIXME - * Hack: Setting timer for responsible process. - * Maybe there is a better way, but avoid posting everything to the process. - */ - struct process *process_actual = PROCESS_CURRENT(); - process_current = transaction_handler_process; - etimer_restart(&t->retrans_timer); /* interval updated above */ - process_current = process_actual; - - t = NULL; - } - else - { - /* Timed out. */ - PRINTF("Timeout\n"); - restful_response_handler callback = t->callback; - void *callback_data = t->callback_data; - - /* handle observers */ - coap_remove_observer_by_client(&t->addr, t->port); - - coap_clear_transaction(t); - - if (callback) { - callback(callback_data, NULL); - } - } - } - else - { - coap_clear_transaction(t); - } -} - -void -coap_clear_transaction(coap_transaction_t *t) -{ - if (t) - { - PRINTF("Freeing transaction %u: %p\n", t->mid, t); - - etimer_stop(&t->retrans_timer); - list_remove(transactions_list, t); - memb_free(&transactions_memb, t); - } -} - -coap_transaction_t * -coap_get_transaction_by_mid(uint16_t mid) -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (t->mid==mid) - { - PRINTF("Found transaction for MID %u: %p\n", t->mid, t); - return t; - } - } - return NULL; -} - -void -coap_check_transactions() -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (etimer_expired(&t->retrans_timer)) - { - ++(t->retrans_counter); - PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter); - coap_send_transaction(t); - } - } -} diff --git a/apps/er-coap-07/er-coap-07.c b/apps/er-coap-07/er-coap-07.c deleted file mode 100644 index 4bf995c97..000000000 --- a/apps/er-coap-07/er-coap-07.c +++ /dev/null @@ -1,1205 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 07) - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include "contiki-net.h" -#include -#include - -#include "er-coap-07.h" -#include "er-coap-07-transactions.h" - - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -/*-----------------------------------------------------------------------------------*/ -/*- Variables -----------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static struct uip_udp_conn *udp_conn = NULL; -static uint16_t current_mid = 0; - -coap_status_t coap_error_code = NO_ERROR; -char *coap_error_message = ""; -/*-----------------------------------------------------------------------------------*/ -/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static -uint16_t -coap_log_2(uint16_t value) -{ - uint16_t result = 0; - do { - value = value >> 1; - result++; - } while (value); - - return result ? result - 1 : result; -} -/*-----------------------------------------------------------------------------------*/ -static -uint32_t -coap_parse_int_option(uint8_t *bytes, uint16_t length) -{ - uint32_t var = 0; - int i = 0; - while (i 15) - { - uint8_t delta = COAP_OPTION_FENCE_POST - (*current_number%COAP_OPTION_FENCE_POST); - coap_set_option_header(delta, 0, &buffer[i++]); - *current_number += delta; - - PRINTF("OPTION FENCE POST delta %u\n", delta); - } - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -size_t -coap_serialize_int_option(int number, int current_number, uint8_t *buffer, uint32_t value) -{ - /* Insert fence-posts for large deltas */ - size_t i = coap_insert_option_fence_posts(number, ¤t_number, buffer); - size_t start_i = i; - - uint8_t *option = &buffer[i]; - - if (0xFF000000 & value) buffer[++i] = (uint8_t) (0xFF & value>>24); - if (0xFFFF0000 & value) buffer[++i] = (uint8_t) (0xFF & value>>16); - if (0xFFFFFF00 & value) buffer[++i] = (uint8_t) (0xFF & value>>8); - if (0xFFFFFFFF & value) buffer[++i] = (uint8_t) (0xFF & value); - - i += coap_set_option_header(number - current_number, i-start_i, option); - - PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, i-start_i); - - return i; -} -/*-----------------------------------------------------------------------------------*/ -/* - * Pass the char to split the string at in split_option and receive the number of options in split_option on return. - */ -static -size_t -coap_serialize_array_option(int number, int current_number, uint8_t *buffer, uint8_t *array, size_t length, uint8_t *split_option) -{ - /* Insert fence-posts for large deltas */ - size_t i = coap_insert_option_fence_posts(number, ¤t_number, buffer); - - if (split_option!=NULL) - { - int j; - uint8_t *part_start = array; - uint8_t *part_end = NULL; - size_t temp_length; - - char split_char = *split_option; - *split_option = 0; /* Ensure reflecting the created option count */ - - for (j = 0; j<=length; ++j) - { - if (array[j]==split_char || j==length) - { - part_end = array + j; - temp_length = part_end-part_start; - - i += coap_set_option_header(number - current_number, temp_length, &buffer[i]); - memcpy(&buffer[i], part_start, temp_length); - i += temp_length; - - PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start); - - ++(*split_option); - ++j; /* skip the slash */ - current_number = number; - while( array[j]=='/') ++j; - part_start = array + j; - } - } /* for */ - } - else - { - i += coap_set_option_header(number - current_number, length, &buffer[i]); - memcpy(&buffer[i], array, length); - i += length; - - PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, i); - } - - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -void -coap_merge_multi_option(char **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator) -{ - /* Merge multiple options. */ - if (*dst_len > 0) - { - /* dst already contains an option: concatenate */ - (*dst)[*dst_len] = separator; - *dst_len += 1; - - /* memmove handles 2-byte option headers */ - memmove((*dst)+(*dst_len), option, option_len); - - *dst_len += option_len; - } - else - { - /* dst is empty: set to option */ - *dst = (char *) option; - *dst_len = option_len; - } -} -/*-----------------------------------------------------------------------------------*/ -static -int -coap_get_variable(const char *buffer, size_t length, const char *name, const char **output) -{ - const char *start = NULL; - const char *end = NULL; - const char *value_end = NULL; - size_t name_len = 0; - - /*initialize the output buffer first*/ - *output = 0; - - name_len = strlen(name); - end = buffer + length; - - for (start = buffer; start + name_len < end; ++start){ - if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && - strncmp(name, start, name_len)==0) { - - /* Point start to variable value */ - start += name_len + 1; - - /* Point end to the end of the value */ - value_end = (const char *) memchr(start, '&', end - start); - if (value_end == NULL) { - value_end = end; - } - - *output = start; - - return (value_end - start); - } - } - - return 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE SENDING ----------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_connection(uint16_t port) -{ - /* new connection with remote host */ - udp_conn = udp_new(NULL, 0, NULL); - udp_bind(udp_conn, port); - PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport)); - - /* Initialize transaction ID. */ - current_mid = random_rand(); -} -/*-----------------------------------------------------------------------------------*/ -uint16_t -coap_get_mid() -{ - return ++current_mid; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE PROCESSING -------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Important thing */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - coap_pkt->type = type; - coap_pkt->code = code; - coap_pkt->mid = mid; -} -/*-----------------------------------------------------------------------------------*/ -size_t -coap_serialize_message(void *packet, uint8_t *buffer) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Initialize */ - coap_pkt->buffer = buffer; - coap_pkt->version = 1; - coap_pkt->option_count = 0; - - /* serialize options */ - uint8_t *option = coap_pkt->buffer + COAP_HEADER_LEN; - int current_number = 0; - - PRINTF("-Serializing options-\n"); - - if (IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) { - PRINTF("Content-Type [%u]\n", coap_pkt->content_type); - - option += coap_serialize_int_option(COAP_OPTION_CONTENT_TYPE, current_number, option, coap_pkt->content_type); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_CONTENT_TYPE; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { - PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); - - option += coap_serialize_int_option(COAP_OPTION_MAX_AGE, current_number, option, coap_pkt->max_age); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_MAX_AGE; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) { - PRINTF("Proxy-Uri [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri); - - int length = coap_pkt->proxy_uri_len; - int j = 0; - while (length>0) - { - option += coap_serialize_array_option(COAP_OPTION_PROXY_URI, current_number, option, (uint8_t *) coap_pkt->proxy_uri + j*270, MIN(270, length), NULL); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_PROXY_URI; - - ++j; - length -= 270; - } - } - if (IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) { - PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len, - coap_pkt->etag[0], - coap_pkt->etag[1], - coap_pkt->etag[2], - coap_pkt->etag[3], - coap_pkt->etag[4], - coap_pkt->etag[5], - coap_pkt->etag[6], - coap_pkt->etag[7] - ); /*FIXME always prints 8 bytes */ - - option += coap_serialize_array_option(COAP_OPTION_ETAG, current_number, option, coap_pkt->etag, coap_pkt->etag_len, NULL); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_ETAG; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) { - PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); - - option += coap_serialize_array_option(COAP_OPTION_URI_HOST, current_number, option, (uint8_t *) coap_pkt->uri_host, coap_pkt->uri_host_len, NULL); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_URI_HOST; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) { - PRINTF("Location [%.*s]\n", coap_pkt->location_path_len, coap_pkt->location_path); - - uint8_t split_options = '/'; - - option += coap_serialize_array_option(COAP_OPTION_LOCATION_PATH, current_number, option, (uint8_t *) coap_pkt->location_path, coap_pkt->location_path_len, &split_options); - coap_pkt->option_count += split_options; - current_number = COAP_OPTION_LOCATION_PATH; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PORT)) { - PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); - - option += coap_serialize_int_option(COAP_OPTION_URI_PORT, current_number, option, coap_pkt->uri_port); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_URI_PORT; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) { - PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, coap_pkt->location_query); - - uint8_t split_options = '&'; - - option += coap_serialize_array_option(COAP_OPTION_LOCATION_QUERY, current_number, option, (uint8_t *) coap_pkt->location_query, coap_pkt->location_query_len, &split_options); - coap_pkt->option_count += split_options; - current_number = COAP_OPTION_LOCATION_QUERY; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) { - PRINTF("Uri-Path [%.*s]\n", coap_pkt->uri_path_len, coap_pkt->uri_path); - - uint8_t split_options = '/'; - - option += coap_serialize_array_option(COAP_OPTION_URI_PATH, current_number, option, (uint8_t *) coap_pkt->uri_path, coap_pkt->uri_path_len, &split_options); - coap_pkt->option_count += split_options; - current_number = COAP_OPTION_URI_PATH; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) { - PRINTF("Observe [%u]\n", coap_pkt->observe); - - option += coap_serialize_int_option(COAP_OPTION_OBSERVE, current_number, option, coap_pkt->observe); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_OBSERVE; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) { - PRINTF("Token %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len, - coap_pkt->token[0], - coap_pkt->token[1], - coap_pkt->token[2], - coap_pkt->token[3], - coap_pkt->token[4], - coap_pkt->token[5], - coap_pkt->token[6], - coap_pkt->token[7] - ); /*FIXME always prints 8 bytes */ - - option += coap_serialize_array_option(COAP_OPTION_TOKEN, current_number, option, coap_pkt->token, coap_pkt->token_len, NULL); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_TOKEN; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) { - int i; - for (i=0; iaccept_num; ++i) - { - PRINTF("Accept [%u]\n", coap_pkt->accept[i]); - - option += coap_serialize_int_option(COAP_OPTION_ACCEPT, current_number, option, (uint32_t)coap_pkt->accept[i]); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_ACCEPT; - } - } - if (IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) { - PRINTF("If-Match [FIXME]\n"); - - option += coap_serialize_array_option(COAP_OPTION_IF_MATCH, current_number, option, coap_pkt->if_match, coap_pkt->if_match_len, NULL); - coap_pkt->option_count += 1; - current_number = COAP_OPTION_IF_MATCH; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { - PRINTF("Uri-Query [%.*s]\n", coap_pkt->uri_query_len, coap_pkt->uri_query); - - uint8_t split_options = '&'; - - option += coap_serialize_array_option(COAP_OPTION_URI_QUERY, current_number, option, (uint8_t *) coap_pkt->uri_query, coap_pkt->uri_query_len, &split_options); - coap_pkt->option_count += split_options + (COAP_OPTION_URI_QUERY-current_number)/COAP_OPTION_FENCE_POST; - current_number = COAP_OPTION_URI_QUERY; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) - { - PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); - - uint32_t block = coap_pkt->block2_num << 4; - if (coap_pkt->block2_more) block |= 0x8; - block |= 0xF & coap_log_2(coap_pkt->block2_size/16); - - PRINTF("Block2 encoded: 0x%lX\n", block); - - option += coap_serialize_int_option(COAP_OPTION_BLOCK2, current_number, option, block); - - coap_pkt->option_count += 1 + (COAP_OPTION_BLOCK2-current_number)/COAP_OPTION_FENCE_POST; - current_number = COAP_OPTION_BLOCK2; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) - { - PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); - - uint32_t block = coap_pkt->block1_num << 4; - if (coap_pkt->block1_more) block |= 0x8; - block |= 0xF & coap_log_2(coap_pkt->block1_size/16); - - PRINTF("Block1 encoded: 0x%lX\n", block); - - option += coap_serialize_int_option(COAP_OPTION_BLOCK1, current_number, option, block); - - coap_pkt->option_count += 1 + (COAP_OPTION_BLOCK1-current_number)/COAP_OPTION_FENCE_POST; - current_number = COAP_OPTION_BLOCK1; - } - if (IS_OPTION(coap_pkt, COAP_OPTION_IF_NONE_MATCH)) { - PRINTF("If-None-Match\n"); - - option += coap_serialize_int_option(COAP_OPTION_IF_NONE_MATCH, current_number, option, 0); - - coap_pkt->option_count += 1 + (COAP_OPTION_IF_NONE_MATCH-current_number)/COAP_OPTION_FENCE_POST; - current_number = COAP_OPTION_IF_NONE_MATCH; - } - - /* pack payload */ - if ((option - coap_pkt->buffer)<=COAP_MAX_HEADER_SIZE) - { - memmove(option, coap_pkt->payload, coap_pkt->payload_len); - } - else - { - /* An error occured. Caller must check for !=0. */ - coap_pkt->buffer = NULL; - coap_error_message = "Serialized header exceeds COAP_MAX_HEADER_SIZE"; - return 0; - } - - /* set header fields */ - coap_pkt->buffer[0] = 0x00; - coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<buffer[0] |= COAP_HEADER_OPTION_COUNT_MASK & (coap_pkt->option_count)<buffer[1] = coap_pkt->code; - coap_pkt->buffer[2] = 0xFF & (coap_pkt->mid)>>8; - coap_pkt->buffer[3] = 0xFF & coap_pkt->mid; - - PRINTF("-Done %u options, header len %u, payload len %u-\n", coap_pkt->option_count, option - buffer, coap_pkt->payload_len); - - return (option - buffer) + coap_pkt->payload_len; /* packet length */ -} -/*-----------------------------------------------------------------------------------*/ -void -coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length) -{ - /* Configure connection to reply to client */ - uip_ipaddr_copy(&udp_conn->ripaddr, addr); - udp_conn->rport = port; - - uip_udp_packet_send(udp_conn, data, length); - PRINTF("-sent UDP datagram (%u)-\n", length); - - /* Restore server connection to allow data from any node */ - memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr)); - udp_conn->rport = 0; -} -/*-----------------------------------------------------------------------------------*/ -coap_status_t -coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Initialize packet */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - /* pointer to packet bytes */ - coap_pkt->buffer = data; - - /* parse header fields */ - coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION; - coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION; - coap_pkt->option_count = (COAP_HEADER_OPTION_COUNT_MASK & coap_pkt->buffer[0])>>COAP_HEADER_OPTION_COUNT_POSITION; - coap_pkt->code = coap_pkt->buffer[1]; - coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3]; - - if (coap_pkt->version != 1) - { - coap_error_message = "CoAP version must be 1"; - return BAD_REQUEST_4_00; - } - - /* parse options */ - coap_pkt->options = 0x0000; - uint8_t *current_option = data + COAP_HEADER_LEN; - - if (coap_pkt->option_count) - { - uint8_t option_index = 0; - - uint8_t current_number = 0; - size_t option_len = 0; - - PRINTF("-Parsing %u options-\n", coap_pkt->option_count); - for (option_index=0; option_index < coap_pkt->option_count; ++option_index) { - - current_number += current_option[0]>>4; - - PRINTF("OPTION %u (type %u, delta %u, len %u): ", option_index, current_number, current_option[0]>>4, (0x0F & current_option[0]) < 15 ? (0x0F & current_option[0]) : current_option[1] + 15); - - if ((0x0F & current_option[0]) < 15) { - option_len = 0x0F & current_option[0]; - current_option += 1; - } else { - option_len = current_option[1] + 15; - current_option += 2; - } - - SET_OPTION(coap_pkt, current_number); - - switch (current_number) { - case COAP_OPTION_CONTENT_TYPE: - coap_pkt->content_type = coap_parse_int_option(current_option, option_len); - PRINTF("Content-Type [%u]\n", coap_pkt->content_type); - break; - case COAP_OPTION_MAX_AGE: - coap_pkt->max_age = coap_parse_int_option(current_option, option_len); - PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); - break; - case COAP_OPTION_PROXY_URI: - /*FIXME check for own end-point */ - coap_pkt->proxy_uri = (char *) current_option; - coap_pkt->proxy_uri_len = option_len; - /*TODO length > 270 not implemented (actually not required) */ - PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri); - coap_error_message = "This is a constrained server (Contiki)"; - return PROXYING_NOT_SUPPORTED_5_05; - break; - case COAP_OPTION_ETAG: - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, option_len); - memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len); - PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len, - coap_pkt->etag[0], - coap_pkt->etag[1], - coap_pkt->etag[2], - coap_pkt->etag[3], - coap_pkt->etag[4], - coap_pkt->etag[5], - coap_pkt->etag[6], - coap_pkt->etag[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_URI_HOST: - coap_pkt->uri_host = (char *) current_option; - coap_pkt->uri_host_len = option_len; - PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); - break; - case COAP_OPTION_LOCATION_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_path), &(coap_pkt->location_path_len), current_option, option_len, '/'); - PRINTF("Location-Path [%.*s]\n", coap_pkt->location_path_len, coap_pkt->location_path); - break; - case COAP_OPTION_URI_PORT: - coap_pkt->uri_port = coap_parse_int_option(current_option, option_len); - PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); - break; - case COAP_OPTION_LOCATION_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_len, '&'); - PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, coap_pkt->location_query); - break; - case COAP_OPTION_URI_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_len, '/'); - PRINTF("Uri-Path [%.*s]\n", coap_pkt->uri_path_len, coap_pkt->uri_path); - break; - case COAP_OPTION_OBSERVE: - coap_pkt->observe = coap_parse_int_option(current_option, option_len); - PRINTF("Observe [%u]\n", coap_pkt->observe); - break; - case COAP_OPTION_TOKEN: - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, option_len); - memcpy(coap_pkt->token, current_option, coap_pkt->token_len); - PRINTF("Token %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len, - coap_pkt->token[0], - coap_pkt->token[1], - coap_pkt->token[2], - coap_pkt->token[3], - coap_pkt->token[4], - coap_pkt->token[5], - coap_pkt->token[6], - coap_pkt->token[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_ACCEPT: - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_len); - coap_pkt->accept_num += 1; - PRINTF("Accept [%u]\n", coap_pkt->content_type); - } - break; - case COAP_OPTION_IF_MATCH: - /*FIXME support multiple ETags */ - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, option_len); - memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len); - PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len, - coap_pkt->if_match[0], - coap_pkt->if_match[1], - coap_pkt->if_match[2], - coap_pkt->if_match[3], - coap_pkt->if_match[4], - coap_pkt->if_match[5], - coap_pkt->if_match[6], - coap_pkt->if_match[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_FENCE_POST: - PRINTF("Fence-Post\n"); - break; - case COAP_OPTION_URI_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_len, '&'); - PRINTF("Uri-Query [%.*s]\n", coap_pkt->uri_query_len, coap_pkt->uri_query); - break; - case COAP_OPTION_BLOCK2: - coap_pkt->block2_num = coap_parse_int_option(current_option, option_len); - coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3; - coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07); - coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07); - coap_pkt->block2_num >>= 4; - PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); - break; - case COAP_OPTION_BLOCK1: - coap_pkt->block1_num = coap_parse_int_option(current_option, option_len); - coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3; - coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07); - coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07); - coap_pkt->block1_num >>= 4; - PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); - break; - case COAP_OPTION_IF_NONE_MATCH: - coap_pkt->if_none_match = 1; - PRINTF("If-None-Match\n"); - break; - default: - PRINTF("unknown (%u)\n", current_number); - /* Check if critical (odd) */ - if (current_number & 1) - { - coap_error_message = "Unsupported critical option"; - return BAD_OPTION_4_02; - } - } - - current_option += option_len; - } /* for */ - PRINTF("-Done parsing-------\n"); - } /* if (oc) */ - - coap_pkt->payload = current_option; - coap_pkt->payload_len = data_len - (coap_pkt->payload - data); - - /* also for receiving, the Erbium upper bound is REST_MAX_CHUNK_SIZE */ - if (coap_pkt->payload_len > REST_MAX_CHUNK_SIZE) - { - coap_pkt->payload_len = REST_MAX_CHUNK_SIZE; - } - - /* Null-terminate payload */ - coap_pkt->payload[coap_pkt->payload_len] = '\0'; - - return NO_ERROR; -} -/*-----------------------------------------------------------------------------------*/ -/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_query_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { - return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output); - } - return 0; -} - -int -coap_get_post_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload_len) { - return coap_get_variable((const char *)coap_pkt->payload, coap_pkt->payload_len, name, output); - } - return 0; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_set_status_code(void *packet, unsigned int code) -{ - if (code <= 0xFF) - { - ((coap_packet_t *)packet)->code = (uint8_t) code; - return 1; - } - else - { - return 0; - } -} -/*-----------------------------------------------------------------------------------*/ -/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -unsigned int -coap_get_header_content_type(void *packet) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1; - - return coap_pkt->content_type; -} - -int -coap_set_header_content_type(void *packet, unsigned int content_type) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->content_type = (coap_content_type_t) content_type; - SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_accept(void *packet, const uint16_t **accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0; - - *accept = coap_pkt->accept; - return coap_pkt->accept_num; -} - -int -coap_set_header_accept(void *packet, uint16_t accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = accept; - coap_pkt->accept_num += 1; - - SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT); - } - return coap_pkt->accept_num; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_max_age(void *packet, uint32_t *age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { - *age = COAP_DEFAULT_MAX_AGE; - } else { - *age = coap_pkt->max_age; - } - return 1; -} - -int -coap_set_header_max_age(void *packet, uint32_t age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->max_age = age; - SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_etag(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0; - - *etag = coap_pkt->etag; - return coap_pkt->etag_len; -} - -int -coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->etag, etag, coap_pkt->etag_len); - - SET_OPTION(coap_pkt, COAP_OPTION_ETAG); - return coap_pkt->etag_len; -} -/*-----------------------------------------------------------------------------------*/ -/*FIXME support multiple ETags */ -int -coap_get_header_if_match(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0; - - *etag = coap_pkt->if_match; - return coap_pkt->if_match_len; -} - -int -coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len); - - SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH); - return coap_pkt->if_match_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_if_none_match(void *packet) -{ - return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; -} - -int -coap_set_header_if_none_match(void *packet) -{ - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_token(void *packet, const uint8_t **token) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) return 0; - - *token = coap_pkt->token; - return coap_pkt->token_len; -} - -int -coap_set_header_token(void *packet, const uint8_t *token, size_t token_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len); - memcpy(coap_pkt->token, token, coap_pkt->token_len); - - SET_OPTION(coap_pkt, COAP_OPTION_TOKEN); - return coap_pkt->token_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_proxy_uri(void *packet, const char **uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0; - - *uri = coap_pkt->proxy_uri; - return coap_pkt->proxy_uri_len; -} - -int -coap_set_header_proxy_uri(void *packet, const char *uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->proxy_uri = uri; - coap_pkt->proxy_uri_len = strlen(uri); - - SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI); - return coap_pkt->proxy_uri_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_host(void *packet, const char **host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0; - - *host = coap_pkt->uri_host; - return coap_pkt->uri_host_len; -} - -int -coap_set_header_uri_host(void *packet, const char *host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->uri_host = host; - coap_pkt->uri_host_len = strlen(host); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST); - return coap_pkt->uri_host_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0; - - *path = coap_pkt->uri_path; - return coap_pkt->uri_path_len; -} - -int -coap_set_header_uri_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (path[0]=='/') ++path; - - coap_pkt->uri_path = path; - coap_pkt->uri_path_len = strlen(path); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); - return coap_pkt->uri_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0; - - *query = coap_pkt->uri_query; - return coap_pkt->uri_query_len; -} - -int -coap_set_header_uri_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->uri_query = query; - coap_pkt->uri_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); - return coap_pkt->uri_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0; - - *path = coap_pkt->location_path; - return coap_pkt->location_path_len; -} - -int -coap_set_header_location_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - char *query; - - while (path[0]=='/') ++path; - - if ((query = strchr(path, '?'))) - { - coap_set_header_location_query(packet, query+1); - coap_pkt->location_path_len = query - path; - } - else - { - coap_pkt->location_path_len = strlen(path); - } - - coap_pkt->location_path = path; - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH); - return coap_pkt->location_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0; - - *query = coap_pkt->location_query; - return coap_pkt->location_query_len; -} - -int -coap_set_header_location_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->location_query = query; - coap_pkt->location_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY); - return coap_pkt->location_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_observe(void *packet, uint32_t *observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0; - - *observe = coap_pkt->observe; - return 1; -} - -int -coap_set_header_observe(void *packet, uint32_t observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->observe = observe; - SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block2_num; - if (more!=NULL) *more = coap_pkt->block2_more; - if (size!=NULL) *size = coap_pkt->block2_size; - if (offset!=NULL) *offset = coap_pkt->block2_offset; - - return 1; -} - -int -coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block2_num = num; - coap_pkt->block2_more = more ? 1 : 0; - coap_pkt->block2_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block1_num; - if (more!=NULL) *more = coap_pkt->block1_more; - if (size!=NULL) *size = coap_pkt->block1_size; - if (offset!=NULL) *offset = coap_pkt->block1_offset; - - return 1; -} - -int -coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block1_num = num; - coap_pkt->block1_more = more; - coap_pkt->block1_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -/*- PAYLOAD -------------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_payload(void *packet, const uint8_t **payload) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload) { - *payload = coap_pkt->payload; - return coap_pkt->payload_len; - } else { - *payload = NULL; - return 0; - } -} - -int -coap_set_payload(void *packet, const void *payload, size_t length) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE); - - coap_pkt->payload = (uint8_t *) payload; - coap_pkt->payload_len = MIN(REST_MAX_CHUNK_SIZE, length); - - return coap_pkt->payload_len; -} -/*-----------------------------------------------------------------------------------*/ diff --git a/apps/er-coap-07/er-coap-07.h b/apps/er-coap-07/er-coap-07.h deleted file mode 100644 index 52ad10209..000000000 --- a/apps/er-coap-07/er-coap-07.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 07) - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_07_H_ -#define COAP_07_H_ - -#include /* for size_t */ -#include "contiki-net.h" -#include "erbium.h" - -#define COAP_DEFAULT_PORT 5683 - -#ifndef COAP_SERVER_PORT -#define COAP_SERVER_PORT COAP_DEFAULT_PORT -#endif - -#define COAP_DEFAULT_MAX_AGE 60 -#define COAP_RESPONSE_TIMEOUT 2 -#define COAP_RESPONSE_RANDOM_FACTOR 1.5 -#define COAP_MAX_RETRANSMIT 4 - -#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | mid:0x00FF | mid:0xFF00 | */ -#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ -#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ -#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */ - -#define COAP_HEADER_VERSION_MASK 0xC0 -#define COAP_HEADER_VERSION_POSITION 6 -#define COAP_HEADER_TYPE_MASK 0x30 -#define COAP_HEADER_TYPE_POSITION 4 -#define COAP_HEADER_OPTION_COUNT_MASK 0x0F -#define COAP_HEADER_OPTION_COUNT_POSITION 0 - -#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 -#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F - -/* - * Conservative size limit, as not all options have to be set at the same time. - */ -/* Hdr CoT Age Tag Obs Tok Blo strings */ -#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 10) /* 50 */ -#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) -/* 0/14 48 for IPv6 (28 for IPv4) */ -#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN) -#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" -#endif - -/* - * Maximum number of failed request attempts before action - */ -#ifndef COAP_MAX_ATTEMPTS -#define COAP_MAX_ATTEMPTS 4 -#endif /* COAP_MAX_ATTEMPTS */ - -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) - -#define SET_OPTION(packet, opt) ((packet)->options |= 1L<options & 1L< - */ - -#include -#include -#include -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-12-engine.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#define PRINTBITS(buf,len) { \ - int i,j=0; \ - for (i=0; i=0; --j) { \ - PRINTF("%c", (((char *)buf)[i] & 1<srcipaddr); - PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() ); - PRINTBITS(uip_appdata, uip_datalen()); - PRINTF("\n"); - - coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen()); - - if (coap_error_code==NO_ERROR) - { - - /*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); - PRINTF(" Payload: %.*s\n", message->payload_len, message->payload); - - /* Handle requests. */ - if (message->code >= COAP_GET && message->code <= COAP_DELETE) - { - /* Use transaction buffer for response to confirmable request. */ - if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) ) - { - 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) - { - /* Reliable CON requests are answered with an ACK. */ - coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid); - } - else - { - /* Unreliable NON requests are answered with a NON as well. */ - coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid()); - } - - /* resource handlers must take care of different handling (e.g., TOKEN_OPTION_REQUIRED_240) */ - if (IS_OPTION(message, COAP_OPTION_TOKEN)) - { - coap_set_header_token(response, message->token, message->token_len); - SET_OPTION(response, COAP_OPTION_TOKEN); - } - - /* get offset for blockwise transfers */ - if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset)) - { - PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); - block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); - new_offset = block_offset; - } - - /* Invoke resource handler. */ - if (service_cbk) - { - /* Call REST framework and check if found and allowed. */ - if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset)) - { - if (coap_error_code==NO_ERROR) - { - /* Apply blockwise transfers. */ - if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->codepayload_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 */ - } - 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 - { - /* 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); - - 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 - { - coap_error_code = NOT_IMPLEMENTED_5_01; - coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes - } /* if (service callback) */ - - } else { - coap_error_code = SERVICE_UNAVAILABLE_5_03; - coap_error_message = "NoFreeTraBuffer"; - } /* if (transaction buffer) */ - } - else - { - /* Responses */ - - if (message->type==COAP_TYPE_ACK) - { - PRINTF("Received ACK\n"); - } - else if (message->type==COAP_TYPE_RST) - { - PRINTF("Received RST\n"); - /* Cancel possible subscriptions. */ - coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid); - } - - if ( (transaction = coap_get_transaction_by_mid(message->mid)) ) - { - /* Free transaction memory before callback, as it may create a new transaction. */ - restful_response_handler callback = transaction->callback; - void *callback_data = transaction->callback_data; - coap_clear_transaction(transaction); - - /* Check if someone registered for the response */ - if (callback) { - callback(callback_data, message); - } - } /* if (ACKed transaction) */ - transaction = NULL; - - } /* Request or Response */ - - } /* if (parsed correctly) */ - - 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); - coap_clear_transaction(transaction); - - /* Set to sendable error code. */ - if (coap_error_code >= 192) - { - coap_error_code = INTERNAL_SERVER_ERROR_5_00; - } - /* Reuse input buffer for error message. */ - coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid); - coap_set_payload(message, coap_error_message, strlen(coap_error_message)); - coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata)); - } - } /* if (new data) */ - - return coap_error_code; -} -/*----------------------------------------------------------------------------*/ -void -coap_receiver_init() -{ - process_start(&coap_receiver, NULL); -} -/*----------------------------------------------------------------------------*/ -void -coap_set_service_callback(service_callback_t callback) -{ - service_cbk = callback; -} -/*----------------------------------------------------------------------------*/ -rest_resource_flags_t -coap_get_rest_method(void *packet) -{ - return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1)); -} -/*----------------------------------------------------------------------------*/ -/*- Server part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ - -/* The discover resource is automatically included for CoAP. */ -RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40"); -void -well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - size_t strpos = 0; /* position in overall string (which is larger than the buffer) */ - size_t bufpos = 0; /* position within buffer (bytes written) */ - size_t tmplen = 0; - resource_t* resource = NULL; - -#if COAP_LINK_FORMAT_FILTERING - /* For filtering. */ - const char *filter = NULL; - const char *attrib = NULL; - const char *found = NULL; - const char *end = NULL; - char *value = NULL; - char lastchar; - int len = coap_get_header_uri_query(request, &filter); - if (len) - { - value = strchr(filter, '='); - value[0] = '\0'; - ++value; - len -= strlen(filter)+1; - - PRINTF("Filter %s = %.*s\n", filter, len, value); - - if (strcmp(filter,"href")==0 && value[0]=='/') - { - ++value; - --len; - } - - lastchar = value[len-1]; - value[len-1] = '\0'; - } -#endif - - for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next) - { -#if COAP_LINK_FORMAT_FILTERING - /* Filtering */ - if (len) - { - if (strcmp(filter,"href")==0) - { - attrib=strstr(resource->url, value); - if (attrib==NULL || (value[-1]=='/' && attrib!=resource->url)) continue; - end = attrib + strlen(attrib); - } - else - { - attrib=strstr(resource->attributes, filter); - if (attrib==NULL || (attrib[strlen(filter)]!='=' && attrib[strlen(filter)]!='"')) continue; - attrib += strlen(filter)+2; - end = strchr(attrib, '"'); - } - - PRINTF("Filter: res has attrib %s (%s)\n", attrib, value); - found = attrib; - while ((found=strstr(found, value))!=NULL) { - if (found > end) - { - found = NULL; - break; - } - if (lastchar==found[len-1] || lastchar=='*') - { - break; - } - ++found; - } - if (found==NULL) - { - continue; - } - PRINTF("Filter: res has prefix %s\n", found); - if (lastchar!='*' && (found[len]!='"' && found[len]!=' ' && found[len]!='\0')) continue; - PRINTF("Filter: res has match\n"); - } -#endif - - PRINTF("res: /%s (%p)\npos: s%d, o%d, b%d\n", resource->url, resource, strpos, *offset, bufpos); - - if (strpos>0) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ','; - } - ++strpos; - } - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '<'; - } - ++strpos; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '/'; - } - ++strpos; - - tmplen = strlen(resource->url); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0)); - /* minimal-net requires these casts */ - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '>'; - } - ++strpos; - - if (resource->attributes[0]) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ';'; - } - ++strpos; - - tmplen = strlen(resource->attributes); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0)); - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - } - - /* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */ - if (bufpos >= preferred_size && strpos-bufpos > *offset) - { - PRINTF("res: BREAK at %s (%p)\n", resource->url, resource); - break; - } - } - - if (bufpos>0) { - PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer); - - coap_set_payload(response, buffer, bufpos ); - coap_set_header_content_type(response, APPLICATION_LINK_FORMAT); - } - else if (strpos>0) - { - PRINTF("well_known_core_handler(): bufpos<=0\n"); - - coap_set_status_code(response, BAD_OPTION_4_02); - coap_set_payload(response, "BlockOutOfScope", 15); - } - - if (resource==NULL) { - PRINTF("res: DONE\n"); - *offset = -1; - } - else - { - PRINTF("res: MORE at %s (%p)\n", resource->url, resource); - *offset += preferred_size; - } -} -/*----------------------------------------------------------------------------*/ -PROCESS_THREAD(coap_receiver, ev, data) -{ - PROCESS_BEGIN(); - PRINTF("Starting CoAP-12 receiver...\n"); - - rest_activate_resource(&resource_well_known_core); - - coap_register_as_transaction_handler(); - coap_init_connection(SERVER_LISTEN_PORT); - - while(1) { - PROCESS_YIELD(); - - if(ev == tcpip_event) { - coap_receive(); - } else if (ev == PROCESS_EVENT_TIMER) { - /* retransmissions are handled here */ - coap_check_transactions(); - } - } /* while (1) */ - - PROCESS_END(); -} -/*----------------------------------------------------------------------------*/ -/*- Client part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -void coap_blocking_request_callback(void *callback_data, void *response) { - struct request_state_t *state = (struct request_state_t *) callback_data; - state->response = (coap_packet_t*) response; - process_poll(state->process); -} -/*----------------------------------------------------------------------------*/ -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)) { - PT_BEGIN(&state->pt); - - static uint8_t more; - static uint32_t res_block; - static uint8_t block_error; - - state->block_num = 0; - state->response = NULL; - state->process = PROCESS_CURRENT(); - - more = 0; - res_block = 0; - block_error = 0; - - do { - request->mid = coap_get_mid(); - if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port))) - { - state->transaction->callback = coap_blocking_request_callback; - state->transaction->callback_data = state; - - if (state->block_num>0) - { - coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE); - } - - state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet); - - coap_send_transaction(state->transaction); - PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid); - - PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); - - if (!state->response) - { - PRINTF("Server not responding\n"); - PT_EXIT(&state->pt); - } - - coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); - - PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len); - - if (res_block==state->block_num) - { - request_callback(state->response); - ++(state->block_num); - } - else - { - PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num); - ++block_error; - } - } - else - { - PRINTF("Could not allocate transaction buffer"); - PT_EXIT(&state->pt); - } - } while (more && block_errorpt); -} -/*----------------------------------------------------------------------------*/ -/*- Engine Interface ---------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -const struct rest_implementation coap_rest_implementation = { - "CoAP-12", - - coap_receiver_init, - coap_set_service_callback, - - coap_get_header_uri_path, - coap_set_header_uri_path, - coap_get_rest_method, - coap_set_status_code, - - coap_get_header_content_type, - coap_set_header_content_type, - coap_get_header_accept, - coap_get_header_size, - coap_set_header_size, - coap_get_header_max_age, - coap_set_header_max_age, - coap_set_header_etag, - coap_get_header_if_match, - coap_get_header_if_none_match, - coap_get_header_uri_host, - coap_set_header_location_path, - - coap_get_payload, - coap_set_payload, - - coap_get_header_uri_query, - coap_get_query_variable, - coap_get_post_variable, - - coap_notify_observers, - (restful_post_handler) coap_observe_handler, - - NULL, /* default pre-handler (set separate handler after activation if needed) */ - NULL, /* default post-handler for non-observable resources */ - - { - CONTENT_2_05, - CREATED_2_01, - CHANGED_2_04, - DELETED_2_02, - VALID_2_03, - BAD_REQUEST_4_00, - UNAUTHORIZED_4_01, - BAD_OPTION_4_02, - FORBIDDEN_4_03, - NOT_FOUND_4_04, - METHOD_NOT_ALLOWED_4_05, - NOT_ACCEPTABLE_4_06, - REQUEST_ENTITY_TOO_LARGE_4_13, - UNSUPPORTED_MEDIA_TYPE_4_15, - INTERNAL_SERVER_ERROR_5_00, - NOT_IMPLEMENTED_5_01, - BAD_GATEWAY_5_02, - SERVICE_UNAVAILABLE_5_03, - GATEWAY_TIMEOUT_5_04, - PROXYING_NOT_SUPPORTED_5_05 - }, - - { - TEXT_PLAIN, - TEXT_XML, - TEXT_CSV, - TEXT_HTML, - IMAGE_GIF, - IMAGE_JPEG, - IMAGE_PNG, - IMAGE_TIFF, - AUDIO_RAW, - VIDEO_RAW, - APPLICATION_LINK_FORMAT, - APPLICATION_XML, - APPLICATION_OCTET_STREAM, - APPLICATION_RDF_XML, - APPLICATION_SOAP_XML, - APPLICATION_ATOM_XML, - APPLICATION_XMPP_XML, - APPLICATION_EXI, - APPLICATION_FASTINFOSET, - APPLICATION_SOAP_FASTINFOSET, - APPLICATION_JSON, - APPLICATION_X_OBIX_BINARY - } -}; diff --git a/apps/er-coap-12/er-coap-12-engine.h b/apps/er-coap-12/er-coap-12-engine.h deleted file mode 100644 index d3e5c8bad..000000000 --- a/apps/er-coap-12/er-coap-12-engine.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP implementation of the REST Engine - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_SERVER_H_ -#define COAP_SERVER_H_ - -#if !defined(REST) -#error "Define REST to \"coap_rest_implementation\"" -#endif - -#include "er-coap-12.h" -#include "er-coap-12-transactions.h" -#include "er-coap-12-observing.h" -#include "er-coap-12-separate.h" - -#include "pt.h" - -/* Declare server process */ -PROCESS_NAME(coap_receiver); - -#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT) - -typedef coap_packet_t rest_request_t; -typedef coap_packet_t rest_response_t; - -extern const struct rest_implementation coap_rest_implementation; - -void coap_receiver_init(void); - -/*-----------------------------------------------------------------------------------*/ -/*- Client part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -struct request_state_t { - struct pt pt; - struct process *process; - coap_transaction_t *transaction; - coap_packet_t *response; - uint32_t block_num; -}; - -typedef void (*blocking_response_handler) (void* response); - -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)); - -#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \ -{ \ - static struct request_state_t request_state; \ - PT_SPAWN(process_pt, &request_state.pt, \ - coap_blocking_request(&request_state, ev, \ - server_addr, server_port, \ - request, chunk_handler) \ - ); \ -} -/*-----------------------------------------------------------------------------------*/ - -#endif /* COAP_SERVER_H_ */ diff --git a/apps/er-coap-12/er-coap-12-observing.c b/apps/er-coap-12/er-coap-12-observing.c deleted file mode 100644 index 323a90fe3..000000000 --- a/apps/er-coap-12/er-coap-12-observing.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#include -#include - -#include "er-coap-12-observing.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS); -LIST(observers_list); - -/*-----------------------------------------------------------------------------------*/ -coap_observer_t * -coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url) -{ - /* Remove existing observe relationship, if any. */ - coap_remove_observer_by_url(addr, port, url); - - coap_observer_t *o = memb_alloc(&observers_memb); - - if (o) - { - o->url = url; - uip_ipaddr_copy(&o->addr, addr); - o->port = port; - o->token_len = token_len; - memcpy(o->token, token, token_len); - o->last_mid = 0; - - stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL); - - PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - list_add(observers_list, o); - } - - return o; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_remove_observer(coap_observer_t *o) -{ - PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - - memb_free(&observers_memb, o); - list_remove(observers_list, o); -} - -int -coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check client "); - PRINT6ADDR(addr); - PRINTF(":%u\n", port); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check URL %p\n", url); - if ((addr==NULL || (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)) && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0)) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check MID %u\n", mid); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification) -{ - coap_packet_t *const coap_res = (coap_packet_t *) notification; - coap_observer_t* obs = NULL; - uint8_t preferred_type = coap_res->type; - - PRINTF("Observing: Notification from %s\n", resource->url); - - /* Iterate over observers. */ - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - if (obs->url==resource->url) /* using RESOURCE url pointer as handle */ - { - coap_transaction_t *transaction = NULL; - - /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */ - - if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) ) - { - PRINTF(" Observer "); - PRINT6ADDR(&obs->addr); - PRINTF(":%u\n", obs->port); - - /* Update last MID for RST matching. */ - obs->last_mid = transaction->mid; - - /* Prepare response */ - coap_res->mid = transaction->mid; - if (obs_counter>=0) coap_set_header_observe(coap_res, obs_counter); - coap_set_header_token(coap_res, obs->token, obs->token_len); - - /* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */ - if (stimer_expired(&obs->refresh_timer)) - { - PRINTF(" Refreshing with CON\n"); - coap_res->type = COAP_TYPE_CON; - stimer_restart(&obs->refresh_timer); - } - else - { - coap_res->type = preferred_type; - } - - transaction->packet_len = coap_serialize_message(coap_res, transaction->packet); - - coap_send_transaction(transaction); - } - } - } -} -/*-----------------------------------------------------------------------------------*/ -void -coap_observe_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; - - static char content[16]; - - if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */ - { - if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) - { - - if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url)) - { - coap_set_header_observe(coap_res, 0); - /* - * For demonstration purposes only. A subscription should return the same representation as a normal GET. - * TODO: Comment the following line for any real application. - */ - coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS)); - } - else - { - coap_res->code = SERVICE_UNAVAILABLE_5_03; - coap_set_payload(coap_res, "TooManyObservers", 16); - } /* if (added observer) */ - } - else /* if (observe) */ - { - /* Remove client if it is currently observing. */ - coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url); - } /* if (observe) */ - } -} diff --git a/apps/er-coap-12/er-coap-12-observing.h b/apps/er-coap-12/er-coap-12-observing.h deleted file mode 100644 index 78aa64d05..000000000 --- a/apps/er-coap-12/er-coap-12-observing.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_OBSERVING_H_ -#define COAP_OBSERVING_H_ - -#include "sys/stimer.h" -#include "er-coap-12.h" -#include "er-coap-12-transactions.h" - -#ifndef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS-1 -#endif /* COAP_MAX_OBSERVERS */ - -/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */ -#define COAP_OBSERVING_REFRESH_INTERVAL 60 - -#if COAP_MAX_OPEN_TRANSACTIONS - */ - -#include -#include - -#include "er-coap-12-separate.h" -#include "er-coap-12-transactions.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -/*----------------------------------------------------------------------------*/ -void -coap_separate_reject() -{ - coap_error_code = SERVICE_UNAVAILABLE_5_03; - coap_error_message = "AlreadyInUse"; -} -/*----------------------------------------------------------------------------*/ -int -coap_separate_accept(void *request, coap_separate_t *separate_store) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid); - - PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid); - if (t) - { - /* Send separate ACK for CON. */ - if (coap_req->type==COAP_TYPE_CON) - { - 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)); - } - - /* Store remote address. */ - uip_ipaddr_copy(&separate_store->addr, &t->addr); - separate_store->port = t->port; - - /* Store correct response type. */ - separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON; - separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */ - - memcpy(separate_store->token, coap_req->token, coap_req->token_len); - separate_store->token_len = coap_req->token_len; - - separate_store->block2_num = coap_req->block2_num; - separate_store->block2_size = coap_req->block2_size; - - /* Signal the engine to skip automatic response and clear transaction by engine. */ - coap_error_code = MANUAL_RESPONSE; - - return 1; - } - else - { - PRINTF("ERROR: Response transaction for separate request not found!\n"); - return 0; - } -} -/*----------------------------------------------------------------------------*/ -void -coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code) -{ - coap_init_message(response, separate_store->type, code, separate_store->mid); - if (separate_store->token_len) - { - coap_set_header_token(response, separate_store->token, separate_store->token_len); - } -} diff --git a/apps/er-coap-12/er-coap-12-transactions.c b/apps/er-coap-12/er-coap-12-transactions.c deleted file mode 100644 index 3cd496e65..000000000 --- a/apps/er-coap-12/er-coap-12-transactions.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for reliable transport - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-12-transactions.h" -#include "er-coap-12-observing.h" - -/* - * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random - * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR. - */ -#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT) -#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5) - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS); -LIST(transactions_list); - - -static struct process *transaction_handler_process = NULL; - -void -coap_register_as_transaction_handler() -{ - transaction_handler_process = PROCESS_CURRENT(); -} - -coap_transaction_t * -coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port) -{ - coap_transaction_t *t = memb_alloc(&transactions_memb); - - if (t) - { - t->mid = mid; - t->retrans_counter = 0; - - /* 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; -} - -void -coap_send_transaction(coap_transaction_t *t) -{ - PRINTF("Sending transaction %u\n", t->mid); - - coap_send_message(&t->addr, t->port, t->packet, t->packet_len); - - if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION)) - { - if (t->retrans_countermid); - - if (t->retrans_counter==0) - { - t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK); - PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND); - } - else - { - t->retrans_timer.timer.interval <<= 1; /* double */ - PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND); - } - - /*FIXME - * Hack: Setting timer for responsible process. - * Maybe there is a better way, but avoid posting everything to the process. - */ - struct process *process_actual = PROCESS_CURRENT(); - process_current = transaction_handler_process; - etimer_restart(&t->retrans_timer); /* interval updated above */ - process_current = process_actual; - - t = NULL; - } - else - { - /* Timed out. */ - PRINTF("Timeout\n"); - restful_response_handler callback = t->callback; - void *callback_data = t->callback_data; - - /* handle observers */ - coap_remove_observer_by_client(&t->addr, t->port); - - coap_clear_transaction(t); - - if (callback) { - callback(callback_data, NULL); - } - } - } - else - { - coap_clear_transaction(t); - } -} - -void -coap_clear_transaction(coap_transaction_t *t) -{ - if (t) - { - PRINTF("Freeing transaction %u: %p\n", t->mid, t); - - etimer_stop(&t->retrans_timer); - list_remove(transactions_list, t); - memb_free(&transactions_memb, t); - } -} - -coap_transaction_t * -coap_get_transaction_by_mid(uint16_t mid) -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (t->mid==mid) - { - PRINTF("Found transaction for MID %u: %p\n", t->mid, t); - return t; - } - } - return NULL; -} - -void -coap_check_transactions() -{ - coap_transaction_t *t = NULL; - - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (etimer_expired(&t->retrans_timer)) - { - ++(t->retrans_counter); - PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter); - coap_send_transaction(t); - } - } -} diff --git a/apps/er-coap-12/er-coap-12.c b/apps/er-coap-12/er-coap-12.c deleted file mode 100644 index 0e68bfa34..000000000 --- a/apps/er-coap-12/er-coap-12.c +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 12) - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include "contiki-net.h" -#include -#include - -#include "er-coap-12.h" -#include "er-coap-12-transactions.h" - - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -/*-----------------------------------------------------------------------------------*/ -/*- Variables -----------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static struct uip_udp_conn *udp_conn = NULL; -static uint16_t current_mid = 0; - -coap_status_t coap_error_code = NO_ERROR; -char *coap_error_message = ""; -/*-----------------------------------------------------------------------------------*/ -/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static -uint16_t -coap_log_2(uint16_t value) -{ - uint16_t result = 0; - do { - value = value >> 1; - result++; - } while (value); - - return result ? result - 1 : result; -} -/*-----------------------------------------------------------------------------------*/ -static -uint32_t -coap_parse_int_option(uint8_t *bytes, uint16_t length) -{ - uint32_t var = 0; - int i = 0; - while (i 254) - { - PRINTF("+255"); - buffer[++size] = 0xFF; - length -= 255; - } - PRINTF("+%u\n", length); - buffer[++size] = 0xFF & length; - return ++size; - } -} -/*-----------------------------------------------------------------------------------*/ -static -size_t -coap_insert_option_jump(unsigned int number, unsigned int *current_number, uint8_t *buffer) -{ - unsigned int delta = number-*current_number; - if (delta < 15) - { - return 0; - } - else if (delta < 30) - { - buffer[0] = 0xF1; - *current_number += 15; - - PRINTF("OPTION JUMP 1: 15\n"); - return 1; - } - else if (delta < 2064) - { - buffer[0] = 0xF2; - buffer[1] = ((delta/8)-2); - *current_number += buffer[1]; - - PRINTF("OPTION JUMP 2: %u\n", buffer[1]); - return 2; - } - else /* no upper bound check as delta is 16 bit */ - { - buffer[0] = 0xF3; - delta = ((delta/8)-258); - buffer[1] = delta>>8; - buffer[2] = 0xFF & delta; - *current_number += delta; - - PRINTF("OPTION JUMP 3: %u\n", delta); - return 3; - } -} -/*-----------------------------------------------------------------------------------*/ -static -size_t -coap_serialize_int_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint32_t value) -{ - /* Insert jumps for large deltas */ - size_t i = coap_insert_option_jump(number, ¤t_number, buffer); - size_t start_i = i; - - uint8_t *option = &buffer[i]; - - if (0xFF000000 & value) buffer[++i] = (uint8_t) (0xFF & value>>24); - if (0xFFFF0000 & value) buffer[++i] = (uint8_t) (0xFF & value>>16); - if (0xFFFFFF00 & value) buffer[++i] = (uint8_t) (0xFF & value>>8); - if (0xFFFFFFFF & value) buffer[++i] = (uint8_t) (0xFF & value); - - i += coap_set_option_header(number - current_number, i-start_i, option); - - PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, i-start_i); - - return i; -} -/*-----------------------------------------------------------------------------------*/ -/* - * Pass the char to split the string at in split_option and receive the number of options in split_option on return. - */ -static -size_t -coap_serialize_array_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint8_t *array, size_t length, uint8_t *split_option) -{ - /* Insert jumps for large deltas */ - size_t i = coap_insert_option_jump(number, ¤t_number, buffer); - - if (*split_option!='\0') - { - int j; - uint8_t *part_start = array; - uint8_t *part_end = NULL; - size_t temp_length; - - char split_char = *split_option; - *split_option = 0; /* Ensure reflecting the created option count */ - - for (j = 0; j<=length; ++j) - { - if (array[j]==split_char || j==length) - { - part_end = array + j; - temp_length = part_end-part_start; - - i += coap_set_option_header(number - current_number, temp_length, &buffer[i]); - memcpy(&buffer[i], part_start, temp_length); - i += temp_length; - - PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start); - - ++(*split_option); - ++j; /* skip the slash */ - current_number = number; - while( array[j]=='/') ++j; - part_start = array + j; - } - } /* for */ - } - else - { - i += coap_set_option_header(number - current_number, length, &buffer[i]); - memcpy(&buffer[i], array, length); - i += length; - - *split_option = 1; - - PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, length); - } - - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -void -coap_merge_multi_option(char **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator) -{ - /* Merge multiple options. */ - if (*dst_len > 0) - { - /* dst already contains an option: concatenate */ - (*dst)[*dst_len] = separator; - *dst_len += 1; - - /* memmove handles 2-byte option headers */ - memmove((*dst)+(*dst_len), option, option_len); - - *dst_len += option_len; - } - else - { - /* dst is empty: set to option */ - *dst = (char *) option; - *dst_len = option_len; - } -} -/*-----------------------------------------------------------------------------------*/ -static -int -coap_get_variable(const char *buffer, size_t length, const char *name, const char **output) -{ - const char *start = NULL; - const char *end = NULL; - const char *value_end = NULL; - size_t name_len = 0; - - /*initialize the output buffer first*/ - *output = 0; - - name_len = strlen(name); - end = buffer + length; - - for (start = buffer; start + name_len < end; ++start){ - if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && - strncmp(name, start, name_len)==0) { - - /* Point start to variable value */ - start += name_len + 1; - - /* Point end to the end of the value */ - value_end = (const char *) memchr(start, '&', end - start); - if (value_end == NULL) { - value_end = end; - } - - *output = start; - - return (value_end - start); - } - } - - return 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE SENDING ----------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_connection(uint16_t port) -{ - /* new connection with remote host */ - udp_conn = udp_new(NULL, 0, NULL); - udp_bind(udp_conn, port); - PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport)); - - /* Initialize transaction ID. */ - current_mid = random_rand(); -} -/*-----------------------------------------------------------------------------------*/ -uint16_t -coap_get_mid() -{ - return ++current_mid; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE PROCESSING -------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Important thing */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - coap_pkt->type = type; - coap_pkt->code = code; - coap_pkt->mid = mid; -} -/*-----------------------------------------------------------------------------------*/ -size_t -coap_serialize_message(void *packet, uint8_t *buffer) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Initialize */ - coap_pkt->buffer = buffer; - coap_pkt->version = 1; - coap_pkt->option_count = 0; - - /* Serialize options */ - uint8_t *option = coap_pkt->buffer + COAP_HEADER_LEN; - unsigned int current_number = 0; - - PRINTF("-Serializing options-\n"); - - /* The options must be serialized in the order of their number */ - COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_IF_MATCH, if_match, "If-Match") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_HOST, uri_host, '\0', "Uri-Host") - COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_ETAG, etag, "ETag") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_IF_NONE_MATCH, content_type-coap_pkt->content_type, "If-None-Match") /* hack to get a zero field */ - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_OBSERVE, observe, "Observe") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_URI_PORT, uri_port, "Uri-Port") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_PATH, location_path, '/', "Location-Path") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_PATH, uri_path, '/', "Uri-Path") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_CONTENT_TYPE, content_type, "Content-Format") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_MAX_AGE, max_age, "Max-Age") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_QUERY, uri_query, '&', "Uri-Query") - COAP_SERIALIZE_ACCEPT_OPTION( COAP_OPTION_ACCEPT, accept, "Accept") - COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_TOKEN, token, "Token") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_QUERY, location_query, '&', "Location-Query") - COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK2, block2, "Block2") - COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK1, block1, "Block1") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_SIZE, size, "Size") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_PROXY_URI, proxy_uri, '\0', "Proxy-Uri") - - /* Option terminator 0xF0 for 15 and more options */ - if (coap_pkt->option_count>14) - { - coap_pkt->option_count = 15; - *option = 0xF0; - ++option; - } - - /* pack payload */ - if ((option - coap_pkt->buffer)<=COAP_MAX_HEADER_SIZE) - { - memmove(option, coap_pkt->payload, coap_pkt->payload_len); - } - else - { - /* An error occured. Caller must check for !=0. */ - coap_pkt->buffer = NULL; - coap_error_message = "Serialized header exceeds COAP_MAX_HEADER_SIZE"; - return 0; - } - - /* set header fields */ - coap_pkt->buffer[0] = 0x00; - coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<buffer[0] |= COAP_HEADER_OPTION_COUNT_MASK & (coap_pkt->option_count)<buffer[1] = coap_pkt->code; - coap_pkt->buffer[2] = 0xFF & (coap_pkt->mid)>>8; - coap_pkt->buffer[3] = 0xFF & coap_pkt->mid; - - PRINTF("-Done %u options, header len %u, payload len %u-\n", coap_pkt->option_count, option - buffer, coap_pkt->payload_len); - - return (option - buffer) + coap_pkt->payload_len; /* packet length */ -} -/*-----------------------------------------------------------------------------------*/ -void -coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length) -{ - /* Configure connection to reply to client */ - uip_ipaddr_copy(&udp_conn->ripaddr, addr); - udp_conn->rport = port; - - uip_udp_packet_send(udp_conn, data, length); - PRINTF("-sent UDP datagram (%u)-\n", length); - - /* Restore server connection to allow data from any node */ - memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr)); - udp_conn->rport = 0; -} -/*-----------------------------------------------------------------------------------*/ -coap_status_t -coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Initialize packet */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - /* pointer to packet bytes */ - coap_pkt->buffer = data; - - /* parse header fields */ - coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION; - coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION; - coap_pkt->option_count = (COAP_HEADER_OPTION_COUNT_MASK & coap_pkt->buffer[0])>>COAP_HEADER_OPTION_COUNT_POSITION; - coap_pkt->code = coap_pkt->buffer[1]; - coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3]; - - if (coap_pkt->version != 1) - { - coap_error_message = "CoAP version must be 1"; - return BAD_REQUEST_4_00; - } - - /* parse options */ - memset(coap_pkt->options, 0, sizeof(coap_pkt->options)); - uint8_t *current_option = data + COAP_HEADER_LEN; - - if (coap_pkt->option_count) - { - int option_index = 0; - - unsigned int current_number = 0; - size_t option_len = 0; - - PRINTF("-Parsing %u options-\n", coap_pkt->option_count); - for (option_index=0; option_index < coap_pkt->option_count || coap_pkt->option_count==15; ++option_index) { - - /* Option terminator 0xF0 */ - if (current_option[0]==0xF0) - { - break; - } - /* Option jumps */ - else if (current_option[0]==0xF1) - { - PRINTF("JUMP 1: 15\n"); - current_number += 15; - ++current_option; - } - else if (current_option[0]==0xF2) - { - PRINTF("JUMP 2: %u\n", (current_option[1]+2) * 8); - current_number += (current_option[1]+2) * 8; - current_option += 2; - } - else if (current_option[0]==0xF3) - { - unsigned int jump = (current_option[1]<<8) | current_option[2]; - PRINTF("JUMP 3: %u\n", (jump+258) * 8); - current_number += (jump+258) * 8; - current_option +=3; - } - - current_number += current_option[0]>>4; - - PRINTF("OPTION %u (type %u, delta %u, len %u", option_index, current_number, current_option[0]>>4, (0x0F & current_option[0])); - - if ((0x0F & current_option[0]) < 15) - { - option_len = 0x0F & current_option[0]; - ++current_option; - } - else - { - option_len = 15; - do - { - ++current_option; - option_len += current_option[0]; - PRINTF("+%u", current_option[0]); - } while (current_option[0]==255); - ++current_option; - } - PRINTF("=%u: ", option_len); - - SET_OPTION(coap_pkt, current_number); - - switch (current_number) - { - case COAP_OPTION_CONTENT_TYPE: - coap_pkt->content_type = coap_parse_int_option(current_option, option_len); - PRINTF("Content-Format [%u]\n", coap_pkt->content_type); - break; - case COAP_OPTION_MAX_AGE: - coap_pkt->max_age = coap_parse_int_option(current_option, option_len); - PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); - break; - case COAP_OPTION_PROXY_URI: - /*FIXME check for own end-point */ - coap_pkt->proxy_uri = (char *) current_option; - coap_pkt->proxy_uri_len = option_len; - /*TODO length > 270 not implemented (actually not required) */ - PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri); - coap_error_message = "This is a constrained server (Contiki)"; - return PROXYING_NOT_SUPPORTED_5_05; - break; - case COAP_OPTION_ETAG: - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, option_len); - memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len); - PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len, - coap_pkt->etag[0], - coap_pkt->etag[1], - coap_pkt->etag[2], - coap_pkt->etag[3], - coap_pkt->etag[4], - coap_pkt->etag[5], - coap_pkt->etag[6], - coap_pkt->etag[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_URI_HOST: - coap_pkt->uri_host = (char *) current_option; - coap_pkt->uri_host_len = option_len; - PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); - break; - case COAP_OPTION_LOCATION_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_path), &(coap_pkt->location_path_len), current_option, option_len, '/'); - PRINTF("Location-Path [%.*s]\n", coap_pkt->location_path_len, coap_pkt->location_path); - break; - case COAP_OPTION_URI_PORT: - coap_pkt->uri_port = coap_parse_int_option(current_option, option_len); - PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); - break; - case COAP_OPTION_LOCATION_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_len, '&'); - PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, coap_pkt->location_query); - break; - case COAP_OPTION_URI_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_len, '/'); - PRINTF("Uri-Path [%.*s]\n", coap_pkt->uri_path_len, coap_pkt->uri_path); - break; - case COAP_OPTION_OBSERVE: - coap_pkt->observe = coap_parse_int_option(current_option, option_len); - PRINTF("Observe [%lu]\n", coap_pkt->observe); - break; - case COAP_OPTION_TOKEN: - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, option_len); - memcpy(coap_pkt->token, current_option, coap_pkt->token_len); - PRINTF("Token %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len, - coap_pkt->token[0], - coap_pkt->token[1], - coap_pkt->token[2], - coap_pkt->token[3], - coap_pkt->token[4], - coap_pkt->token[5], - coap_pkt->token[6], - coap_pkt->token[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_ACCEPT: - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_len); - coap_pkt->accept_num += 1; - PRINTF("Accept [%u]\n", coap_pkt->content_type); - } - break; - case COAP_OPTION_IF_MATCH: - /*FIXME support multiple ETags */ - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, option_len); - memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len); - PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len, - coap_pkt->if_match[0], - coap_pkt->if_match[1], - coap_pkt->if_match[2], - coap_pkt->if_match[3], - coap_pkt->if_match[4], - coap_pkt->if_match[5], - coap_pkt->if_match[6], - coap_pkt->if_match[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_URI_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_len, '&'); - PRINTF("Uri-Query [%.*s]\n", coap_pkt->uri_query_len, coap_pkt->uri_query); - break; - case COAP_OPTION_BLOCK2: - coap_pkt->block2_num = coap_parse_int_option(current_option, option_len); - coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3; - coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07); - coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07); - coap_pkt->block2_num >>= 4; - PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); - break; - case COAP_OPTION_BLOCK1: - coap_pkt->block1_num = coap_parse_int_option(current_option, option_len); - coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3; - coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07); - coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07); - coap_pkt->block1_num >>= 4; - PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); - break; - case COAP_OPTION_SIZE: - coap_pkt->size = coap_parse_int_option(current_option, option_len); - PRINTF("Size [%lu]\n", coap_pkt->size); - break; - case COAP_OPTION_IF_NONE_MATCH: - coap_pkt->if_none_match = 1; - PRINTF("If-None-Match\n"); - break; - default: - PRINTF("unknown (%u)\n", current_number); - /* Check if critical (odd) */ - if (current_number & 1) - { - coap_error_message = "Unsupported critical option"; - return BAD_OPTION_4_02; - } - } - - current_option += option_len; - } /* for */ - PRINTF("-Done parsing-------\n"); - } /* if (oc) */ - - coap_pkt->payload = current_option; - coap_pkt->payload_len = data_len - (coap_pkt->payload - data); - - /* also for receiving, the Erbium upper bound is REST_MAX_CHUNK_SIZE */ - if (coap_pkt->payload_len > REST_MAX_CHUNK_SIZE) - { - coap_pkt->payload_len = REST_MAX_CHUNK_SIZE; - } - - /* Null-terminate payload */ - coap_pkt->payload[coap_pkt->payload_len] = '\0'; - - return NO_ERROR; -} -/*-----------------------------------------------------------------------------------*/ -/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_query_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { - return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output); - } - return 0; -} - -int -coap_get_post_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload_len) { - return coap_get_variable((const char *)coap_pkt->payload, coap_pkt->payload_len, name, output); - } - return 0; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_set_status_code(void *packet, unsigned int code) -{ - if (code <= 0xFF) - { - ((coap_packet_t *)packet)->code = (uint8_t) code; - return 1; - } - else - { - return 0; - } -} -/*-----------------------------------------------------------------------------------*/ -/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -unsigned int -coap_get_header_content_type(void *packet) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1; - - return coap_pkt->content_type; -} - -int -coap_set_header_content_type(void *packet, unsigned int content_type) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->content_type = (coap_content_type_t) content_type; - SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_accept(void *packet, const uint16_t **accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0; - - *accept = coap_pkt->accept; - return coap_pkt->accept_num; -} - -int -coap_set_header_accept(void *packet, uint16_t accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = accept; - coap_pkt->accept_num += 1; - - SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT); - } - return coap_pkt->accept_num; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_max_age(void *packet, uint32_t *age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { - *age = COAP_DEFAULT_MAX_AGE; - } else { - *age = coap_pkt->max_age; - } - return 1; -} - -int -coap_set_header_max_age(void *packet, uint32_t age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->max_age = age; - SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_etag(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0; - - *etag = coap_pkt->etag; - return coap_pkt->etag_len; -} - -int -coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->etag, etag, coap_pkt->etag_len); - - SET_OPTION(coap_pkt, COAP_OPTION_ETAG); - return coap_pkt->etag_len; -} -/*-----------------------------------------------------------------------------------*/ -/*FIXME support multiple ETags */ -int -coap_get_header_if_match(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0; - - *etag = coap_pkt->if_match; - return coap_pkt->if_match_len; -} - -int -coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len); - - SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH); - return coap_pkt->if_match_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_if_none_match(void *packet) -{ - return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; -} - -int -coap_set_header_if_none_match(void *packet) -{ - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_token(void *packet, const uint8_t **token) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) return 0; - - *token = coap_pkt->token; - return coap_pkt->token_len; -} - -int -coap_set_header_token(void *packet, const uint8_t *token, size_t token_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len); - memcpy(coap_pkt->token, token, coap_pkt->token_len); - - SET_OPTION(coap_pkt, COAP_OPTION_TOKEN); - return coap_pkt->token_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_proxy_uri(void *packet, const char **uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0; - - *uri = coap_pkt->proxy_uri; - return coap_pkt->proxy_uri_len; -} - -int -coap_set_header_proxy_uri(void *packet, const char *uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->proxy_uri = uri; - coap_pkt->proxy_uri_len = strlen(uri); - - SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI); - return coap_pkt->proxy_uri_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_host(void *packet, const char **host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0; - - *host = coap_pkt->uri_host; - return coap_pkt->uri_host_len; -} - -int -coap_set_header_uri_host(void *packet, const char *host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->uri_host = host; - coap_pkt->uri_host_len = strlen(host); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST); - return coap_pkt->uri_host_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0; - - *path = coap_pkt->uri_path; - return coap_pkt->uri_path_len; -} - -int -coap_set_header_uri_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (path[0]=='/') ++path; - - coap_pkt->uri_path = path; - coap_pkt->uri_path_len = strlen(path); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); - return coap_pkt->uri_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0; - - *query = coap_pkt->uri_query; - return coap_pkt->uri_query_len; -} - -int -coap_set_header_uri_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->uri_query = query; - coap_pkt->uri_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); - return coap_pkt->uri_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0; - - *path = coap_pkt->location_path; - return coap_pkt->location_path_len; -} - -int -coap_set_header_location_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - char *query; - - while (path[0]=='/') ++path; - - if ((query = strchr(path, '?'))) - { - coap_set_header_location_query(packet, query+1); - coap_pkt->location_path_len = query - path; - } - else - { - coap_pkt->location_path_len = strlen(path); - } - - coap_pkt->location_path = path; - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH); - return coap_pkt->location_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0; - - *query = coap_pkt->location_query; - return coap_pkt->location_query_len; -} - -int -coap_set_header_location_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->location_query = query; - coap_pkt->location_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY); - return coap_pkt->location_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_observe(void *packet, uint32_t *observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0; - - *observe = coap_pkt->observe; - return 1; -} - -int -coap_set_header_observe(void *packet, uint32_t observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->observe = observe; - SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block2_num; - if (more!=NULL) *more = coap_pkt->block2_more; - if (size!=NULL) *size = coap_pkt->block2_size; - if (offset!=NULL) *offset = coap_pkt->block2_offset; - - return 1; -} - -int -coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block2_num = num; - coap_pkt->block2_more = more ? 1 : 0; - coap_pkt->block2_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block1_num; - if (more!=NULL) *more = coap_pkt->block1_more; - if (size!=NULL) *size = coap_pkt->block1_size; - if (offset!=NULL) *offset = coap_pkt->block1_offset; - - return 1; -} - -int -coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block1_num = num; - coap_pkt->block1_more = more; - coap_pkt->block1_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_size(void *packet, uint32_t *size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) return 0; - - *size = coap_pkt->size; - return 1; -} - -int -coap_set_header_size(void *packet, uint32_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->size = size; - SET_OPTION(coap_pkt, COAP_OPTION_SIZE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -/*- PAYLOAD -------------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_payload(void *packet, const uint8_t **payload) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload) { - *payload = coap_pkt->payload; - return coap_pkt->payload_len; - } else { - *payload = NULL; - return 0; - } -} - -int -coap_set_payload(void *packet, const void *payload, size_t length) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - //PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE); - - coap_pkt->payload = (uint8_t *) payload; - coap_pkt->payload_len = MIN(REST_MAX_CHUNK_SIZE, length); - - return coap_pkt->payload_len; -} -/*-----------------------------------------------------------------------------------*/ diff --git a/apps/er-coap-12/er-coap-12.h b/apps/er-coap-12/er-coap-12.h deleted file mode 100644 index 78a642ba7..000000000 --- a/apps/er-coap-12/er-coap-12.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 12) - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_12_H_ -#define COAP_12_H_ - -#include /* for size_t */ -#include "contiki-net.h" -#include "erbium.h" - -#define COAP_LINK_FORMAT_FILTERING 1 - -#define COAP_DEFAULT_PORT 5683 - -#ifndef COAP_SERVER_PORT -#define COAP_SERVER_PORT COAP_DEFAULT_PORT -#endif - -#define COAP_DEFAULT_MAX_AGE 60 -#define COAP_RESPONSE_TIMEOUT 2 -#define COAP_RESPONSE_RANDOM_FACTOR 1.5 -#define COAP_MAX_RETRANSMIT 4 - -#define COAP_HEADER_LEN 4 /* | oc:0xF0 type:0x0C version:0x03 | code | mid:0x00FF | mid:0xFF00 | */ -#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ -#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ -#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */ - -#define COAP_HEADER_VERSION_MASK 0xC0 -#define COAP_HEADER_VERSION_POSITION 6 -#define COAP_HEADER_TYPE_MASK 0x30 -#define COAP_HEADER_TYPE_POSITION 4 -#define COAP_HEADER_OPTION_COUNT_MASK 0x0F -#define COAP_HEADER_OPTION_COUNT_POSITION 0 - -#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 -#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F - -/* - * Conservative size limit, as not all options have to be set at the same time. - */ -#ifndef COAP_MAX_HEADER_SIZE -/* Hdr CoT Age Tag Obs Tok Blo strings */ -#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */ -#endif /* COAP_MAX_HEADER_SIZE */ - -#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) -/* 0/14 48 for IPv6 (28 for IPv4) */ -#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN) -#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" -#endif - -/* - * Maximum number of failed request attempts before action - */ -#ifndef COAP_MAX_ATTEMPTS -#define COAP_MAX_ATTEMPTS 4 -#endif /* COAP_MAX_ATTEMPTS */ - -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) - -/* Bitmap for set options */ -enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 }; -#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE)) -#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE))) - -#ifndef MIN -#define MIN(a, b) ((a) < (b)? (a) : (b)) -#endif /* MIN */ - -/* CoAP message types */ -typedef enum { - COAP_TYPE_CON, /* confirmables */ - COAP_TYPE_NON, /* non-confirmables */ - COAP_TYPE_ACK, /* acknowledgements */ - COAP_TYPE_RST /* reset */ -} coap_message_type_t; - -/* CoAP request method codes */ -typedef enum { - COAP_GET = 1, - COAP_POST, - COAP_PUT, - COAP_DELETE -} coap_method_t; - -/* CoAP response codes */ -typedef enum { - NO_ERROR = 0, - - CREATED_2_01 = 65, /* CREATED */ - DELETED_2_02 = 66, /* DELETED */ - VALID_2_03 = 67, /* NOT_MODIFIED */ - CHANGED_2_04 = 68, /* CHANGED */ - CONTENT_2_05 = 69, /* OK */ - - BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ - UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ - BAD_OPTION_4_02 = 130, /* BAD_OPTION */ - FORBIDDEN_4_03 = 131, /* FORBIDDEN */ - NOT_FOUND_4_04 = 132, /* NOT_FOUND */ - METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ - NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ - PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ - REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ - UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ - - INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ - NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ - BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ - SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ - GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ - PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - - /* Erbium errors */ - MEMORY_ALLOCATION_ERROR = 192, - PACKET_SERIALIZATION_ERROR, - - /* Erbium hooks */ - MANUAL_RESPONSE - -} coap_status_t; - -/* CoAP header options */ -typedef enum { - COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ - COAP_OPTION_URI_HOST = 3, /* 1-255 B */ - COAP_OPTION_ETAG = 4, /* 1-8 B */ - COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ - COAP_OPTION_OBSERVE = 6, /* 0-3 B */ - COAP_OPTION_URI_PORT = 7, /* 0-2 B */ - COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ - COAP_OPTION_URI_PATH = 11, /* 0-255 B */ - COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */ - COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ - COAP_OPTION_URI_QUERY = 15, /* 0-270 B */ - COAP_OPTION_ACCEPT = 16, /* 0-2 B */ - COAP_OPTION_TOKEN = 19, /* 1-8 B */ - COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */ - COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ - COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ - COAP_OPTION_SIZE = 28, /* 0-4 B */ - COAP_OPTION_PROXY_URI = 35, /* 1-270 B */ -} coap_option_t; - -/* CoAP Content-Types */ -typedef enum { - TEXT_PLAIN = 0, - TEXT_XML = 1, /* Indented types are not in the initial registry. */ - TEXT_CSV = 2, - TEXT_HTML = 3, - IMAGE_GIF = 21, - IMAGE_JPEG = 22, - IMAGE_PNG = 23, - IMAGE_TIFF = 24, - AUDIO_RAW = 25, - VIDEO_RAW = 26, - APPLICATION_LINK_FORMAT = 40, - APPLICATION_XML = 41, - APPLICATION_OCTET_STREAM = 42, - APPLICATION_RDF_XML = 43, - APPLICATION_SOAP_XML = 44, - APPLICATION_ATOM_XML = 45, - APPLICATION_XMPP_XML = 46, - APPLICATION_EXI = 47, - APPLICATION_FASTINFOSET = 48, - APPLICATION_SOAP_FASTINFOSET = 49, - APPLICATION_JSON = 50, - APPLICATION_X_OBIX_BINARY = 51 -} coap_content_type_t; - -/* Parsed message struct */ -typedef struct { - uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */ - - uint8_t version; - coap_message_type_t type; - uint8_t option_count; - uint8_t code; - uint16_t mid; - - uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */ - - coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */ - uint32_t max_age; - size_t proxy_uri_len; - const char *proxy_uri; - uint8_t etag_len; - uint8_t etag[COAP_ETAG_LEN]; - size_t uri_host_len; - const char *uri_host; - size_t location_path_len; - const char *location_path; - uint16_t uri_port; - size_t location_query_len; - const char *location_query; - size_t uri_path_len; - const char *uri_path; - uint16_t observe; - uint8_t token_len; - uint8_t token[COAP_TOKEN_LEN]; - uint8_t accept_num; - uint16_t accept[COAP_MAX_ACCEPT_NUM]; - uint8_t if_match_len; - uint8_t if_match[COAP_ETAG_LEN]; - uint32_t block2_num; - uint8_t block2_more; - uint16_t block2_size; - uint32_t block2_offset; - uint32_t block1_num; - uint8_t block1_more; - uint16_t block1_size; - uint32_t block1_offset; - uint32_t size; - size_t uri_query_len; - const char *uri_query; - uint8_t if_none_match; - - uint16_t payload_len; - uint8_t *payload; - -} coap_packet_t; - -/* Option format serialization*/ -#define COAP_SERIALIZE_INT_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" [%u]\n", coap_pkt->field); \ - option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \ - coap_pkt->option_count += 1; \ - current_number = number; \ - } -#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \ - coap_pkt->field[0], \ - coap_pkt->field[1], \ - coap_pkt->field[2], \ - coap_pkt->field[3], \ - coap_pkt->field[4], \ - coap_pkt->field[5], \ - coap_pkt->field[6], \ - coap_pkt->field[7] \ - ); /*FIXME always prints 8 bytes */ \ - uint8_t split_options = '\0'; \ - option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, &split_options); \ - coap_pkt->option_count += split_options; \ - current_number = number; \ - } -#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \ - uint8_t split_options = splitter; \ - option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, &split_options); \ - coap_pkt->option_count += split_options; \ - current_number = number; \ - } -#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - int i; \ - for (i=0; ifield##_num; ++i) \ - { \ - PRINTF(text" [%u]\n", coap_pkt->field[i]); \ - option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \ - coap_pkt->option_count += 1; \ - current_number = number; \ - } \ - } -#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) \ - { \ - PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \ - uint32_t block = coap_pkt->field##_num << 4; \ - if (coap_pkt->field##_more) block |= 0x8; \ - block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \ - PRINTF(text" encoded: 0x%lX\n", block); \ - option += coap_serialize_int_option(number, current_number, option, block); \ - coap_pkt->option_count += 1; \ - current_number = number; \ - } - -/* To store error code and human-readable payload */ -extern coap_status_t coap_error_code; -extern char *coap_error_message; - -void coap_init_connection(uint16_t port); -uint16_t coap_get_mid(void); - -void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid); -size_t coap_serialize_message(void *packet, uint8_t *buffer); -void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length); -coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len); - -int coap_get_query_variable(void *packet, const char *name, const char **output); -int coap_get_post_variable(void *packet, const char *name, const char **output); - -/*-----------------------------------------------------------------------------------*/ - -int coap_set_status_code(void *packet, unsigned int code); - -unsigned int coap_get_header_content_type(void *packet); -int coap_set_header_content_type(void *packet, unsigned int content_type); - -int coap_get_header_accept(void *packet, const uint16_t **accept); -int coap_set_header_accept(void *packet, uint16_t accept); - -int coap_get_header_max_age(void *packet, uint32_t *age); -int coap_set_header_max_age(void *packet, uint32_t age); - -int coap_get_header_etag(void *packet, const uint8_t **etag); -int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len); - -int coap_get_header_if_match(void *packet, const uint8_t **etag); -int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len); - -int coap_get_header_if_none_match(void *packet); -int coap_set_header_if_none_match(void *packet); - -int coap_get_header_token(void *packet, const uint8_t **token); -int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len); - -int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */ -int coap_set_header_proxy_uri(void *packet, const char *uri); - -int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_host(void *packet, const char *host); - -int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_path(void *packet, const char *path); - -int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_query(void *packet, const char *query); - -int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ -int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */ - -int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ -int coap_set_header_location_query(void *packet, const char *query); - -int coap_get_header_observe(void *packet, uint32_t *observe); -int coap_set_header_observe(void *packet, uint32_t observe); - -int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); -int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size); - -int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); -int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size); - -int coap_get_header_size(void *packet, uint32_t *size); -int coap_set_header_size(void *packet, uint32_t size); - -int coap_get_payload(void *packet, const uint8_t **payload); -int coap_set_payload(void *packet, const void *payload, size_t length); - -#endif /* COAP_12_H_ */ diff --git a/apps/er-coap-13/Makefile.er-coap-13 b/apps/er-coap-13/Makefile.er-coap-13 deleted file mode 100644 index b3c3ba169..000000000 --- a/apps/er-coap-13/Makefile.er-coap-13 +++ /dev/null @@ -1 +0,0 @@ -er-coap-13_src = er-coap-13.c er-coap-13-engine.c er-coap-13-transactions.c er-coap-13-observing.c er-coap-13-separate.c diff --git a/apps/er-coap-13/er-coap-13-engine.c b/apps/er-coap-13/er-coap-13-engine.c deleted file mode 100644 index 9bde8b7f7..000000000 --- a/apps/er-coap-13/er-coap-13-engine.c +++ /dev/null @@ -1,681 +0,0 @@ -/* - * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP implementation of the REST Engine - * \author - * Matthias Kovatsch - */ - -#include -#include -#include -#include "contiki.h" -#include "contiki-net.h" - -#include "er-coap-13-engine.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#define PRINTBITS(buf,len) { \ - int i,j=0; \ - for (i=0; i=0; --j) { \ - PRINTF("%c", (((char *)buf)[i] & 1<srcipaddr); - PRINTF(":%u\n Length: %u\n Data: ", uip_ntohs(UIP_UDP_BUF->srcport), uip_datalen() ); - PRINTBITS(uip_appdata, uip_datalen()); - PRINTF("\n"); - - coap_error_code = coap_parse_message(message, uip_appdata, uip_datalen()); - - if (coap_error_code==NO_ERROR) - { - - /*TODO duplicates suppression, if required by application */ - - PRINTF(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version, message->type, message->token_len, message->code, message->mid); - PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path); - PRINTF(" Payload: %.*s\n", message->payload_len, message->payload); - - /* Handle requests. */ - if (message->code >= COAP_GET && message->code <= COAP_DELETE) - { - /* Use transaction buffer for response to confirmable request. */ - if ( (transaction = coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport)) ) - { - 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) - { - /* Reliable CON requests are answered with an ACK. */ - coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, message->mid); - } - else - { - /* Unreliable NON requests are answered with a NON as well. */ - coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid()); - } - - /* mirror token */ - if (message->token_len) - { - coap_set_header_token(response, message->token, message->token_len); - } - - /* get offset for blockwise transfers */ - if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset)) - { - PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); - block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); - new_offset = block_offset; - } - - /* Invoke resource handler. */ - if (service_cbk) - { - /* Call REST framework and check if found and allowed. */ - if (service_cbk(message, response, transaction->packet+COAP_MAX_HEADER_SIZE, block_size, &new_offset)) - { - if (coap_error_code==NO_ERROR) - { - /* Apply blockwise transfers. */ - if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->codepayload_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 */ - } - 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 - { - /* 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); - - 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 - { - coap_error_code = NOT_IMPLEMENTED_5_01; - coap_error_message = "NoServiceCallbck"; // no a to fit 16 bytes - } /* if (service callback) */ - - } else { - coap_error_code = SERVICE_UNAVAILABLE_5_03; - coap_error_message = "NoFreeTraBuffer"; - } /* if (transaction buffer) */ - } - else - { - /* Responses */ - if (message->type==COAP_TYPE_CON && message->code==0) - { - PRINTF("Received Ping\n"); - coap_error_code = PING_RESPONSE; - } - else if (message->type==COAP_TYPE_ACK) - { - /* Transactions are closed through lookup below */ - PRINTF("Received ACK\n"); - } - else if (message->type==COAP_TYPE_RST) - { - PRINTF("Received RST\n"); - /* Cancel possible subscriptions. */ - coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, message->mid); - } - - if ( (transaction = coap_get_transaction_by_mid(message->mid)) ) - { - /* Free transaction memory before callback, as it may create a new transaction. */ - restful_response_handler callback = transaction->callback; - void *callback_data = transaction->callback_data; - coap_clear_transaction(transaction); - - /* Check if someone registered for the response */ - if (callback) { - callback(callback_data, message); - } - } /* if (ACKed transaction) */ - transaction = NULL; - - } /* Request or Response */ - - } /* if (parsed correctly) */ - - 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 - { - coap_message_type_t reply_type = COAP_TYPE_ACK; - - PRINTF("ERROR %u: %s\n", coap_error_code, coap_error_message); - coap_clear_transaction(transaction); - - /* Set to sendable error code. */ - if (coap_error_code >= 192) - { - coap_error_code = INTERNAL_SERVER_ERROR_5_00; - } - if (coap_error_code == PING_RESPONSE) - { - coap_error_code = 0; - reply_type = COAP_TYPE_RST; - } - /* Reuse input buffer for error message. */ - coap_init_message(message, reply_type, coap_error_code, message->mid); - coap_set_payload(message, coap_error_message, strlen(coap_error_message)); - coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, uip_appdata, coap_serialize_message(message, uip_appdata)); - } - } /* if (new data) */ - - return coap_error_code; -} -/*----------------------------------------------------------------------------*/ -void -coap_receiver_init() -{ - process_start(&coap_receiver, NULL); -} -/*----------------------------------------------------------------------------*/ -void -coap_set_service_callback(service_callback_t callback) -{ - service_cbk = callback; -} -/*----------------------------------------------------------------------------*/ -rest_resource_flags_t -coap_get_rest_method(void *packet) -{ - return (rest_resource_flags_t)(1 << (((coap_packet_t *)packet)->code - 1)); -} -/*----------------------------------------------------------------------------*/ -/*- Server part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ - -/* The discover resource is automatically included for CoAP. */ -RESOURCE(well_known_core, METHOD_GET, ".well-known/core", "ct=40"); -void -well_known_core_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - size_t strpos = 0; /* position in overall string (which is larger than the buffer) */ - size_t bufpos = 0; /* position within buffer (bytes written) */ - size_t tmplen = 0; - resource_t* resource = NULL; - -#if COAP_LINK_FORMAT_FILTERING - /* For filtering. */ - const char *filter = NULL; - const char *attrib = NULL; - const char *found = NULL; - const char *end = NULL; - char *value = NULL; - char lastchar = '\0'; - int len = coap_get_header_uri_query(request, &filter); - - if (len) - { - value = strchr(filter, '='); - value[0] = '\0'; - ++value; - len -= strlen(filter)+1; - - PRINTF("Filter %s = %.*s\n", filter, len, value); - - if (strcmp(filter,"href")==0 && value[0]=='/') - { - ++value; - --len; - } - - lastchar = value[len-1]; - value[len-1] = '\0'; - } -#endif - - for (resource = (resource_t*)list_head(rest_get_resources()); resource; resource = resource->next) - { -#if COAP_LINK_FORMAT_FILTERING - /* Filtering */ - if (len) - { - if (strcmp(filter,"href")==0) - { - attrib=strstr(resource->url, value); - if (attrib==NULL || (value[-1]=='/' && attrib!=resource->url)) continue; - end = attrib + strlen(attrib); - } - else - { - attrib=strstr(resource->attributes, filter); - if (attrib==NULL || (attrib[strlen(filter)]!='=' && attrib[strlen(filter)]!='"')) continue; - attrib += strlen(filter)+2; - end = strchr(attrib, '"'); - } - - PRINTF("Filter: res has attrib %s (%s)\n", attrib, value); - found = attrib; - while ((found=strstr(found, value))!=NULL) { - if (found > end) - { - found = NULL; - break; - } - if (lastchar==found[len-1] || lastchar=='*') - { - break; - } - ++found; - } - if (found==NULL) - { - continue; - } - PRINTF("Filter: res has prefix %s\n", found); - if (lastchar!='*' && (found[len]!='"' && found[len]!=' ' && found[len]!='\0')) continue; - PRINTF("Filter: res has match\n"); - } -#endif - - PRINTF("res: /%s (%p)\npos: s%d, o%ld, b%d\n", resource->url, resource, strpos, *offset, bufpos); - - if (strpos>0) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ','; - } - ++strpos; - } - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '<'; - } - ++strpos; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '/'; - } - ++strpos; - - tmplen = strlen(resource->url); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->url + ((*offset-(int32_t)strpos > 0) ? (*offset-(int32_t)strpos) : 0)); - /* minimal-net requires these casts */ - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = '>'; - } - ++strpos; - - if (resource->attributes[0]) - { - if (strpos >= *offset && bufpos < preferred_size) - { - buffer[bufpos++] = ';'; - } - ++strpos; - - tmplen = strlen(resource->attributes); - if (strpos+tmplen > *offset) - { - bufpos += snprintf((char *) buffer + bufpos, preferred_size - bufpos + 1, - "%s", resource->attributes + (*offset-(int32_t)strpos > 0 ? *offset-(int32_t)strpos : 0)); - if (bufpos >= preferred_size) - { - break; - } - } - strpos += tmplen; - } - - /* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */ - if (bufpos >= preferred_size && strpos-bufpos > *offset) - { - PRINTF("res: BREAK at %s (%p)\n", resource->url, resource); - break; - } - } - - if (bufpos>0) { - PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *) buffer); - - coap_set_payload(response, buffer, bufpos ); - coap_set_header_content_type(response, APPLICATION_LINK_FORMAT); - } - else if (strpos>0) - { - PRINTF("well_known_core_handler(): bufpos<=0\n"); - - coap_set_status_code(response, BAD_OPTION_4_02); - coap_set_payload(response, "BlockOutOfScope", 15); - } - - if (resource==NULL) { - PRINTF("res: DONE\n"); - *offset = -1; - } - else - { - PRINTF("res: MORE at %s (%p)\n", resource->url, resource); - *offset += preferred_size; - } -} -/*----------------------------------------------------------------------------*/ -PROCESS_THREAD(coap_receiver, ev, data) -{ - PROCESS_BEGIN(); - PRINTF("Starting CoAP-13 receiver...\n"); - - rest_activate_resource(&resource_well_known_core); - - coap_register_as_transaction_handler(); - coap_init_connection(SERVER_LISTEN_PORT); - - while(1) { - PROCESS_YIELD(); - - if(ev == tcpip_event) { - coap_receive(); - } else if (ev == PROCESS_EVENT_TIMER) { - /* retransmissions are handled here */ - coap_check_transactions(); - } - } /* while (1) */ - - PROCESS_END(); -} -/*----------------------------------------------------------------------------*/ -/*- Client part --------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -void coap_blocking_request_callback(void *callback_data, void *response) { - struct request_state_t *state = (struct request_state_t *) callback_data; - state->response = (coap_packet_t*) response; - process_poll(state->process); -} -/*----------------------------------------------------------------------------*/ -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)) { - PT_BEGIN(&state->pt); - - static uint8_t more; - static uint32_t res_block; - static uint8_t block_error; - - state->block_num = 0; - state->response = NULL; - state->process = PROCESS_CURRENT(); - - more = 0; - res_block = 0; - block_error = 0; - - do { - request->mid = coap_get_mid(); - if ((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, remote_port))) - { - state->transaction->callback = coap_blocking_request_callback; - state->transaction->callback_data = state; - - if (state->block_num>0) - { - coap_set_header_block2(request, state->block_num, 0, REST_MAX_CHUNK_SIZE); - } - - state->transaction->packet_len = coap_serialize_message(request, state->transaction->packet); - - coap_send_transaction(state->transaction); - PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid); - - PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); - - if (!state->response) - { - PRINTF("Server not responding\n"); - PT_EXIT(&state->pt); - } - - coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); - - PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", state->response->payload_len); - - if (res_block==state->block_num) - { - request_callback(state->response); - ++(state->block_num); - } - else - { - PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num); - ++block_error; - } - } - else - { - PRINTF("Could not allocate transaction buffer"); - PT_EXIT(&state->pt); - } - } while (more && block_errorpt); -} -/*----------------------------------------------------------------------------*/ -/*- Engine Interface ---------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -const struct rest_implementation coap_rest_implementation = { - "CoAP-13", - - coap_receiver_init, - coap_set_service_callback, - - coap_get_header_uri_path, - coap_set_header_uri_path, - coap_get_rest_method, - coap_set_status_code, - - coap_get_header_content_type, - coap_set_header_content_type, - coap_get_header_accept, - coap_get_header_size, - coap_set_header_size, - coap_get_header_max_age, - coap_set_header_max_age, - coap_set_header_etag, - coap_get_header_if_match, - coap_get_header_if_none_match, - coap_get_header_uri_host, - coap_set_header_location_path, - - coap_get_payload, - coap_set_payload, - - coap_get_header_uri_query, - coap_get_query_variable, - coap_get_post_variable, - - coap_notify_observers, - (restful_post_handler) coap_observe_handler, - - NULL, /* default pre-handler (set separate handler after activation if needed) */ - NULL, /* default post-handler for non-observable resources */ - - { - CONTENT_2_05, - CREATED_2_01, - CHANGED_2_04, - DELETED_2_02, - VALID_2_03, - BAD_REQUEST_4_00, - UNAUTHORIZED_4_01, - BAD_OPTION_4_02, - FORBIDDEN_4_03, - NOT_FOUND_4_04, - METHOD_NOT_ALLOWED_4_05, - NOT_ACCEPTABLE_4_06, - REQUEST_ENTITY_TOO_LARGE_4_13, - UNSUPPORTED_MEDIA_TYPE_4_15, - INTERNAL_SERVER_ERROR_5_00, - NOT_IMPLEMENTED_5_01, - BAD_GATEWAY_5_02, - SERVICE_UNAVAILABLE_5_03, - GATEWAY_TIMEOUT_5_04, - PROXYING_NOT_SUPPORTED_5_05 - }, - - { - TEXT_PLAIN, - TEXT_XML, - TEXT_CSV, - TEXT_HTML, - IMAGE_GIF, - IMAGE_JPEG, - IMAGE_PNG, - IMAGE_TIFF, - AUDIO_RAW, - VIDEO_RAW, - APPLICATION_LINK_FORMAT, - APPLICATION_XML, - APPLICATION_OCTET_STREAM, - APPLICATION_RDF_XML, - APPLICATION_SOAP_XML, - APPLICATION_ATOM_XML, - APPLICATION_XMPP_XML, - APPLICATION_EXI, - APPLICATION_FASTINFOSET, - APPLICATION_SOAP_FASTINFOSET, - APPLICATION_JSON, - APPLICATION_X_OBIX_BINARY - } -}; diff --git a/apps/er-coap-13/er-coap-13-engine.h b/apps/er-coap-13/er-coap-13-engine.h deleted file mode 100644 index 4730a0791..000000000 --- a/apps/er-coap-13/er-coap-13-engine.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP implementation of the REST Engine - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_SERVER_H_ -#define COAP_SERVER_H_ - -#if !defined(REST) -#error "Define REST to \"coap_rest_implementation\"" -#endif - -#include "er-coap-13.h" -#include "er-coap-13-transactions.h" -#include "er-coap-13-observing.h" -#include "er-coap-13-separate.h" - -#include "pt.h" - -/* Declare server process */ -PROCESS_NAME(coap_receiver); - -#define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT) - -typedef coap_packet_t rest_request_t; -typedef coap_packet_t rest_response_t; - -extern const struct rest_implementation coap_rest_implementation; - -void coap_receiver_init(void); - -/*-----------------------------------------------------------------------------------*/ -/*- Client part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -struct request_state_t { - struct pt pt; - struct process *process; - coap_transaction_t *transaction; - coap_packet_t *response; - uint32_t block_num; -}; - -typedef void (*blocking_response_handler) (void* response); - -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)); - -#define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \ -{ \ - static struct request_state_t request_state; \ - PT_SPAWN(process_pt, &request_state.pt, \ - coap_blocking_request(&request_state, ev, \ - server_addr, server_port, \ - request, chunk_handler) \ - ); \ -} -/*-----------------------------------------------------------------------------------*/ - -#endif /* COAP_SERVER_H_ */ diff --git a/apps/er-coap-13/er-coap-13-observing.c b/apps/er-coap-13/er-coap-13-observing.c deleted file mode 100644 index eeb430877..000000000 --- a/apps/er-coap-13/er-coap-13-observing.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * CoAP module for observing resources - * \author - * Matthias Kovatsch - */ - -#include -#include - -#include "er-coap-13-observing.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS); -LIST(observers_list); - -/*-----------------------------------------------------------------------------------*/ -coap_observer_t * -coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url) -{ - /* Remove existing observe relationship, if any. */ - coap_remove_observer_by_url(addr, port, url); - - coap_observer_t *o = memb_alloc(&observers_memb); - - if (o) - { - o->url = url; - uip_ipaddr_copy(&o->addr, addr); - o->port = port; - o->token_len = token_len; - memcpy(o->token, token, token_len); - o->last_mid = 0; - - stimer_set(&o->refresh_timer, COAP_OBSERVING_REFRESH_INTERVAL); - - PRINTF("Adding observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - list_add(observers_list, o); - } - - return o; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_remove_observer(coap_observer_t *o) -{ - PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], o->token[1]); - - memb_free(&observers_memb, o); - list_remove(observers_list, o); -} - -int -coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check client "); - PRINT6ADDR(addr); - PRINTF(":%u\n", port); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->token_len==token_len && memcmp(obs->token, token, token_len)==0) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check URL %p\n", url); - if ((addr==NULL || (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port)) && (obs->url==url || memcmp(obs->url, url, strlen(obs->url))==0)) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} - -int -coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid) -{ - int removed = 0; - coap_observer_t* obs = NULL; - - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - PRINTF("Remove check MID %u\n", mid); - if (uip_ipaddr_cmp(&obs->addr, addr) && obs->port==port && obs->last_mid==mid) - { - coap_remove_observer(obs); - removed++; - } - } - return removed; -} -/*-----------------------------------------------------------------------------------*/ -void -coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification) -{ - coap_packet_t *const coap_res = (coap_packet_t *) notification; - coap_observer_t* obs = NULL; - uint8_t preferred_type = coap_res->type; - - PRINTF("Observing: Notification from %s\n", resource->url); - - /* Iterate over observers. */ - for (obs = (coap_observer_t*)list_head(observers_list); obs; obs = obs->next) - { - if (obs->url==resource->url) /* using RESOURCE url pointer as handle */ - { - coap_transaction_t *transaction = NULL; - - /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers. */ - - if ( (transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port)) ) - { - PRINTF(" Observer "); - PRINT6ADDR(&obs->addr); - PRINTF(":%u\n", obs->port); - - /* Update last MID for RST matching. */ - obs->last_mid = transaction->mid; - - /* Prepare response */ - coap_res->mid = transaction->mid; - if (obs_counter>=0) coap_set_header_observe(coap_res, obs_counter); - coap_set_header_token(coap_res, obs->token, obs->token_len); - - /* Use CON to check whether client is still there/interested after COAP_OBSERVING_REFRESH_INTERVAL. */ - if (stimer_expired(&obs->refresh_timer)) - { - PRINTF(" Refreshing with CON\n"); - coap_res->type = COAP_TYPE_CON; - stimer_restart(&obs->refresh_timer); - } - else - { - coap_res->type = preferred_type; - } - - transaction->packet_len = coap_serialize_message(coap_res, transaction->packet); - - coap_send_transaction(transaction); - } - } - } -} -/*-----------------------------------------------------------------------------------*/ -void -coap_observe_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; - - static char content[16]; - - if (coap_req->code==COAP_GET && coap_res->code<128) /* GET request and response without error code */ - { - if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) - { - - if (coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, coap_req->token, coap_req->token_len, resource->url)) - { - coap_set_header_observe(coap_res, 0); - /* - * For demonstration purposes only. A subscription should return the same representation as a normal GET. - * TODO: Comment the following line for any real application. - */ - coap_set_payload(coap_res, content, snprintf(content, sizeof(content), "Added %u/%u", list_length(observers_list), COAP_MAX_OBSERVERS)); - } - else - { - coap_res->code = SERVICE_UNAVAILABLE_5_03; - coap_set_payload(coap_res, "TooManyObservers", 16); - } /* if (added observer) */ - } - else /* if (observe) */ - { - /* Remove client if it is currently observing. */ - coap_remove_observer_by_url(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, resource->url); - } /* if (observe) */ - } -} diff --git a/apps/er-coap-13/er-coap-13.c b/apps/er-coap-13/er-coap-13.c deleted file mode 100644 index 8bd623c36..000000000 --- a/apps/er-coap-13/er-coap-13.c +++ /dev/null @@ -1,1133 +0,0 @@ -/* - * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 12) - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include "contiki-net.h" -#include -#include - -#include "er-coap-13.h" -#include "er-coap-13-transactions.h" - - -#define DEBUG 0 -#if DEBUG -#include -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -/*-----------------------------------------------------------------------------------*/ -/*- Variables -----------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static struct uip_udp_conn *udp_conn = NULL; -static uint16_t current_mid = 0; - -coap_status_t coap_error_code = NO_ERROR; -char *coap_error_message = ""; -/*-----------------------------------------------------------------------------------*/ -/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -static -uint16_t -coap_log_2(uint16_t value) -{ - uint16_t result = 0; - do { - value = value >> 1; - result++; - } while (value); - - return result ? result - 1 : result; -} -/*-----------------------------------------------------------------------------------*/ -static -uint32_t -coap_parse_int_option(uint8_t *bytes, size_t length) -{ - uint32_t var = 0; - int i = 0; - while (i268) - { - buffer[++written] = (*x-269)>>8; - buffer[++written] = (*x-269); - } - else if (*x>12) - { - buffer[++written] = (*x-13); - } - } - while (x!=&length && (x=&length)); - - PRINTF("WRITTEN %u B opt header\n", written); - - return ++written; -} -/*-----------------------------------------------------------------------------------*/ -static -size_t -coap_serialize_int_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint32_t value) -{ - size_t i = 0; - - if (0xFF000000 & value) ++i; - if (0xFFFF0000 & value) ++i; - if (0xFFFFFF00 & value) ++i; - if (0xFFFFFFFF & value) ++i; - - PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, i); - - i = coap_set_option_header(number - current_number, i, buffer); - - if (0xFF000000 & value) buffer[i++] = (uint8_t) (value>>24); - if (0xFFFF0000 & value) buffer[i++] = (uint8_t) (value>>16); - if (0xFFFFFF00 & value) buffer[i++] = (uint8_t) (value>>8); - if (0xFFFFFFFF & value) buffer[i++] = (uint8_t) (value); - - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -size_t -coap_serialize_array_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint8_t *array, size_t length, char split_char) -{ - size_t i = 0; - - if (split_char!='\0') - { - int j; - uint8_t *part_start = array; - uint8_t *part_end = NULL; - size_t temp_length; - - for (j = 0; j<=length; ++j) - { - if (array[j]==split_char || j==length) - { - part_end = array + j; - temp_length = part_end-part_start; - - i += coap_set_option_header(number - current_number, temp_length, &buffer[i]); - memcpy(&buffer[i], part_start, temp_length); - i += temp_length; - - PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start); - - ++j; /* skip the splitter */ - current_number = number; - part_start = array + j; - } - } /* for */ - } - else - { - i += coap_set_option_header(number - current_number, length, &buffer[i]); - memcpy(&buffer[i], array, length); - i += length; - - PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, length); - } - - return i; -} -/*-----------------------------------------------------------------------------------*/ -static -void -coap_merge_multi_option(char **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator) -{ - /* Merge multiple options. */ - if (*dst_len > 0) - { - /* dst already contains an option: concatenate */ - (*dst)[*dst_len] = separator; - *dst_len += 1; - - /* memmove handles 2-byte option headers */ - memmove((*dst)+(*dst_len), option, option_len); - - *dst_len += option_len; - } - else - { - /* dst is empty: set to option */ - *dst = (char *) option; - *dst_len = option_len; - } -} -/*-----------------------------------------------------------------------------------*/ -static -int -coap_get_variable(const char *buffer, size_t length, const char *name, const char **output) -{ - const char *start = NULL; - const char *end = NULL; - const char *value_end = NULL; - size_t name_len = 0; - - /*initialize the output buffer first*/ - *output = 0; - - name_len = strlen(name); - end = buffer + length; - - for (start = buffer; start + name_len < end; ++start){ - if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && - strncmp(name, start, name_len)==0) { - - /* Point start to variable value */ - start += name_len + 1; - - /* Point end to the end of the value */ - value_end = (const char *) memchr(start, '&', end - start); - if (value_end == NULL) { - value_end = end; - } - - *output = start; - - return (value_end - start); - } - } - - return 0; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE SENDING ----------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_connection(uint16_t port) -{ - /* new connection with remote host */ - udp_conn = udp_new(NULL, 0, NULL); - udp_bind(udp_conn, port); - PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport)); - - /* Initialize transaction ID. */ - current_mid = random_rand(); -} -/*-----------------------------------------------------------------------------------*/ -uint16_t -coap_get_mid() -{ - return ++current_mid; -} -/*-----------------------------------------------------------------------------------*/ -/*- MEASSAGE PROCESSING -------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -void -coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Important thing */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - coap_pkt->type = type; - coap_pkt->code = code; - coap_pkt->mid = mid; -} -/*-----------------------------------------------------------------------------------*/ -size_t -coap_serialize_message(void *packet, uint8_t *buffer) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - uint8_t *option; - unsigned int current_number = 0; - - /* Initialize */ - coap_pkt->buffer = buffer; - coap_pkt->version = 1; - - PRINTF("-Serializing MID %u to %p, ", coap_pkt->mid, coap_pkt->buffer); - - /* set header fields */ - coap_pkt->buffer[0] = 0x00; - coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<buffer[0] |= COAP_HEADER_TOKEN_LEN_MASK & (coap_pkt->token_len)<buffer[1] = coap_pkt->code; - coap_pkt->buffer[2] = (uint8_t) ((coap_pkt->mid)>>8); - coap_pkt->buffer[3] = (uint8_t) (coap_pkt->mid); - - /* set Token */ - PRINTF("Token (len %u)", coap_pkt->token_len); - option = coap_pkt->buffer + COAP_HEADER_LEN; - for (current_number=0; current_numbertoken_len; ++current_number) - { - PRINTF(" %02X", coap_pkt->token[current_number]); - *option = coap_pkt->token[current_number]; - ++option; - } - PRINTF("-\n"); - - /* Serialize options */ - current_number = 0; - - PRINTF("-Serializing options at %p-\n", option); - - /* The options must be serialized in the order of their number */ - COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_IF_MATCH, if_match, "If-Match") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_HOST, uri_host, '\0', "Uri-Host") - COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_ETAG, etag, "ETag") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_IF_NONE_MATCH, content_type-coap_pkt->content_type, "If-None-Match") /* hack to get a zero field */ - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_OBSERVE, observe, "Observe") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_URI_PORT, uri_port, "Uri-Port") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_PATH, location_path, '/', "Location-Path") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_PATH, uri_path, '/', "Uri-Path") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_CONTENT_TYPE, content_type, "Content-Format") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_MAX_AGE, max_age, "Max-Age") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_QUERY, uri_query, '&', "Uri-Query") - COAP_SERIALIZE_ACCEPT_OPTION( COAP_OPTION_ACCEPT, accept, "Accept") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_QUERY, location_query, '&', "Location-Query") - COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK2, block2, "Block2") - COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK1, block1, "Block1") - COAP_SERIALIZE_INT_OPTION( COAP_OPTION_SIZE, size, "Size") - COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_PROXY_URI, proxy_uri, '\0', "Proxy-Uri") - - PRINTF("-Done serializing at %p----\n", option); - - /* Pack payload */ - if ((option - coap_pkt->buffer)<=COAP_MAX_HEADER_SIZE) - { - /* Payload marker */ - if (coap_pkt->payload_len) - { - *option = 0xFF; - ++option; - } - - memmove(option, coap_pkt->payload, coap_pkt->payload_len); - } - else - { - /* An error occured. Caller must check for !=0. */ - coap_pkt->buffer = NULL; - coap_error_message = "Serialized header exceeds COAP_MAX_HEADER_SIZE"; - return 0; - } - - PRINTF("-Done %u B (header len %u, payload len %u)-\n", coap_pkt->payload_len + option - buffer, option - buffer, coap_pkt->payload_len); - - PRINTF("Dump [0x%02X %02X %02X %02X %02X %02X %02X %02X]\n", - coap_pkt->buffer[0], - coap_pkt->buffer[1], - coap_pkt->buffer[2], - coap_pkt->buffer[3], - coap_pkt->buffer[4], - coap_pkt->buffer[5], - coap_pkt->buffer[6], - coap_pkt->buffer[7] - ); - - return (option - buffer) + coap_pkt->payload_len; /* packet length */ -} -/*-----------------------------------------------------------------------------------*/ -void -coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length) -{ - /* Configure connection to reply to client */ - uip_ipaddr_copy(&udp_conn->ripaddr, addr); - udp_conn->rport = port; - - uip_udp_packet_send(udp_conn, data, length); - PRINTF("-sent UDP datagram (%u)-\n", length); - - /* Restore server connection to allow data from any node */ - memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr)); - udp_conn->rport = 0; -} -/*-----------------------------------------------------------------------------------*/ -coap_status_t -coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - /* Initialize packet */ - memset(coap_pkt, 0, sizeof(coap_packet_t)); - - /* pointer to packet bytes */ - coap_pkt->buffer = data; - - /* parse header fields */ - coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION; - coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION; - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, (COAP_HEADER_TOKEN_LEN_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TOKEN_LEN_POSITION); - coap_pkt->code = coap_pkt->buffer[1]; - coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3]; - - if (coap_pkt->version != 1) - { - coap_error_message = "CoAP version must be 1"; - return BAD_REQUEST_4_00; - } - - uint8_t *current_option = data + COAP_HEADER_LEN; - - memcpy(coap_pkt->token, current_option, coap_pkt->token_len); - PRINTF("Token (len %u) [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len, - coap_pkt->token[0], - coap_pkt->token[1], - coap_pkt->token[2], - coap_pkt->token[3], - coap_pkt->token[4], - coap_pkt->token[5], - coap_pkt->token[6], - coap_pkt->token[7] - ); /*FIXME always prints 8 bytes */ - - - /* parse options */ - memset(coap_pkt->options, 0, sizeof(coap_pkt->options)); - current_option += coap_pkt->token_len; - - unsigned int option_number = 0; - unsigned int option_delta = 0; - size_t option_length = 0; - - while (current_option < data+data_len) - { - /* Payload marker 0xFF, currently only checking for 0xF* because rest is reserved */ - if ((current_option[0] & 0xF0)==0xF0) - { - coap_pkt->payload = ++current_option; - coap_pkt->payload_len = data_len - (coap_pkt->payload - data); - - /* also for receiving, the Erbium upper bound is REST_MAX_CHUNK_SIZE */ - if (coap_pkt->payload_len > REST_MAX_CHUNK_SIZE) - { - coap_pkt->payload_len = REST_MAX_CHUNK_SIZE; - } - - /* Null-terminate payload */ - coap_pkt->payload[coap_pkt->payload_len] = '\0'; - - break; - } - - option_delta = current_option[0]>>4; - option_length = current_option[0] & 0x0F; - ++current_option; - - /* avoids code duplication without function overhead */ - unsigned int *x = &option_delta; - do - { - if (*x==13) - { - *x += current_option[0]; - ++current_option; - } - else if (*x==14) - { - *x += 255; - *x += current_option[0]<<8; - ++current_option; - *x += current_option[0]; - ++current_option; - } - } - while (x!=&option_length && (x=&option_length)); - - option_number += option_delta; - - PRINTF("OPTION %u (delta %u, len %u): ", option_number, option_delta, option_length); - - SET_OPTION(coap_pkt, option_number); - - switch (option_number) - { - case COAP_OPTION_CONTENT_TYPE: - coap_pkt->content_type = coap_parse_int_option(current_option, option_length); - PRINTF("Content-Format [%u]\n", coap_pkt->content_type); - break; - case COAP_OPTION_MAX_AGE: - coap_pkt->max_age = coap_parse_int_option(current_option, option_length); - PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); - break; - case COAP_OPTION_ETAG: - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, option_length); - memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len); - PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len, - coap_pkt->etag[0], - coap_pkt->etag[1], - coap_pkt->etag[2], - coap_pkt->etag[3], - coap_pkt->etag[4], - coap_pkt->etag[5], - coap_pkt->etag[6], - coap_pkt->etag[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_ACCEPT: - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_length); - coap_pkt->accept_num += 1; - PRINTF("Accept [%u]\n", coap_pkt->content_type); - } - break; - case COAP_OPTION_IF_MATCH: - /*FIXME support multiple ETags */ - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, option_length); - memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len); - PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len, - coap_pkt->if_match[0], - coap_pkt->if_match[1], - coap_pkt->if_match[2], - coap_pkt->if_match[3], - coap_pkt->if_match[4], - coap_pkt->if_match[5], - coap_pkt->if_match[6], - coap_pkt->if_match[7] - ); /*FIXME always prints 8 bytes */ - break; - case COAP_OPTION_IF_NONE_MATCH: - coap_pkt->if_none_match = 1; - PRINTF("If-None-Match\n"); - break; - - case COAP_OPTION_URI_HOST: - coap_pkt->uri_host = (char *) current_option; - coap_pkt->uri_host_len = option_length; - PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); - break; - case COAP_OPTION_URI_PORT: - coap_pkt->uri_port = coap_parse_int_option(current_option, option_length); - PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); - break; - case COAP_OPTION_URI_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_length, '/'); - PRINTF("Uri-Path [%.*s]\n", coap_pkt->uri_path_len, coap_pkt->uri_path); - break; - case COAP_OPTION_URI_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_length, '&'); - PRINTF("Uri-Query [%.*s]\n", coap_pkt->uri_query_len, coap_pkt->uri_query); - break; - - case COAP_OPTION_LOCATION_PATH: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_path), &(coap_pkt->location_path_len), current_option, option_length, '/'); - PRINTF("Location-Path [%.*s]\n", coap_pkt->location_path_len, coap_pkt->location_path); - break; - case COAP_OPTION_LOCATION_QUERY: - /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ - coap_merge_multi_option( (char **) &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_length, '&'); - PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, coap_pkt->location_query); - break; - - case COAP_OPTION_PROXY_URI: - /*FIXME check for own end-point */ - coap_pkt->proxy_uri = (char *) current_option; - coap_pkt->proxy_uri_len = option_length; - /*TODO length > 270 not implemented (actually not required) */ - PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri); - coap_error_message = "This is a constrained server (Contiki)"; - return PROXYING_NOT_SUPPORTED_5_05; - break; - - case COAP_OPTION_OBSERVE: - coap_pkt->observe = coap_parse_int_option(current_option, option_length); - PRINTF("Observe [%lu]\n", coap_pkt->observe); - break; - case COAP_OPTION_BLOCK2: - coap_pkt->block2_num = coap_parse_int_option(current_option, option_length); - coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3; - coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07); - coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07); - coap_pkt->block2_num >>= 4; - PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); - break; - case COAP_OPTION_BLOCK1: - coap_pkt->block1_num = coap_parse_int_option(current_option, option_length); - coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3; - coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07); - coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07); - coap_pkt->block1_num >>= 4; - PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); - break; - case COAP_OPTION_SIZE: - coap_pkt->size = coap_parse_int_option(current_option, option_length); - PRINTF("Size [%lu]\n", coap_pkt->size); - break; - default: - PRINTF("unknown (%u)\n", option_number); - /* Check if critical (odd) */ - if (option_number & 1) - { - coap_error_message = "Unsupported critical option"; - return BAD_OPTION_4_02; - } - } - - current_option += option_length; - } /* for */ - PRINTF("-Done parsing-------\n"); - - - - return NO_ERROR; -} -/*-----------------------------------------------------------------------------------*/ -/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_query_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { - return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output); - } - return 0; -} - -int -coap_get_post_variable(void *packet, const char *name, const char **output) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload_len) { - return coap_get_variable((const char *)coap_pkt->payload, coap_pkt->payload_len, name, output); - } - return 0; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_set_status_code(void *packet, unsigned int code) -{ - if (code <= 0xFF) - { - ((coap_packet_t *)packet)->code = (uint8_t) code; - return 1; - } - else - { - return 0; - } -} -/*-----------------------------------------------------------------------------------*/ -/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -unsigned int -coap_get_header_content_type(void *packet) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1; - - return coap_pkt->content_type; -} - -int -coap_set_header_content_type(void *packet, unsigned int content_type) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->content_type = (coap_content_type_t) content_type; - SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_accept(void *packet, const uint16_t **accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0; - - *accept = coap_pkt->accept; - return coap_pkt->accept_num; -} - -int -coap_set_header_accept(void *packet, uint16_t accept) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) - { - coap_pkt->accept[coap_pkt->accept_num] = accept; - coap_pkt->accept_num += 1; - - SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT); - } - return coap_pkt->accept_num; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_max_age(void *packet, uint32_t *age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { - *age = COAP_DEFAULT_MAX_AGE; - } else { - *age = coap_pkt->max_age; - } - return 1; -} - -int -coap_set_header_max_age(void *packet, uint32_t age) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->max_age = age; - SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_etag(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0; - - *etag = coap_pkt->etag; - return coap_pkt->etag_len; -} - -int -coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->etag, etag, coap_pkt->etag_len); - - SET_OPTION(coap_pkt, COAP_OPTION_ETAG); - return coap_pkt->etag_len; -} -/*-----------------------------------------------------------------------------------*/ -/*FIXME support multiple ETags */ -int -coap_get_header_if_match(void *packet, const uint8_t **etag) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0; - - *etag = coap_pkt->if_match; - return coap_pkt->if_match_len; -} - -int -coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len); - memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len); - - SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH); - return coap_pkt->if_match_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_if_none_match(void *packet) -{ - return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; -} - -int -coap_set_header_if_none_match(void *packet) -{ - SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_token(void *packet, const uint8_t **token) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - *token = coap_pkt->token; - return coap_pkt->token_len; -} - -int -coap_set_header_token(void *packet, const uint8_t *token, size_t token_len) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len); - memcpy(coap_pkt->token, token, coap_pkt->token_len); - - return coap_pkt->token_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_proxy_uri(void *packet, const char **uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0; - - *uri = coap_pkt->proxy_uri; - return coap_pkt->proxy_uri_len; -} - -int -coap_set_header_proxy_uri(void *packet, const char *uri) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->proxy_uri = uri; - coap_pkt->proxy_uri_len = strlen(uri); - - SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI); - return coap_pkt->proxy_uri_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_host(void *packet, const char **host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0; - - *host = coap_pkt->uri_host; - return coap_pkt->uri_host_len; -} - -int -coap_set_header_uri_host(void *packet, const char *host) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->uri_host = host; - coap_pkt->uri_host_len = strlen(host); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST); - return coap_pkt->uri_host_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0; - - *path = coap_pkt->uri_path; - return coap_pkt->uri_path_len; -} - -int -coap_set_header_uri_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (path[0]=='/') ++path; - - coap_pkt->uri_path = path; - coap_pkt->uri_path_len = strlen(path); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); - return coap_pkt->uri_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_uri_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0; - - *query = coap_pkt->uri_query; - return coap_pkt->uri_query_len; -} - -int -coap_set_header_uri_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->uri_query = query; - coap_pkt->uri_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); - return coap_pkt->uri_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_path(void *packet, const char **path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0; - - *path = coap_pkt->location_path; - return coap_pkt->location_path_len; -} - -int -coap_set_header_location_path(void *packet, const char *path) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - char *query; - - while (path[0]=='/') ++path; - - if ((query = strchr(path, '?'))) - { - coap_set_header_location_query(packet, query+1); - coap_pkt->location_path_len = query - path; - } - else - { - coap_pkt->location_path_len = strlen(path); - } - - coap_pkt->location_path = path; - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH); - return coap_pkt->location_path_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_location_query(void *packet, const char **query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0; - - *query = coap_pkt->location_query; - return coap_pkt->location_query_len; -} - -int -coap_set_header_location_query(void *packet, const char *query) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - while (query[0]=='?') ++query; - - coap_pkt->location_query = query; - coap_pkt->location_query_len = strlen(query); - - SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY); - return coap_pkt->location_query_len; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_observe(void *packet, uint32_t *observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0; - - *observe = coap_pkt->observe; - return 1; -} - -int -coap_set_header_observe(void *packet, uint32_t observe) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->observe = 0x00FFFFFF & observe; - SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block2_num; - if (more!=NULL) *more = coap_pkt->block2_more; - if (size!=NULL) *size = coap_pkt->block2_size; - if (offset!=NULL) *offset = coap_pkt->block2_offset; - - return 1; -} - -int -coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block2_num = num; - coap_pkt->block2_more = more ? 1 : 0; - coap_pkt->block2_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0; - - /* pointers may be NULL to get only specific block parameters */ - if (num!=NULL) *num = coap_pkt->block1_num; - if (more!=NULL) *more = coap_pkt->block1_more; - if (size!=NULL) *size = coap_pkt->block1_size; - if (offset!=NULL) *offset = coap_pkt->block1_offset; - - return 1; -} - -int -coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (size<16) return 0; - if (size>2048) return 0; - if (num>0x0FFFFF) return 0; - - coap_pkt->block1_num = num; - coap_pkt->block1_more = more; - coap_pkt->block1_size = size; - - SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -int -coap_get_header_size(void *packet, uint32_t *size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (!IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) return 0; - - *size = coap_pkt->size; - return 1; -} - -int -coap_set_header_size(void *packet, uint32_t size) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - coap_pkt->size = size; - SET_OPTION(coap_pkt, COAP_OPTION_SIZE); - return 1; -} -/*-----------------------------------------------------------------------------------*/ -/*- PAYLOAD -------------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ -int -coap_get_payload(void *packet, const uint8_t **payload) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - if (coap_pkt->payload) { - *payload = coap_pkt->payload; - return coap_pkt->payload_len; - } else { - *payload = NULL; - return 0; - } -} - -int -coap_set_payload(void *packet, const void *payload, size_t length) -{ - coap_packet_t *const coap_pkt = (coap_packet_t *) packet; - - //PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE); - - coap_pkt->payload = (uint8_t *) payload; - coap_pkt->payload_len = MIN(REST_MAX_CHUNK_SIZE, length); - - return coap_pkt->payload_len; -} -/*-----------------------------------------------------------------------------------*/ diff --git a/apps/er-coap-13/er-coap-13.h b/apps/er-coap-13/er-coap-13.h deleted file mode 100644 index 734361e95..000000000 --- a/apps/er-coap-13/er-coap-13.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An implementation of the Constrained Application Protocol (draft 12) - * \author - * Matthias Kovatsch - */ - -#ifndef COAP_13_H_ -#define COAP_13_H_ - -#include /* for size_t */ -#include "contiki-net.h" -#include "erbium.h" - -#define COAP_LINK_FORMAT_FILTERING 1 - -#define COAP_DEFAULT_PORT 5683 - -#ifndef COAP_SERVER_PORT -#define COAP_SERVER_PORT COAP_DEFAULT_PORT -#endif - -#define COAP_DEFAULT_MAX_AGE 60 -#define COAP_RESPONSE_TIMEOUT 2 -#define COAP_RESPONSE_RANDOM_FACTOR 1.5 -#define COAP_MAX_RETRANSMIT 4 - -#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */ -#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ -#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ -#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */ - -#define COAP_HEADER_VERSION_MASK 0xC0 -#define COAP_HEADER_VERSION_POSITION 6 -#define COAP_HEADER_TYPE_MASK 0x30 -#define COAP_HEADER_TYPE_POSITION 4 -#define COAP_HEADER_TOKEN_LEN_MASK 0x0F -#define COAP_HEADER_TOKEN_LEN_POSITION 0 - -#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 -#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F - -/* - * Conservative size limit, as not all options have to be set at the same time. - */ -#ifndef COAP_MAX_HEADER_SIZE -/* Hdr CoT Age Tag Obs Tok Blo strings */ -#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */ -#endif /* COAP_MAX_HEADER_SIZE */ - -#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) -/* 0/14 48 for IPv6 (28 for IPv4) */ -#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN) -#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" -#endif - -/* - * Maximum number of failed request attempts before action - */ -#ifndef COAP_MAX_ATTEMPTS -#define COAP_MAX_ATTEMPTS 4 -#endif /* COAP_MAX_ATTEMPTS */ - -#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) -#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) - -/* Bitmap for set options */ -enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 }; -#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE)) -#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE))) - -#ifndef MIN -#define MIN(a, b) ((a) < (b)? (a) : (b)) -#endif /* MIN */ - -/* CoAP message types */ -typedef enum { - COAP_TYPE_CON, /* confirmables */ - COAP_TYPE_NON, /* non-confirmables */ - COAP_TYPE_ACK, /* acknowledgements */ - COAP_TYPE_RST /* reset */ -} coap_message_type_t; - -/* CoAP request method codes */ -typedef enum { - COAP_GET = 1, - COAP_POST, - COAP_PUT, - COAP_DELETE -} coap_method_t; - -/* CoAP response codes */ -typedef enum { - NO_ERROR = 0, - - CREATED_2_01 = 65, /* CREATED */ - DELETED_2_02 = 66, /* DELETED */ - VALID_2_03 = 67, /* NOT_MODIFIED */ - CHANGED_2_04 = 68, /* CHANGED */ - CONTENT_2_05 = 69, /* OK */ - - BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ - UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ - BAD_OPTION_4_02 = 130, /* BAD_OPTION */ - FORBIDDEN_4_03 = 131, /* FORBIDDEN */ - NOT_FOUND_4_04 = 132, /* NOT_FOUND */ - METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ - NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ - PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ - REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ - UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ - - INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ - NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ - BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ - SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ - GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ - PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - - /* Erbium errors */ - MEMORY_ALLOCATION_ERROR = 192, - PACKET_SERIALIZATION_ERROR, - - /* Erbium hooks */ - MANUAL_RESPONSE, - PING_RESPONSE - -} coap_status_t; - -/* CoAP header options */ -typedef enum { - COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ - COAP_OPTION_URI_HOST = 3, /* 1-255 B */ - COAP_OPTION_ETAG = 4, /* 1-8 B */ - COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ - COAP_OPTION_OBSERVE = 6, /* 0-3 B */ - COAP_OPTION_URI_PORT = 7, /* 0-2 B */ - COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ - COAP_OPTION_URI_PATH = 11, /* 0-255 B */ - COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */ - COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ - COAP_OPTION_URI_QUERY = 15, /* 0-270 B */ - COAP_OPTION_ACCEPT = 16, /* 0-2 B */ - COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */ - COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ - COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ - COAP_OPTION_SIZE = 28, /* 0-4 B */ - COAP_OPTION_PROXY_URI = 35, /* 1-270 B */ -} coap_option_t; - -/* CoAP Content-Types */ -typedef enum { - TEXT_PLAIN = 0, - TEXT_XML = 1, /* Indented types are not in the initial registry. */ - TEXT_CSV = 2, - TEXT_HTML = 3, - IMAGE_GIF = 21, - IMAGE_JPEG = 22, - IMAGE_PNG = 23, - IMAGE_TIFF = 24, - AUDIO_RAW = 25, - VIDEO_RAW = 26, - APPLICATION_LINK_FORMAT = 40, - APPLICATION_XML = 41, - APPLICATION_OCTET_STREAM = 42, - APPLICATION_RDF_XML = 43, - APPLICATION_SOAP_XML = 44, - APPLICATION_ATOM_XML = 45, - APPLICATION_XMPP_XML = 46, - APPLICATION_EXI = 47, - APPLICATION_FASTINFOSET = 48, - APPLICATION_SOAP_FASTINFOSET = 49, - APPLICATION_JSON = 50, - APPLICATION_X_OBIX_BINARY = 51 -} coap_content_type_t; - -/* Parsed message struct */ -typedef struct { - uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */ - - uint8_t version; - coap_message_type_t type; - uint8_t code; - uint16_t mid; - - uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */ - - coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */ - uint32_t max_age; - size_t proxy_uri_len; - const char *proxy_uri; - uint8_t etag_len; - uint8_t etag[COAP_ETAG_LEN]; - size_t uri_host_len; - const char *uri_host; - size_t location_path_len; - const char *location_path; - uint16_t uri_port; - size_t location_query_len; - const char *location_query; - size_t uri_path_len; - const char *uri_path; - uint16_t observe; - uint8_t token_len; - uint8_t token[COAP_TOKEN_LEN]; - uint8_t accept_num; - uint16_t accept[COAP_MAX_ACCEPT_NUM]; - uint8_t if_match_len; - uint8_t if_match[COAP_ETAG_LEN]; - uint32_t block2_num; - uint8_t block2_more; - uint16_t block2_size; - uint32_t block2_offset; - uint32_t block1_num; - uint8_t block1_more; - uint16_t block1_size; - uint32_t block1_offset; - uint32_t size; - size_t uri_query_len; - const char *uri_query; - uint8_t if_none_match; - - uint16_t payload_len; - uint8_t *payload; - -} coap_packet_t; - -/* Option format serialization*/ -#define COAP_SERIALIZE_INT_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" [%u]\n", coap_pkt->field); \ - option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \ - current_number = number; \ - } -#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \ - coap_pkt->field[0], \ - coap_pkt->field[1], \ - coap_pkt->field[2], \ - coap_pkt->field[3], \ - coap_pkt->field[4], \ - coap_pkt->field[5], \ - coap_pkt->field[6], \ - coap_pkt->field[7] \ - ); /*FIXME always prints 8 bytes */ \ - option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \ - current_number = number; \ - } -#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \ - option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, splitter); \ - current_number = number; \ - } -#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) { \ - int i; \ - for (i=0; ifield##_num; ++i) \ - { \ - PRINTF(text" [%u]\n", coap_pkt->field[i]); \ - option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \ - current_number = number; \ - } \ - } -#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \ - if (IS_OPTION(coap_pkt, number)) \ - { \ - PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \ - uint32_t block = coap_pkt->field##_num << 4; \ - if (coap_pkt->field##_more) block |= 0x8; \ - block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \ - PRINTF(text" encoded: 0x%lX\n", block); \ - option += coap_serialize_int_option(number, current_number, option, block); \ - current_number = number; \ - } - -/* To store error code and human-readable payload */ -extern coap_status_t coap_error_code; -extern char *coap_error_message; - -void coap_init_connection(uint16_t port); -uint16_t coap_get_mid(void); - -void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid); -size_t coap_serialize_message(void *packet, uint8_t *buffer); -void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, uint16_t length); -coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len); - -int coap_get_query_variable(void *packet, const char *name, const char **output); -int coap_get_post_variable(void *packet, const char *name, const char **output); - -/*-----------------------------------------------------------------------------------*/ - -int coap_set_status_code(void *packet, unsigned int code); - -unsigned int coap_get_header_content_type(void *packet); -int coap_set_header_content_type(void *packet, unsigned int content_type); - -int coap_get_header_accept(void *packet, const uint16_t **accept); -int coap_set_header_accept(void *packet, uint16_t accept); - -int coap_get_header_max_age(void *packet, uint32_t *age); -int coap_set_header_max_age(void *packet, uint32_t age); - -int coap_get_header_etag(void *packet, const uint8_t **etag); -int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len); - -int coap_get_header_if_match(void *packet, const uint8_t **etag); -int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len); - -int coap_get_header_if_none_match(void *packet); -int coap_set_header_if_none_match(void *packet); - -int coap_get_header_token(void *packet, const uint8_t **token); -int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len); - -int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */ -int coap_set_header_proxy_uri(void *packet, const char *uri); - -int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_host(void *packet, const char *host); - -int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_path(void *packet, const char *path); - -int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ -int coap_set_header_uri_query(void *packet, const char *query); - -int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ -int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */ - -int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ -int coap_set_header_location_query(void *packet, const char *query); - -int coap_get_header_observe(void *packet, uint32_t *observe); -int coap_set_header_observe(void *packet, uint32_t observe); - -int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); -int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size); - -int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); -int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size); - -int coap_get_header_size(void *packet, uint32_t *size); -int coap_set_header_size(void *packet, uint32_t size); - -int coap_get_payload(void *packet, const uint8_t **payload); -int coap_set_payload(void *packet, const void *payload, size_t length); - -#endif /* COAP_13_H_ */ diff --git a/apps/er-coap/Makefile.er-coap b/apps/er-coap/Makefile.er-coap new file mode 100755 index 000000000..096b10e2e --- /dev/null +++ b/apps/er-coap/Makefile.er-coap @@ -0,0 +1,4 @@ +er-coap_src = er-coap.c er-coap-engine.c er-coap-transactions.c er-coap-observe.c er-coap-separate.c er-coap-res-well-known-core.c er-coap-block1.c + +# Erbium will implement the REST Engine +CFLAGS += -DREST=coap_rest_implementation diff --git a/apps/er-coap/er-coap-block1.c b/apps/er-coap/er-coap-block1.c new file mode 100644 index 000000000..2f895b5fa --- /dev/null +++ b/apps/er-coap/er-coap-block1.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, Lars Schmertmann . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * CoAP module for block 1 handling + * \author + * Lars Schmertmann + */ + +#include +#include + +#include "er-coap.h" +#include "er-coap-block1.h" + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +/*----------------------------------------------------------------------------*/ + +/** + * \brief Block 1 support within a coap-ressource + * + * This function will help you to use block 1. If target is null + * error handling and response configuration is active. On return + * value 0, the last block was recived, while on return value 1 + * more blocks will follow. With target, len and maxlen this + * function will assemble the blocks. + * + * You can find an example in: + * examples/er-rest-example/resources/res-b1-sep-b2.c + * + * \param request Request pointer from the handler + * \param response Response pointer from the handler + * \param target Pointer to the buffer where the request payload can be assembled + * \param len Pointer to the variable, where the function stores the actual length + * \param max_len Length of the "target"-Buffer + * + * \return 0 if initialisation was successful + * -1 if initialisation failed + */ +int +coap_block1_handler(void *request, void *response, uint8_t *target, size_t *len, size_t max_len) +{ + const uint8_t *payload = 0; + int pay_len = REST.get_request_payload(request, &payload); + + if(!pay_len || !payload) { + erbium_status_code = REST.status.BAD_REQUEST; + coap_error_message = "NoPayload"; + return -1; + } + + coap_packet_t *packet = (coap_packet_t *)request; + + if(packet->block1_offset + pay_len > max_len) { + erbium_status_code = REST.status.REQUEST_ENTITY_TOO_LARGE; + coap_error_message = "Message to big"; + return -1; + } + + if(target && len) { + memcpy(target + packet->block1_offset, payload, pay_len); + *len = packet->block1_offset + pay_len; + } + + if(IS_OPTION(packet, COAP_OPTION_BLOCK1)) { + PRINTF("Blockwise: block 1 request: Num: %u, More: %u, Size: %u, Offset: %u\n", + packet->block1_num, + packet->block1_more, + packet->block1_size, + packet->block1_offset); + + coap_set_header_block1(response, packet->block1_num, packet->block1_more, packet->block1_size); + if(packet->block1_more) { + coap_set_status_code(response, CONTINUE_2_31); + return 1; + } + } + + return 0; +} diff --git a/apps/er-coap-07/er-coap-07-transactions.h b/apps/er-coap/er-coap-block1.h similarity index 54% rename from apps/er-coap-07/er-coap-07-transactions.h rename to apps/er-coap/er-coap-block1.h index 0301ec13d..a6b1de5ab 100644 --- a/apps/er-coap-07/er-coap-07-transactions.h +++ b/apps/er-coap/er-coap-block1.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2014, Lars Schmertmann . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,48 +31,17 @@ /** * \file - * CoAP module for reliable transport + * CoAP module for block 1 handling * \author - * Matthias Kovatsch + * Lars Schmertmann */ -#ifndef COAP_TRANSACTIONS_H_ -#define COAP_TRANSACTIONS_H_ +#ifndef COAP_BLOCK1_H_ +#define COAP_BLOCK1_H_ -#include "er-coap-07.h" +#include +#include -/* - * The number of concurrent messages that can be stored for retransmission in the transaction layer. - */ -#ifndef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 4 -#endif /* COAP_MAX_OPEN_TRANSACTIONS */ +int coap_block1_handler(void *request, void *response, uint8_t *target, size_t *len, size_t max_len); -/* container for transactions with message buffer and retransmission info */ -typedef struct coap_transaction { - struct coap_transaction *next; /* for LIST */ - - uint16_t mid; - struct etimer retrans_timer; - uint8_t retrans_counter; - - uip_ipaddr_t addr; - uint16_t port; - - restful_response_handler callback; - void *callback_data; - - uint16_t packet_len; - uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */ -} coap_transaction_t; - -void coap_register_as_transaction_handler(); - -coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port); -void coap_send_transaction(coap_transaction_t *t); -void coap_clear_transaction(coap_transaction_t *t); -coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid); - -void coap_check_transactions(); - -#endif /* COAP_TRANSACTIONS_H_ */ +#endif /* COAP_BLOCK1_H_ */ diff --git a/apps/er-coap-12/er-coap-12-transactions.h b/apps/er-coap/er-coap-conf.h similarity index 54% rename from apps/er-coap-12/er-coap-12-transactions.h rename to apps/er-coap/er-coap-conf.h index b2a748cda..00d13d7c5 100644 --- a/apps/er-coap-12/er-coap-12-transactions.h +++ b/apps/er-coap/er-coap-conf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,48 +31,44 @@ /** * \file - * CoAP module for reliable transport + * Collection of default configuration values. * \author * Matthias Kovatsch */ -#ifndef COAP_TRANSACTIONS_H_ -#define COAP_TRANSACTIONS_H_ +#ifndef ER_COAP_CONF_H_ +#define ER_COAP_CONF_H_ -#include "er-coap-12.h" +/* Features that can be disabled to achieve smaller memory footprint */ +#define COAP_LINK_FORMAT_FILTERING 0 +#define COAP_PROXY_OPTION_PROCESSING 0 -/* - * The number of concurrent messages that can be stored for retransmission in the transaction layer. - */ +/* Listening port for the CoAP REST Engine */ +#ifndef COAP_SERVER_PORT +#define COAP_SERVER_PORT COAP_DEFAULT_PORT +#endif + +/* The number of concurrent messages that can be stored for retransmission in the transaction layer. */ #ifndef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 4 +#define COAP_MAX_OPEN_TRANSACTIONS 4 #endif /* COAP_MAX_OPEN_TRANSACTIONS */ -/* container for transactions with message buffer and retransmission info */ -typedef struct coap_transaction { - struct coap_transaction *next; /* for LIST */ +/* Maximum number of failed request attempts before action */ +#ifndef COAP_MAX_ATTEMPTS +#define COAP_MAX_ATTEMPTS 4 +#endif /* COAP_MAX_ATTEMPTS */ - uint16_t mid; - struct etimer retrans_timer; - uint8_t retrans_counter; +/* Conservative size limit, as not all options have to be set at the same time. Check when Proxy-Uri option is used */ +#ifndef COAP_MAX_HEADER_SIZE /* Hdr CoF If-Match Obs Blo strings */ +#define COAP_MAX_HEADER_SIZE (4 + COAP_TOKEN_LEN + 3 + 1 + COAP_ETAG_LEN + 4 + 4 + 30) /* 65 */ +#endif /* COAP_MAX_HEADER_SIZE */ - uip_ipaddr_t addr; - uint16_t port; +/* Number of observer slots (each takes abot xxx bytes) */ +#ifndef COAP_MAX_OBSERVERS +#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS - 1 +#endif /* COAP_MAX_OBSERVERS */ - restful_response_handler callback; - void *callback_data; +/* Interval in notifies in which NON notifies are changed to CON notifies to check client. */ +#define COAP_OBSERVE_REFRESH_INTERVAL 20 - uint16_t packet_len; - uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */ -} coap_transaction_t; - -void coap_register_as_transaction_handler(); - -coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port); -void coap_send_transaction(coap_transaction_t *t); -void coap_clear_transaction(coap_transaction_t *t); -coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid); - -void coap_check_transactions(); - -#endif /* COAP_TRANSACTIONS_H_ */ +#endif /* ER_COAP_CONF_H_ */ diff --git a/apps/er-coap/er-coap-constants.h b/apps/er-coap/er-coap-constants.h new file mode 100644 index 000000000..cfa73abcf --- /dev/null +++ b/apps/er-coap/er-coap-constants.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Collection of constants specified in the CoAP standard. + * \author + * Matthias Kovatsch + */ + +#ifndef ER_COAP_CONSTANTS_H_ +#define ER_COAP_CONSTANTS_H_ + +#define COAP_DEFAULT_PORT 5683 + +#define COAP_DEFAULT_MAX_AGE 60 +#define COAP_RESPONSE_TIMEOUT 3 +#define COAP_RESPONSE_RANDOM_FACTOR 1.5 +#define COAP_MAX_RETRANSMIT 4 + +#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */ +#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ +#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ + +#define COAP_HEADER_VERSION_MASK 0xC0 +#define COAP_HEADER_VERSION_POSITION 6 +#define COAP_HEADER_TYPE_MASK 0x30 +#define COAP_HEADER_TYPE_POSITION 4 +#define COAP_HEADER_TOKEN_LEN_MASK 0x0F +#define COAP_HEADER_TOKEN_LEN_POSITION 0 + +#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 +#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F + +/* CoAP message types */ +typedef enum { + COAP_TYPE_CON, /* confirmables */ + COAP_TYPE_NON, /* non-confirmables */ + COAP_TYPE_ACK, /* acknowledgements */ + COAP_TYPE_RST /* reset */ +} coap_message_type_t; + +/* CoAP request method codes */ +typedef enum { + COAP_GET = 1, + COAP_POST, + COAP_PUT, + COAP_DELETE +} coap_method_t; + +/* CoAP response codes */ +typedef enum { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, + PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, + PING_RESPONSE +} coap_status_t; + +/* CoAP header option numbers */ +typedef enum { + COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ + COAP_OPTION_URI_HOST = 3, /* 1-255 B */ + COAP_OPTION_ETAG = 4, /* 1-8 B */ + COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ + COAP_OPTION_OBSERVE = 6, /* 0-3 B */ + COAP_OPTION_URI_PORT = 7, /* 0-2 B */ + COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ + COAP_OPTION_URI_PATH = 11, /* 0-255 B */ + COAP_OPTION_CONTENT_FORMAT = 12, /* 0-2 B */ + COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ + COAP_OPTION_URI_QUERY = 15, /* 0-255 B */ + COAP_OPTION_ACCEPT = 17, /* 0-2 B */ + COAP_OPTION_LOCATION_QUERY = 20, /* 0-255 B */ + COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ + COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ + COAP_OPTION_SIZE2 = 28, /* 0-4 B */ + COAP_OPTION_PROXY_URI = 35, /* 1-1034 B */ + COAP_OPTION_PROXY_SCHEME = 39, /* 1-255 B */ + COAP_OPTION_SIZE1 = 60, /* 0-4 B */ +} coap_option_t; + +/* CoAP Content-Formats */ +typedef enum { + TEXT_PLAIN = 0, + TEXT_XML = 1, + TEXT_CSV = 2, + TEXT_HTML = 3, + IMAGE_GIF = 21, + IMAGE_JPEG = 22, + IMAGE_PNG = 23, + IMAGE_TIFF = 24, + AUDIO_RAW = 25, + VIDEO_RAW = 26, + APPLICATION_LINK_FORMAT = 40, + APPLICATION_XML = 41, + APPLICATION_OCTET_STREAM = 42, + APPLICATION_RDF_XML = 43, + APPLICATION_SOAP_XML = 44, + APPLICATION_ATOM_XML = 45, + APPLICATION_XMPP_XML = 46, + APPLICATION_EXI = 47, + APPLICATION_FASTINFOSET = 48, + APPLICATION_SOAP_FASTINFOSET = 49, + APPLICATION_JSON = 50, + APPLICATION_X_OBIX_BINARY = 51 +} coap_content_format_t; + +#endif /* ER_COAP_CONSTANTS_H_ */ diff --git a/apps/er-coap/er-coap-engine.c b/apps/er-coap/er-coap-engine.c new file mode 100644 index 000000000..3a7af5e5f --- /dev/null +++ b/apps/er-coap/er-coap-engine.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * CoAP implementation for the REST Engine. + * \author + * Matthias Kovatsch + */ + +#include +#include +#include +#include "er-coap-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +PROCESS(coap_engine, "CoAP Engine"); + +/*---------------------------------------------------------------------------*/ +/*- Variables ---------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +static service_callback_t service_cbk = NULL; + +/*---------------------------------------------------------------------------*/ +/*- Internal API ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +static int +coap_receive(void) +{ + erbium_status_code = NO_ERROR; + + PRINTF("handle_incoming_data(): received uip_datalen=%u \n", + (uint16_t)uip_datalen()); + + /* static declaration reduces stack peaks and program code size */ + static coap_packet_t message[1]; /* this way the packet can be treated as pointer as usual */ + static coap_packet_t response[1]; + static coap_transaction_t *transaction = NULL; + + if(uip_newdata()) { + + PRINTF("receiving UDP datagram from: "); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF(":%u\n Length: %u\n", uip_ntohs(UIP_UDP_BUF->srcport), + uip_datalen()); + + erbium_status_code = + coap_parse_message(message, uip_appdata, uip_datalen()); + + if(erbium_status_code == NO_ERROR) { + + /*TODO duplicates suppression, if required by application */ + + PRINTF(" Parsed: v %u, t %u, tkl %u, c %u, mid %u\n", message->version, + message->type, message->token_len, message->code, message->mid); + PRINTF(" URL: %.*s\n", message->uri_path_len, message->uri_path); + PRINTF(" Payload: %.*s\n", message->payload_len, message->payload); + + /* handle requests */ + if(message->code >= COAP_GET && message->code <= COAP_DELETE) { + + /* use transaction buffer for response to confirmable request */ + if((transaction = + coap_new_transaction(message->mid, &UIP_IP_BUF->srcipaddr, + UIP_UDP_BUF->srcport))) { + 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) { + /* reliable CON requests are answered with an ACK */ + coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05, + message->mid); + } else { + /* unreliable NON requests are answered with a NON as well */ + coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, + coap_get_mid()); + /* mirror token */ + } if(message->token_len) { + coap_set_token(response, message->token, message->token_len); + /* get offset for blockwise transfers */ + } + if(coap_get_header_block2 + (message, &block_num, NULL, &block_size, &block_offset)) { + PRINTF("Blockwise: block request %lu (%u/%u) @ %lu bytes\n", + block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); + block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); + new_offset = block_offset; + } + + /* invoke resource handler */ + if(service_cbk) { + + /* call REST framework and check if found and allowed */ + if(service_cbk + (message, response, transaction->packet + COAP_MAX_HEADER_SIZE, + block_size, &new_offset)) { + + if(erbium_status_code == NO_ERROR) { + + /* TODO coap_handle_blockwise(request, response, start_offset, end_offset); */ + + /* resource is unaware of Block1 */ + if(IS_OPTION(message, COAP_OPTION_BLOCK1) + && response->code < BAD_REQUEST_4_00 + && !IS_OPTION(response, COAP_OPTION_BLOCK1)) { + PRINTF("Block1 NOT IMPLEMENTED\n"); + + erbium_status_code = NOT_IMPLEMENTED_5_01; + coap_error_message = "NoBlock1Support"; + + /* client requested Block2 transfer */ + } else if(IS_OPTION(message, COAP_OPTION_BLOCK2)) { + + /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ + if(new_offset == block_offset) { + 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 */ + } 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 */ + } else { + 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) */ + + /* Resource requested Block2 transfer */ + } else if(new_offset != 0) { + PRINTF + ("Blockwise: no block option for blockwise resource, using block size %u\n", + COAP_MAX_BLOCK_SIZE); + + coap_set_header_block2(response, 0, new_offset != -1, + COAP_MAX_BLOCK_SIZE); + coap_set_payload(response, response->payload, + MIN(response->payload_len, + COAP_MAX_BLOCK_SIZE)); + } /* blockwise transfer handling */ + } /* no errors/hooks */ + /* successful service callback */ + /* serialize response */ + } + if(erbium_status_code == NO_ERROR) { + if((transaction->packet_len = coap_serialize_message(response, + transaction-> + packet)) == + 0) { + erbium_status_code = PACKET_SERIALIZATION_ERROR; + } + } + } else { + erbium_status_code = NOT_IMPLEMENTED_5_01; + coap_error_message = "NoServiceCallbck"; /* no 'a' to fit into 16 bytes */ + } /* if(service callback) */ + } else { + erbium_status_code = SERVICE_UNAVAILABLE_5_03; + coap_error_message = "NoFreeTraBuffer"; + } /* if(transaction buffer) */ + + /* handle responses */ + } else { + + if(message->type == COAP_TYPE_CON && message->code == 0) { + PRINTF("Received Ping\n"); + erbium_status_code = PING_RESPONSE; + } else if(message->type == COAP_TYPE_ACK) { + /* transactions are closed through lookup below */ + PRINTF("Received ACK\n"); + } else if(message->type == COAP_TYPE_RST) { + PRINTF("Received RST\n"); + /* cancel possible subscriptions */ + coap_remove_observer_by_mid(&UIP_IP_BUF->srcipaddr, + UIP_UDP_BUF->srcport, message->mid); + } + + if((transaction = coap_get_transaction_by_mid(message->mid))) { + /* free transaction memory before callback, as it may create a new transaction */ + restful_response_handler callback = transaction->callback; + void *callback_data = transaction->callback_data; + + coap_clear_transaction(transaction); + + /* check if someone registered for the response */ + if(callback) { + callback(callback_data, message); + } + } + /* if(ACKed transaction) */ + transaction = NULL; + } /* request or response */ + } /* parsed correctly */ + + /* if(parsed correctly) */ + if(erbium_status_code == NO_ERROR) { + if(transaction) { + coap_send_transaction(transaction); + } + } else if(erbium_status_code == MANUAL_RESPONSE) { + PRINTF("Clearing transaction for manual response"); + coap_clear_transaction(transaction); + } else { + coap_message_type_t reply_type = COAP_TYPE_ACK; + + PRINTF("ERROR %u: %s\n", erbium_status_code, coap_error_message); + coap_clear_transaction(transaction); + + if(erbium_status_code == PING_RESPONSE) { + erbium_status_code = 0; + reply_type = COAP_TYPE_RST; + } else if(erbium_status_code >= 192) { + /* set to sendable error code */ + erbium_status_code = INTERNAL_SERVER_ERROR_5_00; + /* reuse input buffer for error message */ + } + coap_init_message(message, reply_type, erbium_status_code, + message->mid); + coap_set_payload(message, coap_error_message, + strlen(coap_error_message)); + coap_send_message(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, + uip_appdata, coap_serialize_message(message, + uip_appdata)); + } + } + + /* if(new data) */ + return erbium_status_code; +} +/*---------------------------------------------------------------------------*/ +void +coap_init_engine(void) +{ + process_start(&coap_engine, NULL); +} +/*---------------------------------------------------------------------------*/ +void +coap_set_service_callback(service_callback_t callback) +{ + service_cbk = callback; +} +/*---------------------------------------------------------------------------*/ +rest_resource_flags_t +coap_get_rest_method(void *packet) +{ + return (rest_resource_flags_t)(1 << + (((coap_packet_t *)packet)->code - 1)); +} +/*---------------------------------------------------------------------------*/ +/*- Server Part -------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ + +/* the discover resource is automatically included for CoAP */ +extern resource_t res_well_known_core; +#ifdef WITH_DTLS +extern resource_t res_dtls; +#endif + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(coap_engine, ev, data) +{ + PROCESS_BEGIN(); + PRINTF("Starting %s receiver...\n", coap_rest_implementation.name); + + rest_activate_resource(&res_well_known_core, ".well-known/core"); + + coap_register_as_transaction_handler(); + coap_init_connection(SERVER_LISTEN_PORT); + + while(1) { + PROCESS_YIELD(); + + if(ev == tcpip_event) { + coap_receive(); + } else if(ev == PROCESS_EVENT_TIMER) { + /* retransmissions are handled here */ + coap_check_transactions(); + } + } /* while (1) */ + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/*- Client Part -------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void +coap_blocking_request_callback(void *callback_data, void *response) +{ + struct request_state_t *state = (struct request_state_t *)callback_data; + + state->response = (coap_packet_t *)response; + process_poll(state->process); +} +/*---------------------------------------------------------------------------*/ +PT_THREAD(coap_blocking_request + (struct request_state_t *state, process_event_t ev, + uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, + coap_packet_t *request, + blocking_response_handler request_callback)) +{ + PT_BEGIN(&state->pt); + + static uint8_t more; + static uint32_t res_block; + static uint8_t block_error; + + state->block_num = 0; + state->response = NULL; + state->process = PROCESS_CURRENT(); + + more = 0; + res_block = 0; + block_error = 0; + + do { + request->mid = coap_get_mid(); + if((state->transaction = coap_new_transaction(request->mid, remote_ipaddr, + remote_port))) { + state->transaction->callback = coap_blocking_request_callback; + state->transaction->callback_data = state; + + if(state->block_num > 0) { + coap_set_header_block2(request, state->block_num, 0, + REST_MAX_CHUNK_SIZE); + } + state->transaction->packet_len = coap_serialize_message(request, + state-> + transaction-> + packet); + + coap_send_transaction(state->transaction); + PRINTF("Requested #%lu (MID %u)\n", state->block_num, request->mid); + + PT_YIELD_UNTIL(&state->pt, ev == PROCESS_EVENT_POLL); + + if(!state->response) { + PRINTF("Server not responding\n"); + PT_EXIT(&state->pt); + } + + coap_get_header_block2(state->response, &res_block, &more, NULL, NULL); + + PRINTF("Received #%lu%s (%u bytes)\n", res_block, more ? "+" : "", + state->response->payload_len); + + if(res_block == state->block_num) { + request_callback(state->response); + ++(state->block_num); + } else { + PRINTF("WRONG BLOCK %lu/%lu\n", res_block, state->block_num); + ++block_error; + } + } else { + PRINTF("Could not allocate transaction buffer"); + PT_EXIT(&state->pt); + } + } while(more && block_error < COAP_MAX_ATTEMPTS); + + PT_END(&state->pt); +} +/*---------------------------------------------------------------------------*/ +/*- REST Engine Interface ---------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +const struct rest_implementation coap_rest_implementation = { + "CoAP-18", + + coap_init_engine, + coap_set_service_callback, + + coap_get_header_uri_path, + coap_get_rest_method, + coap_set_status_code, + + coap_get_header_content_format, + coap_set_header_content_format, + coap_get_header_accept, + coap_get_header_size2, + coap_set_header_size2, + coap_get_header_max_age, + coap_set_header_max_age, + coap_set_header_etag, + coap_get_header_if_match, + coap_get_header_if_none_match, + coap_get_header_uri_host, + coap_set_header_location_path, + + coap_get_payload, + coap_set_payload, + + coap_get_header_uri_query, + coap_get_query_variable, + coap_get_post_variable, + + coap_notify_observers, + coap_observe_handler, + + { + CONTENT_2_05, + CREATED_2_01, + CHANGED_2_04, + DELETED_2_02, + VALID_2_03, + BAD_REQUEST_4_00, + UNAUTHORIZED_4_01, + BAD_OPTION_4_02, + FORBIDDEN_4_03, + NOT_FOUND_4_04, + METHOD_NOT_ALLOWED_4_05, + NOT_ACCEPTABLE_4_06, + REQUEST_ENTITY_TOO_LARGE_4_13, + UNSUPPORTED_MEDIA_TYPE_4_15, + INTERNAL_SERVER_ERROR_5_00, + NOT_IMPLEMENTED_5_01, + BAD_GATEWAY_5_02, + SERVICE_UNAVAILABLE_5_03, + GATEWAY_TIMEOUT_5_04, + PROXYING_NOT_SUPPORTED_5_05 + }, + + { + TEXT_PLAIN, + TEXT_XML, + TEXT_CSV, + TEXT_HTML, + IMAGE_GIF, + IMAGE_JPEG, + IMAGE_PNG, + IMAGE_TIFF, + AUDIO_RAW, + VIDEO_RAW, + APPLICATION_LINK_FORMAT, + APPLICATION_XML, + APPLICATION_OCTET_STREAM, + APPLICATION_RDF_XML, + APPLICATION_SOAP_XML, + APPLICATION_ATOM_XML, + APPLICATION_XMPP_XML, + APPLICATION_EXI, + APPLICATION_FASTINFOSET, + APPLICATION_SOAP_FASTINFOSET, + APPLICATION_JSON, + APPLICATION_X_OBIX_BINARY + } +}; +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap-03/er-coap-03-engine.h b/apps/er-coap/er-coap-engine.h similarity index 64% rename from apps/er-coap-03/er-coap-03-engine.h rename to apps/er-coap/er-coap-engine.h index 3d8ab7afd..d77d1fa78 100644 --- a/apps/er-coap-03/er-coap-03-engine.h +++ b/apps/er-coap/er-coap-engine.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,61 +31,55 @@ /** * \file - * CoAP implementation of the REST Engine + * CoAP implementation for the REST Engine. * \author * Matthias Kovatsch */ -#ifndef COAP_SERVER_H_ -#define COAP_SERVER_H_ - -#if !defined(REST) -#error "Define REST to \"coap_rest_implementation\"" -#endif - -#include "er-coap-03.h" -#include "er-coap-03-transactions.h" -#include "er-coap-03-observing.h" +#ifndef ER_COAP_ENGINE_H_ +#define ER_COAP_ENGINE_H_ #include "pt.h" - -/* Declare server process */ -PROCESS_NAME(coap_receiver); +#include "er-coap.h" +#include "er-coap-transactions.h" +#include "er-coap-observe.h" +#include "er-coap-separate.h" #define SERVER_LISTEN_PORT UIP_HTONS(COAP_SERVER_PORT) typedef coap_packet_t rest_request_t; typedef coap_packet_t rest_response_t; -extern const struct rest_implementation coap_rest_implementation; +void coap_init_engine(void); -void coap_receiver_init(void); - -/*-----------------------------------------------------------------------------------*/ -/*- Client part ---------------------------------------------------------------------*/ -/*-----------------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +/*- Client Part -------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ struct request_state_t { - struct pt pt; - struct process *process; - coap_transaction_t *transaction; - coap_packet_t *response; - uint32_t block_num; + struct pt pt; + struct process *process; + coap_transaction_t *transaction; + coap_packet_t *response; + uint32_t block_num; }; -typedef void (*blocking_response_handler) (void* response); +typedef void (*blocking_response_handler)(void *response); -PT_THREAD(coap_blocking_request(struct request_state_t *state, process_event_t ev, - uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, - coap_packet_t *request, - blocking_response_handler request_callback)); +PT_THREAD(coap_blocking_request + (struct request_state_t *state, process_event_t ev, + uip_ipaddr_t *remote_ipaddr, uint16_t remote_port, + coap_packet_t *request, + blocking_response_handler request_callback)); #define COAP_BLOCKING_REQUEST(server_addr, server_port, request, chunk_handler) \ -static struct request_state_t request_state; \ -PT_SPAWN(process_pt, &request_state.pt, \ + { \ + static struct request_state_t request_state; \ + PT_SPAWN(process_pt, &request_state.pt, \ coap_blocking_request(&request_state, ev, \ server_addr, server_port, \ request, chunk_handler) \ - ); -/*-----------------------------------------------------------------------------------*/ + ); \ + } +/*---------------------------------------------------------------------------*/ -#endif /* COAP_SERVER_H_ */ +#endif /* ER_COAP_ENGINE_H_ */ diff --git a/apps/er-coap/er-coap-observe.c b/apps/er-coap/er-coap-observe.c new file mode 100644 index 000000000..28ef770f8 --- /dev/null +++ b/apps/er-coap/er-coap-observe.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * CoAP module for observing resources (draft-ietf-core-observe-11). + * \author + * Matthias Kovatsch + */ + +#include +#include +#include "er-coap-observe.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +/*---------------------------------------------------------------------------*/ +MEMB(observers_memb, coap_observer_t, COAP_MAX_OBSERVERS); +LIST(observers_list); +/*---------------------------------------------------------------------------*/ +/*- Internal API ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +coap_observer_t * +coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, + size_t token_len, const char *uri) +{ + /* Remove existing observe relationship, if any. */ + coap_remove_observer_by_uri(addr, port, uri); + + coap_observer_t *o = memb_alloc(&observers_memb); + + if(o) { + o->url = uri; + uip_ipaddr_copy(&o->addr, addr); + o->port = port; + o->token_len = token_len; + memcpy(o->token, token, token_len); + o->last_mid = 0; + + PRINTF("Adding observer (%u/%u) for /%s [0x%02X%02X]\n", + list_length(observers_list) + 1, COAP_MAX_OBSERVERS, + o->url, o->token[0], o->token[1]); + list_add(observers_list, o); + } + + return o; +} +/*---------------------------------------------------------------------------*/ +/*- Removal -----------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void +coap_remove_observer(coap_observer_t *o) +{ + PRINTF("Removing observer for /%s [0x%02X%02X]\n", o->url, o->token[0], + o->token[1]); + + memb_free(&observers_memb, o); + list_remove(observers_list, o); +} +/*---------------------------------------------------------------------------*/ +int +coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port) +{ + int removed = 0; + coap_observer_t *obs = NULL; + + for(obs = (coap_observer_t *)list_head(observers_list); obs; + obs = obs->next) { + PRINTF("Remove check client "); + PRINT6ADDR(addr); + PRINTF(":%u\n", port); + if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port) { + coap_remove_observer(obs); + removed++; + } + } + return removed; +} +/*---------------------------------------------------------------------------*/ +int +coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, + uint8_t *token, size_t token_len) +{ + int removed = 0; + coap_observer_t *obs = NULL; + + for(obs = (coap_observer_t *)list_head(observers_list); obs; + obs = obs->next) { + PRINTF("Remove check Token 0x%02X%02X\n", token[0], token[1]); + if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port + && obs->token_len == token_len + && memcmp(obs->token, token, token_len) == 0) { + coap_remove_observer(obs); + removed++; + } + } + return removed; +} +/*---------------------------------------------------------------------------*/ +int +coap_remove_observer_by_uri(uip_ipaddr_t *addr, uint16_t port, + const char *uri) +{ + int removed = 0; + coap_observer_t *obs = NULL; + + for(obs = (coap_observer_t *)list_head(observers_list); obs; + obs = obs->next) { + PRINTF("Remove check URL %p\n", uri); + if((addr == NULL + || (uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port)) + && (obs->url == uri || memcmp(obs->url, uri, strlen(obs->url)) == 0)) { + coap_remove_observer(obs); + removed++; + } + } + return removed; +} +/*---------------------------------------------------------------------------*/ +int +coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid) +{ + int removed = 0; + coap_observer_t *obs = NULL; + + for(obs = (coap_observer_t *)list_head(observers_list); obs; + obs = obs->next) { + PRINTF("Remove check MID %u\n", mid); + if(uip_ipaddr_cmp(&obs->addr, addr) && obs->port == port + && obs->last_mid == mid) { + coap_remove_observer(obs); + removed++; + } + } + return removed; +} +/*---------------------------------------------------------------------------*/ +/*- Notification ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void +coap_notify_observers(resource_t *resource) +{ + /* build notification */ + coap_packet_t notification[1]; /* this way the packet can be treated as pointer as usual */ + coap_init_message(notification, COAP_TYPE_NON, CONTENT_2_05, 0); + coap_observer_t *obs = NULL; + + PRINTF("Observe: Notification from %s\n", resource->url); + + /* iterate over observers */ + for(obs = (coap_observer_t *)list_head(observers_list); obs; + obs = obs->next) { + if(obs->url == resource->url) { /* using RESOURCE url pointer as handle */ + coap_transaction_t *transaction = NULL; + + /*TODO implement special transaction for CON, sharing the same buffer to allow for more observers */ + + if((transaction = coap_new_transaction(coap_get_mid(), &obs->addr, obs->port))) { + if(obs->obs_counter % COAP_OBSERVE_REFRESH_INTERVAL == 0) { + PRINTF(" Force Confirmable for\n"); + notification->type = COAP_TYPE_CON; + } + + PRINTF(" Observer "); + PRINT6ADDR(&obs->addr); + PRINTF(":%u\n", obs->port); + + /* update last MID for RST matching */ + obs->last_mid = transaction->mid; + + /* prepare response */ + notification->mid = transaction->mid; + + resource->get_handler(NULL, notification, + transaction->packet + COAP_MAX_HEADER_SIZE, + REST_MAX_CHUNK_SIZE, NULL); + + if(notification->code < BAD_REQUEST_4_00) { + coap_set_header_observe(notification, (obs->obs_counter)++); + } + coap_set_token(notification, obs->token, obs->token_len); + + transaction->packet_len = + coap_serialize_message(notification, transaction->packet); + + coap_send_transaction(transaction); + } + } + } +} +/*---------------------------------------------------------------------------*/ +void +coap_observe_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; + + static char content[16]; + + if(coap_req->code == COAP_GET && coap_res->code < 128) { /* GET request and response without error code */ + if(IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) { + if(coap_req->observe == 0) { + + if(coap_add_observer(&UIP_IP_BUF->srcipaddr, UIP_UDP_BUF->srcport, + coap_req->token, coap_req->token_len, + resource->url)) { + coap_set_header_observe(coap_res, 0); + /* + * Following payload is for demonstration purposes only. + * A subscription should return the same representation as a normal GET. + * Uncomment if you want an information about the avaiable observers. + */ + /* + * coap_set_payload(coap_res, + * content, + * snprintf(content, sizeof(content), "Added %u/%u", + * list_length(observers_list), + * COAP_MAX_OBSERVERS)); + */ + } else { + coap_res->code = SERVICE_UNAVAILABLE_5_03; + coap_set_payload(coap_res, "TooManyObservers", 16); + } + } else if(coap_req->observe == 1) { + + /* remove client if it is currently observe */ + coap_remove_observer_by_token(&UIP_IP_BUF->srcipaddr, + UIP_UDP_BUF->srcport, coap_req->token, + coap_req->token_len); + } + } + } +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap-13/er-coap-13-observing.h b/apps/er-coap/er-coap-observe.h similarity index 67% rename from apps/er-coap-13/er-coap-13-observing.h rename to apps/er-coap/er-coap-observe.h index 3b6510a71..6d0498bb6 100644 --- a/apps/er-coap-13/er-coap-13-observing.h +++ b/apps/er-coap/er-coap-observe.h @@ -31,31 +31,28 @@ /** * \file - * CoAP module for observing resources + * CoAP module for observing resources (draft-ietf-core-observe-11). * \author * Matthias Kovatsch */ -#ifndef COAP_OBSERVING_H_ -#define COAP_OBSERVING_H_ +#ifndef COAP_OBSERVE_H_ +#define COAP_OBSERVE_H_ -#include "sys/stimer.h" -#include "er-coap-13.h" -#include "er-coap-13-transactions.h" +#include "er-coap.h" +#include "er-coap-transactions.h" +#include "stimer.h" -#ifndef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS-1 -#endif /* COAP_MAX_OBSERVERS */ - -/* Interval in seconds in which NON notifies are changed to CON notifies to check client. */ -#define COAP_OBSERVING_REFRESH_INTERVAL 60 - -#if COAP_MAX_OPEN_TRANSACTIONS + */ + +#include +#include "er-coap-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +#define ADD_CHAR_IF_POSSIBLE(char) \ + if(strpos >= *offset && bufpos < preferred_size) { \ + buffer[bufpos++] = char; \ + } \ + ++strpos + +#define ADD_STRING_IF_POSSIBLE(string, op) \ + tmplen = strlen(string); \ + if(strpos + tmplen > *offset) { \ + bufpos += snprintf((char *)buffer + bufpos, \ + preferred_size - bufpos + 1, \ + "%s", \ + string \ + + (*offset - (int32_t)strpos > 0 ? \ + *offset - (int32_t)strpos : 0)); \ + if(bufpos op preferred_size) { \ + PRINTF("res: BREAK at %s (%p)\n", string, resource); \ + break; \ + } \ + } \ + strpos += tmplen + +/*---------------------------------------------------------------------------*/ +/*- Resource Handlers -------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void +well_known_core_get_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + size_t strpos = 0; /* position in overall string (which is larger than the buffer) */ + size_t bufpos = 0; /* position within buffer (bytes written) */ + size_t tmplen = 0; + resource_t *resource = NULL; + +#if COAP_LINK_FORMAT_FILTERING + /* For filtering. */ + const char *filter = NULL; + const char *attrib = NULL; + const char *found = NULL; + const char *end = NULL; + char *value = NULL; + char lastchar = '\0'; + int len = coap_get_header_uri_query(request, &filter); + + if(len) { + value = strchr(filter, '='); + value[0] = '\0'; + ++value; + len -= strlen(filter) + 1; + + PRINTF("Filter %s = %.*s\n", filter, len, value); + + if(strcmp(filter, "href") == 0 && value[0] == '/') { + ++value; + --len; + } + + lastchar = value[len - 1]; + value[len - 1] = '\0'; + } +#endif + + for(resource = (resource_t *)list_head(rest_get_resources()); resource; + resource = resource->next) { +#if COAP_LINK_FORMAT_FILTERING + /* Filtering */ + if(len) { + if(strcmp(filter, "href") == 0) { + attrib = strstr(resource->url, value); + if(attrib == NULL || (value[-1] == '/' && attrib != resource->url)) { + continue; + } + end = attrib + strlen(attrib); + } else { + attrib = strstr(resource->attributes, filter); + if(attrib == NULL + || (attrib[strlen(filter)] != '=' + && attrib[strlen(filter)] != '"')) { + continue; + } + attrib += strlen(filter) + 2; + end = strchr(attrib, '"'); + } + + PRINTF("Filter: res has attrib %s (%s)\n", attrib, value); + found = attrib; + while((found = strstr(found, value)) != NULL) { + if(found > end) { + found = NULL; + break; + } + if(lastchar == found[len - 1] || lastchar == '*') { + break; + } + ++found; + } + if(found == NULL) { + continue; + } + PRINTF("Filter: res has prefix %s\n", found); + if(lastchar != '*' + && (found[len] != '"' && found[len] != ' ' && found[len] != '\0')) { + continue; + } + PRINTF("Filter: res has match\n"); + } +#endif + + PRINTF("res: /%s (%p)\npos: s%d, o%ld, b%d\n", resource->url, resource, + strpos, *offset, bufpos); + + if(strpos > 0) { + ADD_CHAR_IF_POSSIBLE(','); + } + ADD_CHAR_IF_POSSIBLE('<'); + ADD_CHAR_IF_POSSIBLE('/'); + ADD_STRING_IF_POSSIBLE(resource->url, >=); + ADD_CHAR_IF_POSSIBLE('>'); + + if(resource->attributes[0]) { + ADD_CHAR_IF_POSSIBLE(';'); + ADD_STRING_IF_POSSIBLE(resource->attributes, >); + } + + /* buffer full, but resource not completed yet; or: do not break if resource exactly fills buffer. */ + if(bufpos > preferred_size && strpos - bufpos > *offset) { + PRINTF("res: BREAK at %s (%p)\n", resource->url, resource); + break; + } + } + + if(bufpos > 0) { + PRINTF("BUF %d: %.*s\n", bufpos, bufpos, (char *)buffer); + + coap_set_payload(response, buffer, bufpos); + coap_set_header_content_format(response, APPLICATION_LINK_FORMAT); + } else if(strpos > 0) { + PRINTF("well_known_core_handler(): bufpos<=0\n"); + + coap_set_status_code(response, BAD_OPTION_4_02); + coap_set_payload(response, "BlockOutOfScope", 15); + } + + if(resource == NULL) { + PRINTF("res: DONE\n"); + *offset = -1; + } else { + PRINTF("res: MORE at %s (%p)\n", resource->url, resource); + *offset += preferred_size; + } +} +/*---------------------------------------------------------------------------*/ +RESOURCE(res_well_known_core, "ct=40", well_known_core_get_handler, NULL, + NULL, NULL); +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap-13/er-coap-13-separate.c b/apps/er-coap/er-coap-separate.c similarity index 55% rename from apps/er-coap-13/er-coap-13-separate.c rename to apps/er-coap/er-coap-separate.c index 85ec4ec0c..d35566d79 100644 --- a/apps/er-coap-13/er-coap-13-separate.c +++ b/apps/er-coap/er-coap-separate.c @@ -31,87 +31,118 @@ /** * \file - * CoAP module for separate responses + * CoAP module for separate responses. * \author * Matthias Kovatsch */ #include #include - -#include "er-coap-13-separate.h" -#include "er-coap-13-transactions.h" +#include "er-coap-separate.h" +#include "er-coap-transactions.h" #define DEBUG 0 #if DEBUG +#include #define PRINTF(...) printf(__VA_ARGS__) #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) #else #define PRINTF(...) #define PRINT6ADDR(addr) #define PRINTLLADDR(addr) #endif -/*----------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +/*- Separate Response API ---------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +/** + * \brief Reject a request that would require a separate response with an error message + * + * When the server does not have enough resources left to store the information + * for a separate response or otherwise cannot execute the resource handler, + * this function will respond with 5.03 Service Unavailable. The client can + * then retry later. + */ void coap_separate_reject() { - coap_error_code = SERVICE_UNAVAILABLE_5_03; + /* TODO: Accept string pointer for custom error message */ + erbium_status_code = SERVICE_UNAVAILABLE_5_03; coap_error_message = "AlreadyInUse"; } /*----------------------------------------------------------------------------*/ -int +/** + * \brief Initiate a separate response with an empty ACK + * \param request The request to accept + * \param separate_store A pointer to the data structure that will store the + * relevant information for the response + * + * When the server does not have enough resources left to store the information + * for a separate response or otherwise cannot execute the resource handler, + * this function will respond with 5.03 Service Unavailable. The client can + * then retry later. + */ +void coap_separate_accept(void *request, coap_separate_t *separate_store) { - coap_packet_t *const coap_req = (coap_packet_t *) request; + coap_packet_t *const coap_req = (coap_packet_t *)request; coap_transaction_t *const t = coap_get_transaction_by_mid(coap_req->mid); - PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, coap_req->uri_path, coap_req->mid); - if (t) - { - /* Send separate ACK for CON. */ - if (coap_req->type==COAP_TYPE_CON) - { + PRINTF("Separate ACCEPT: /%.*s MID %u\n", coap_req->uri_path_len, + coap_req->uri_path, coap_req->mid); + if(t) { + /* send separate ACK for CON */ + if(coap_req->type == COAP_TYPE_CON) { 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)); + /* 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)); } - /* Store remote address. */ + /* store remote address */ uip_ipaddr_copy(&separate_store->addr, &t->addr); separate_store->port = t->port; - /* Store correct response type. */ - separate_store->type = coap_req->type==COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON; + /* store correct response type */ + separate_store->type = + coap_req->type == COAP_TYPE_CON ? COAP_TYPE_CON : COAP_TYPE_NON; separate_store->mid = coap_get_mid(); /* if it was a NON, we burned one MID in the engine... */ memcpy(separate_store->token, coap_req->token, coap_req->token_len); separate_store->token_len = coap_req->token_len; + separate_store->block1_num = coap_req->block1_num; + separate_store->block1_size = coap_req->block1_size; + separate_store->block2_num = coap_req->block2_num; - separate_store->block2_size = coap_req->block2_size; + separate_store->block2_size = coap_req->block2_size > 0 ? MIN(COAP_MAX_BLOCK_SIZE, coap_req->block2_size) : COAP_MAX_BLOCK_SIZE; - /* Signal the engine to skip automatic response and clear transaction by engine. */ - coap_error_code = MANUAL_RESPONSE; - - return 1; - } - else - { + /* signal the engine to skip automatic response and clear transaction by engine */ + erbium_status_code = MANUAL_RESPONSE; + } else { PRINTF("ERROR: Response transaction for separate request not found!\n"); - return 0; + erbium_status_code = INTERNAL_SERVER_ERROR_5_00; } } /*----------------------------------------------------------------------------*/ void -coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code) +coap_separate_resume(void *response, coap_separate_t *separate_store, + uint8_t code) { - coap_init_message(response, separate_store->type, code, separate_store->mid); - if (separate_store->token_len) - { - coap_set_header_token(response, separate_store->token, separate_store->token_len); + coap_init_message(response, separate_store->type, code, + separate_store->mid); + if(separate_store->token_len) { + coap_set_token(response, separate_store->token, + separate_store->token_len); + } + if(separate_store->block1_size) { + coap_set_header_block1(response, separate_store->block1_num, + 0, separate_store->block1_size); } } +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap-13/er-coap-13-separate.h b/apps/er-coap/er-coap-separate.h similarity index 86% rename from apps/er-coap-13/er-coap-13-separate.h rename to apps/er-coap/er-coap-separate.h index d34eea606..dbecf1b39 100644 --- a/apps/er-coap-13/er-coap-13-separate.h +++ b/apps/er-coap/er-coap-separate.h @@ -31,7 +31,7 @@ /** * \file - * CoAP module for separate responses + * CoAP module for separate responses. * \author * Matthias Kovatsch */ @@ -39,7 +39,7 @@ #ifndef COAP_SEPARATE_H_ #define COAP_SEPARATE_H_ -#include "er-coap-13.h" +#include "er-coap.h" typedef struct coap_separate { @@ -52,15 +52,18 @@ typedef struct coap_separate { uint8_t token_len; uint8_t token[COAP_TOKEN_LEN]; - /* separate + blockwise is untested! */ + uint32_t block1_num; + uint16_t block1_size; + uint32_t block2_num; uint16_t block2_size; - } coap_separate_t; -int coap_separate_handler(resource_t *resource, void *request, void *response); +int coap_separate_handler(resource_t *resource, void *request, + void *response); void coap_separate_reject(); -int coap_separate_accept(void *request, coap_separate_t *separate_store); -void coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code); +void coap_separate_accept(void *request, coap_separate_t *separate_store); +void coap_separate_resume(void *response, coap_separate_t *separate_store, + uint8_t code); #endif /* COAP_SEPARATE_H_ */ diff --git a/apps/er-coap-13/er-coap-13-transactions.c b/apps/er-coap/er-coap-transactions.c similarity index 69% rename from apps/er-coap-13/er-coap-13-transactions.c rename to apps/er-coap/er-coap-transactions.c index 6bdc49fc3..7f6537932 100644 --- a/apps/er-coap-13/er-coap-13-transactions.c +++ b/apps/er-coap/er-coap-transactions.c @@ -38,49 +38,41 @@ #include "contiki.h" #include "contiki-net.h" - -#include "er-coap-13-transactions.h" -#include "er-coap-13-observing.h" - -/* - * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random - * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR. - */ -#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT) -#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5) +#include "er-coap-transactions.h" +#include "er-coap-observe.h" #define DEBUG 0 #if DEBUG #include #define PRINTF(...) printf(__VA_ARGS__) #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) #else #define PRINTF(...) #define PRINT6ADDR(addr) #define PRINTLLADDR(addr) #endif - +/*---------------------------------------------------------------------------*/ MEMB(transactions_memb, coap_transaction_t, COAP_MAX_OPEN_TRANSACTIONS); LIST(transactions_list); - static struct process *transaction_handler_process = NULL; +/*---------------------------------------------------------------------------*/ +/*- Internal API ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ void coap_register_as_transaction_handler() { transaction_handler_process = PROCESS_CURRENT(); } - coap_transaction_t * coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port) { coap_transaction_t *t = memb_alloc(&transactions_memb); - if (t) - { + if(t) { t->mid = mid; t->retrans_counter = 0; @@ -88,12 +80,12 @@ coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port) uip_ipaddr_copy(&t->addr, addr); t->port = port; - list_add(transactions_list, t); /* List itself makes sure same element is not added twice. */ + list_add(transactions_list, t); /* list itself makes sure same element is not added twice */ } return t; } - +/*---------------------------------------------------------------------------*/ void coap_send_transaction(coap_transaction_t *t) { @@ -101,22 +93,24 @@ coap_send_transaction(coap_transaction_t *t) coap_send_message(&t->addr, t->port, t->packet, t->packet_len); - if (COAP_TYPE_CON==((COAP_HEADER_TYPE_MASK & t->packet[0])>>COAP_HEADER_TYPE_POSITION)) - { - if (t->retrans_counterpacket[0]) >> COAP_HEADER_TYPE_POSITION)) { + if(t->retrans_counter < COAP_MAX_RETRANSMIT) { + /* not timed out yet */ PRINTF("Keeping transaction %u\n", t->mid); - if (t->retrans_counter==0) - { - t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK); - PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND); - } - else - { - t->retrans_timer.timer.interval <<= 1; /* double */ - PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND); + if(t->retrans_counter == 0) { + t->retrans_timer.timer.interval = + COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() + % + (clock_time_t) + COAP_RESPONSE_TIMEOUT_BACKOFF_MASK); + PRINTF("Initial interval %f\n", + (float)t->retrans_timer.timer.interval / CLOCK_SECOND); + } else { + t->retrans_timer.timer.interval <<= 1; /* double */ + PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, + (float)t->retrans_timer.timer.interval / CLOCK_SECOND); } /*FIXME @@ -124,15 +118,14 @@ coap_send_transaction(coap_transaction_t *t) * Maybe there is a better way, but avoid posting everything to the process. */ struct process *process_actual = PROCESS_CURRENT(); + process_current = transaction_handler_process; - etimer_restart(&t->retrans_timer); /* interval updated above */ + etimer_restart(&t->retrans_timer); /* interval updated above */ process_current = process_actual; t = NULL; - } - else - { - /* Timed out. */ + } else { + /* timed out */ PRINTF("Timeout\n"); restful_response_handler callback = t->callback; void *callback_data = t->callback_data; @@ -142,22 +135,19 @@ coap_send_transaction(coap_transaction_t *t) coap_clear_transaction(t); - if (callback) { + if(callback) { callback(callback_data, NULL); } } - } - else - { + } else { coap_clear_transaction(t); } } - +/*---------------------------------------------------------------------------*/ void coap_clear_transaction(coap_transaction_t *t) { - if (t) - { + if(t) { PRINTF("Freeing transaction %u: %p\n", t->mid, t); etimer_stop(&t->retrans_timer); @@ -165,35 +155,31 @@ coap_clear_transaction(coap_transaction_t *t) memb_free(&transactions_memb, t); } } - coap_transaction_t * coap_get_transaction_by_mid(uint16_t mid) { coap_transaction_t *t = NULL; - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (t->mid==mid) - { + for(t = (coap_transaction_t *)list_head(transactions_list); t; t = t->next) { + if(t->mid == mid) { PRINTF("Found transaction for MID %u: %p\n", t->mid, t); return t; } } return NULL; } - +/*---------------------------------------------------------------------------*/ void coap_check_transactions() { coap_transaction_t *t = NULL; - for (t = (coap_transaction_t*)list_head(transactions_list); t; t = t->next) - { - if (etimer_expired(&t->retrans_timer)) - { + for(t = (coap_transaction_t *)list_head(transactions_list); t; t = t->next) { + if(etimer_expired(&t->retrans_timer)) { ++(t->retrans_counter); PRINTF("Retransmitting %u (%u)\n", t->mid, t->retrans_counter); coap_send_transaction(t); } } } +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap-13/er-coap-13-transactions.h b/apps/er-coap/er-coap-transactions.h similarity index 76% rename from apps/er-coap-13/er-coap-13-transactions.h rename to apps/er-coap/er-coap-transactions.h index a127656d6..18bb421b1 100644 --- a/apps/er-coap-13/er-coap-13-transactions.h +++ b/apps/er-coap/er-coap-transactions.h @@ -39,18 +39,18 @@ #ifndef COAP_TRANSACTIONS_H_ #define COAP_TRANSACTIONS_H_ -#include "er-coap-13.h" +#include "er-coap.h" /* - * The number of concurrent messages that can be stored for retransmission in the transaction layer. + * Modulo mask (thus +1) for a random number to get the tick number for the random + * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR. */ -#ifndef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 4 -#endif /* COAP_MAX_OPEN_TRANSACTIONS */ +#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT) +#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK (long)((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * ((float)COAP_RESPONSE_RANDOM_FACTOR - 1.0)) + 0.5) + 1 /* container for transactions with message buffer and retransmission info */ typedef struct coap_transaction { - struct coap_transaction *next; /* for LIST */ + struct coap_transaction *next; /* for LIST */ uint16_t mid; struct etimer retrans_timer; @@ -63,12 +63,14 @@ typedef struct coap_transaction { void *callback_data; uint16_t packet_len; - uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */ + uint8_t packet[COAP_MAX_PACKET_SIZE + 1]; /* +1 for the terminating '\0' which will not be sent + * Use snprintf(buf, len+1, "", ...) to completely fill payload */ } coap_transaction_t; void coap_register_as_transaction_handler(); -coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, uint16_t port); +coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr, + uint16_t port); void coap_send_transaction(coap_transaction_t *t); void coap_clear_transaction(coap_transaction_t *t); coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid); diff --git a/apps/er-coap/er-coap.c b/apps/er-coap/er-coap.c new file mode 100644 index 000000000..f73b04186 --- /dev/null +++ b/apps/er-coap/er-coap.c @@ -0,0 +1,1193 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An implementation of the Constrained Application Protocol (RFC). + * \author + * Matthias Kovatsch + */ + +#include +#include +#include "contiki.h" +#include "contiki-net.h" + +#include "er-coap.h" +#include "er-coap-transactions.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +/*---------------------------------------------------------------------------*/ +/*- Variables ---------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +static struct uip_udp_conn *udp_conn = NULL; +static uint16_t current_mid = 0; + +coap_status_t erbium_status_code = NO_ERROR; +char *coap_error_message = ""; +/*---------------------------------------------------------------------------*/ +/*- Local helper functions --------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +static uint16_t +coap_log_2(uint16_t value) +{ + uint16_t result = 0; + + do { + value = value >> 1; + result++; + } while(value); + + return result ? result - 1 : result; +} +/*---------------------------------------------------------------------------*/ +static uint32_t +coap_parse_int_option(uint8_t *bytes, size_t length) +{ + uint32_t var = 0; + int i = 0; + + while(i < length) { + var <<= 8; + var |= bytes[i++]; + } + return var; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +coap_option_nibble(unsigned int value) +{ + if(value < 13) { + return value; + } else if(value <= 0xFF + 13) { + return 13; + } else { + return 14; + } +} +/*---------------------------------------------------------------------------*/ +static size_t +coap_set_option_header(unsigned int delta, size_t length, uint8_t *buffer) +{ + size_t written = 0; + + buffer[0] = coap_option_nibble(delta) << 4 | coap_option_nibble(length); + + /* avoids code duplication without function overhead */ + unsigned int *x = δ + + do { + if(*x > 268) { + buffer[++written] = (*x - 269) >> 8; + buffer[++written] = (*x - 269); + } else if(*x > 12) { + buffer[++written] = (*x - 13); + } + } while(x != &length && (x = &length)); + + PRINTF("WRITTEN %u B opt header\n", 1 + written); + + return ++written; +} +/*---------------------------------------------------------------------------*/ +static size_t +coap_serialize_int_option(unsigned int number, unsigned int current_number, + uint8_t *buffer, uint32_t value) +{ + size_t i = 0; + + if(0xFF000000 & value) { + ++i; + } + if(0xFFFF0000 & value) { + ++i; + } + if(0xFFFFFF00 & value) { + ++i; + } + if(0xFFFFFFFF & value) { + ++i; + } + PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, + i); + + i = coap_set_option_header(number - current_number, i, buffer); + + if(0xFF000000 & value) { + buffer[i++] = (uint8_t)(value >> 24); + } + if(0xFFFF0000 & value) { + buffer[i++] = (uint8_t)(value >> 16); + } + if(0xFFFFFF00 & value) { + buffer[i++] = (uint8_t)(value >> 8); + } + if(0xFFFFFFFF & value) { + buffer[i++] = (uint8_t)(value); + } + return i; +} +/*---------------------------------------------------------------------------*/ +static size_t +coap_serialize_array_option(unsigned int number, unsigned int current_number, + uint8_t *buffer, uint8_t *array, size_t length, + char split_char) +{ + size_t i = 0; + + PRINTF("ARRAY type %u, len %u, full [%.*s]\n", number, length, length, + array); + + if(split_char != '\0') { + int j; + uint8_t *part_start = array; + uint8_t *part_end = NULL; + size_t temp_length; + + for(j = 0; j <= length + 1; ++j) { + PRINTF("STEP %u/%u (%c)\n", j, length, array[j]); + if(array[j] == split_char || j == length) { + part_end = array + j; + temp_length = part_end - part_start; + + i += coap_set_option_header(number - current_number, temp_length, + &buffer[i]); + memcpy(&buffer[i], part_start, temp_length); + i += temp_length; + + PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, + number - current_number, i, temp_length, part_start); + + ++j; /* skip the splitter */ + current_number = number; + part_start = array + j; + } + } /* for */ + } else { + i += coap_set_option_header(number - current_number, length, &buffer[i]); + memcpy(&buffer[i], array, length); + i += length; + + PRINTF("OPTION type %u, delta %u, len %u\n", number, + number - current_number, length); + } + + return i; +} +/*---------------------------------------------------------------------------*/ +static void +coap_merge_multi_option(char **dst, size_t *dst_len, uint8_t *option, + size_t option_len, char separator) +{ + /* merge multiple options */ + if(*dst_len > 0) { + /* dst already contains an option: concatenate */ + (*dst)[*dst_len] = separator; + *dst_len += 1; + + /* memmove handles 2-byte option headers */ + memmove((*dst) + (*dst_len), option, option_len); + + *dst_len += option_len; + } else { + /* dst is empty: set to option */ + *dst = (char *)option; + *dst_len = option_len; + } +} +/*---------------------------------------------------------------------------*/ +static int +coap_get_variable(const char *buffer, size_t length, const char *name, + const char **output) +{ + const char *start = NULL; + const char *end = NULL; + const char *value_end = NULL; + size_t name_len = 0; + + /*initialize the output buffer first */ + *output = 0; + + name_len = strlen(name); + end = buffer + length; + + for(start = buffer; start + name_len < end; ++start) { + if((start == buffer || start[-1] == '&') && start[name_len] == '=' + && strncmp(name, start, name_len) == 0) { + + /* Point start to variable value */ + start += name_len + 1; + + /* Point end to the end of the value */ + value_end = (const char *)memchr(start, '&', end - start); + if(value_end == NULL) { + value_end = end; + } + *output = start; + + return value_end - start; + } + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/*- Internal API ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +void +coap_init_connection(uint16_t port) +{ + /* new connection with remote host */ + udp_conn = udp_new(NULL, 0, NULL); + udp_bind(udp_conn, port); + PRINTF("Listening on port %u\n", uip_ntohs(udp_conn->lport)); + + /* initialize transaction ID */ + current_mid = random_rand(); +} +/*---------------------------------------------------------------------------*/ +uint16_t +coap_get_mid() +{ + return ++current_mid; +} +/*---------------------------------------------------------------------------*/ +void +coap_init_message(void *packet, coap_message_type_t type, uint8_t code, + uint16_t mid) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + /* Important thing */ + memset(coap_pkt, 0, sizeof(coap_packet_t)); + + coap_pkt->type = type; + coap_pkt->code = code; + coap_pkt->mid = mid; +} +/*---------------------------------------------------------------------------*/ +size_t +coap_serialize_message(void *packet, uint8_t *buffer) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + uint8_t *option; + unsigned int current_number = 0; + + /* Initialize */ + coap_pkt->buffer = buffer; + coap_pkt->version = 1; + + PRINTF("-Serializing MID %u to %p, ", coap_pkt->mid, coap_pkt->buffer); + + /* set header fields */ + coap_pkt->buffer[0] = 0x00; + coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK + & (coap_pkt->version) << COAP_HEADER_VERSION_POSITION; + coap_pkt->buffer[0] |= COAP_HEADER_TYPE_MASK + & (coap_pkt->type) << COAP_HEADER_TYPE_POSITION; + coap_pkt->buffer[0] |= COAP_HEADER_TOKEN_LEN_MASK + & (coap_pkt->token_len) << COAP_HEADER_TOKEN_LEN_POSITION; + coap_pkt->buffer[1] = coap_pkt->code; + coap_pkt->buffer[2] = (uint8_t)((coap_pkt->mid) >> 8); + coap_pkt->buffer[3] = (uint8_t)(coap_pkt->mid); + + /* empty packet, dont need to do more stuff */ + if(!coap_pkt->code) { + PRINTF("-Done serializing empty message at %p-\n", option); + return 4; + } + + /* set Token */ + PRINTF("Token (len %u)", coap_pkt->token_len); + option = coap_pkt->buffer + COAP_HEADER_LEN; + for(current_number = 0; current_number < coap_pkt->token_len; + ++current_number) { + PRINTF(" %02X", coap_pkt->token[current_number]); + *option = coap_pkt->token[current_number]; + ++option; + } + PRINTF("-\n"); + + /* Serialize options */ + current_number = 0; + + PRINTF("-Serializing options at %p-\n", option); + + /* The options must be serialized in the order of their number */ + COAP_SERIALIZE_BYTE_OPTION(COAP_OPTION_IF_MATCH, if_match, "If-Match"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_URI_HOST, uri_host, '\0', + "Uri-Host"); + COAP_SERIALIZE_BYTE_OPTION(COAP_OPTION_ETAG, etag, "ETag"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_IF_NONE_MATCH, + content_format - + coap_pkt-> + content_format /* hack to get a zero field */, + "If-None-Match"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_OBSERVE, observe, "Observe"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_URI_PORT, uri_port, "Uri-Port"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_LOCATION_PATH, location_path, '/', + "Location-Path"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_URI_PATH, uri_path, '/', + "Uri-Path"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_CONTENT_FORMAT, content_format, + "Content-Format"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_MAX_AGE, max_age, "Max-Age"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_URI_QUERY, uri_query, '&', + "Uri-Query"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_ACCEPT, accept, "Accept"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_LOCATION_QUERY, location_query, + '&', "Location-Query"); + COAP_SERIALIZE_BLOCK_OPTION(COAP_OPTION_BLOCK2, block2, "Block2"); + COAP_SERIALIZE_BLOCK_OPTION(COAP_OPTION_BLOCK1, block1, "Block1"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_SIZE2, size2, "Size2"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_PROXY_URI, proxy_uri, '\0', + "Proxy-Uri"); + COAP_SERIALIZE_STRING_OPTION(COAP_OPTION_PROXY_SCHEME, proxy_scheme, '\0', + "Proxy-Scheme"); + COAP_SERIALIZE_INT_OPTION(COAP_OPTION_SIZE1, size1, "Size1"); + + PRINTF("-Done serializing at %p----\n", option); + + /* Pack payload */ + if((option - coap_pkt->buffer) <= COAP_MAX_HEADER_SIZE) { + /* Payload marker */ + if(coap_pkt->payload_len) { + *option = 0xFF; + ++option; + } + memmove(option, coap_pkt->payload, coap_pkt->payload_len); + } else { + /* an error occurred: caller must check for !=0 */ + coap_pkt->buffer = NULL; + coap_error_message = "Serialized header exceeds COAP_MAX_HEADER_SIZE"; + return 0; + } + + PRINTF("-Done %u B (header len %u, payload len %u)-\n", + coap_pkt->payload_len + option - buffer, option - buffer, + coap_pkt->payload_len); + + PRINTF("Dump [0x%02X %02X %02X %02X %02X %02X %02X %02X]\n", + coap_pkt->buffer[0], + coap_pkt->buffer[1], + coap_pkt->buffer[2], + coap_pkt->buffer[3], + coap_pkt->buffer[4], + coap_pkt->buffer[5], coap_pkt->buffer[6], coap_pkt->buffer[7] + ); + + return (option - buffer) + coap_pkt->payload_len; /* packet length */ +} +/*---------------------------------------------------------------------------*/ +void +coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, + uint16_t length) +{ + /* configure connection to reply to client */ + uip_ipaddr_copy(&udp_conn->ripaddr, addr); + udp_conn->rport = port; + + uip_udp_packet_send(udp_conn, data, length); + + PRINTF("-sent UDP datagram (%u)-\n", length); + + /* restore server socket to allow data from any node */ + memset(&udp_conn->ripaddr, 0, sizeof(udp_conn->ripaddr)); + udp_conn->rport = 0; +} +/*---------------------------------------------------------------------------*/ +coap_status_t +coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + /* initialize packet */ + memset(coap_pkt, 0, sizeof(coap_packet_t)); + + /* pointer to packet bytes */ + coap_pkt->buffer = data; + + /* parse header fields */ + coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0]) + >> COAP_HEADER_VERSION_POSITION; + coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0]) + >> COAP_HEADER_TYPE_POSITION; + coap_pkt->token_len = + MIN(COAP_TOKEN_LEN, + (COAP_HEADER_TOKEN_LEN_MASK & coap_pkt-> + buffer[0]) >> COAP_HEADER_TOKEN_LEN_POSITION); + coap_pkt->code = coap_pkt->buffer[1]; + coap_pkt->mid = coap_pkt->buffer[2] << 8 | coap_pkt->buffer[3]; + + if(coap_pkt->version != 1) { + coap_error_message = "CoAP version must be 1"; + return BAD_REQUEST_4_00; + } + + uint8_t *current_option = data + COAP_HEADER_LEN; + + memcpy(coap_pkt->token, current_option, coap_pkt->token_len); + PRINTF("Token (len %u) [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", + coap_pkt->token_len, coap_pkt->token[0], coap_pkt->token[1], + coap_pkt->token[2], coap_pkt->token[3], coap_pkt->token[4], + coap_pkt->token[5], coap_pkt->token[6], coap_pkt->token[7] + ); /*FIXME always prints 8 bytes */ + + /* parse options */ + memset(coap_pkt->options, 0, sizeof(coap_pkt->options)); + current_option += coap_pkt->token_len; + + unsigned int option_number = 0; + unsigned int option_delta = 0; + size_t option_length = 0; + + while(current_option < data + data_len) { + /* payload marker 0xFF, currently only checking for 0xF* because rest is reserved */ + if((current_option[0] & 0xF0) == 0xF0) { + coap_pkt->payload = ++current_option; + coap_pkt->payload_len = data_len - (coap_pkt->payload - data); + + /* also for receiving, the Erbium upper bound is REST_MAX_CHUNK_SIZE */ + if(coap_pkt->payload_len > REST_MAX_CHUNK_SIZE) { + coap_pkt->payload_len = REST_MAX_CHUNK_SIZE; + /* null-terminate payload */ + } + coap_pkt->payload[coap_pkt->payload_len] = '\0'; + + break; + } + + option_delta = current_option[0] >> 4; + option_length = current_option[0] & 0x0F; + ++current_option; + + /* avoids code duplication without function overhead */ + unsigned int *x = &option_delta; + + do { + if(*x == 13) { + *x += current_option[0]; + ++current_option; + } else if(*x == 14) { + *x += 255; + *x += current_option[0] << 8; + ++current_option; + *x += current_option[0]; + ++current_option; + } + } while(x != &option_length && (x = &option_length)); + + option_number += option_delta; + + PRINTF("OPTION %u (delta %u, len %u): ", option_number, option_delta, + option_length); + + SET_OPTION(coap_pkt, option_number); + + switch(option_number) { + case COAP_OPTION_CONTENT_FORMAT: + coap_pkt->content_format = coap_parse_int_option(current_option, + option_length); + PRINTF("Content-Format [%u]\n", coap_pkt->content_format); + break; + case COAP_OPTION_MAX_AGE: + coap_pkt->max_age = coap_parse_int_option(current_option, + option_length); + PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); + break; + case COAP_OPTION_ETAG: + coap_pkt->etag_len = MIN(COAP_ETAG_LEN, option_length); + memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len); + PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", + coap_pkt->etag_len, coap_pkt->etag[0], coap_pkt->etag[1], + coap_pkt->etag[2], coap_pkt->etag[3], coap_pkt->etag[4], + coap_pkt->etag[5], coap_pkt->etag[6], coap_pkt->etag[7] + ); /*FIXME always prints 8 bytes */ + break; + case COAP_OPTION_ACCEPT: + coap_pkt->accept = coap_parse_int_option(current_option, option_length); + PRINTF("Accept [%u]\n", coap_pkt->accept); + break; + case COAP_OPTION_IF_MATCH: + /* TODO support multiple ETags */ + coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, option_length); + memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len); + PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", + coap_pkt->if_match_len, coap_pkt->if_match[0], + coap_pkt->if_match[1], coap_pkt->if_match[2], + coap_pkt->if_match[3], coap_pkt->if_match[4], + coap_pkt->if_match[5], coap_pkt->if_match[6], + coap_pkt->if_match[7] + ); /* FIXME always prints 8 bytes */ + break; + case COAP_OPTION_IF_NONE_MATCH: + coap_pkt->if_none_match = 1; + PRINTF("If-None-Match\n"); + break; + + case COAP_OPTION_PROXY_URI: +#if COAP_PROXY_OPTION_PROCESSING + coap_pkt->proxy_uri = (char *)current_option; + coap_pkt->proxy_uri_len = option_length; +#endif + PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, + coap_pkt->proxy_uri); + coap_error_message = "This is a constrained server (Contiki)"; + return PROXYING_NOT_SUPPORTED_5_05; + break; + case COAP_OPTION_PROXY_SCHEME: +#if COAP_PROXY_OPTION_PROCESSING + coap_pkt->proxy_scheme = (char *)current_option; + coap_pkt->proxy_scheme_len = option_length; +#endif + PRINTF("Proxy-Scheme NOT IMPLEMENTED [%.*s]\n", + coap_pkt->proxy_scheme_len, coap_pkt->proxy_scheme); + coap_error_message = "This is a constrained server (Contiki)"; + return PROXYING_NOT_SUPPORTED_5_05; + break; + + case COAP_OPTION_URI_HOST: + coap_pkt->uri_host = (char *)current_option; + coap_pkt->uri_host_len = option_length; + PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); + break; + case COAP_OPTION_URI_PORT: + coap_pkt->uri_port = coap_parse_int_option(current_option, + option_length); + PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); + break; + case COAP_OPTION_URI_PATH: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + coap_merge_multi_option((char **)&(coap_pkt->uri_path), + &(coap_pkt->uri_path_len), current_option, + option_length, '/'); + PRINTF("Uri-Path [%.*s]\n", coap_pkt->uri_path_len, coap_pkt->uri_path); + break; + case COAP_OPTION_URI_QUERY: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + coap_merge_multi_option((char **)&(coap_pkt->uri_query), + &(coap_pkt->uri_query_len), current_option, + option_length, '&'); + PRINTF("Uri-Query [%.*s]\n", coap_pkt->uri_query_len, + coap_pkt->uri_query); + break; + + case COAP_OPTION_LOCATION_PATH: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + coap_merge_multi_option((char **)&(coap_pkt->location_path), + &(coap_pkt->location_path_len), current_option, + option_length, '/'); + PRINTF("Location-Path [%.*s]\n", coap_pkt->location_path_len, + coap_pkt->location_path); + break; + case COAP_OPTION_LOCATION_QUERY: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + coap_merge_multi_option((char **)&(coap_pkt->location_query), + &(coap_pkt->location_query_len), current_option, + option_length, '&'); + PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, + coap_pkt->location_query); + break; + + case COAP_OPTION_OBSERVE: + coap_pkt->observe = coap_parse_int_option(current_option, + option_length); + PRINTF("Observe [%lu]\n", coap_pkt->observe); + break; + case COAP_OPTION_BLOCK2: + coap_pkt->block2_num = coap_parse_int_option(current_option, + option_length); + coap_pkt->block2_more = (coap_pkt->block2_num & 0x08) >> 3; + coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07); + coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F) + << (coap_pkt->block2_num & 0x07); + coap_pkt->block2_num >>= 4; + PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, + coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); + break; + case COAP_OPTION_BLOCK1: + coap_pkt->block1_num = coap_parse_int_option(current_option, + option_length); + coap_pkt->block1_more = (coap_pkt->block1_num & 0x08) >> 3; + coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07); + coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F) + << (coap_pkt->block1_num & 0x07); + coap_pkt->block1_num >>= 4; + PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, + coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); + break; + case COAP_OPTION_SIZE2: + coap_pkt->size2 = coap_parse_int_option(current_option, option_length); + PRINTF("Size2 [%lu]\n", coap_pkt->size2); + break; + case COAP_OPTION_SIZE1: + coap_pkt->size1 = coap_parse_int_option(current_option, option_length); + PRINTF("Size1 [%lu]\n", coap_pkt->size1); + break; + default: + PRINTF("unknown (%u)\n", option_number); + /* check if critical (odd) */ + if(option_number & 1) { + coap_error_message = "Unsupported critical option"; + return BAD_OPTION_4_02; + } + } + + current_option += option_length; + } /* for */ + PRINTF("-Done parsing-------\n"); + + return NO_ERROR; +} +/*---------------------------------------------------------------------------*/ +/*- REST Engine API ---------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +int +coap_get_query_variable(void *packet, const char *name, const char **output) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { + return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, + name, output); + } + return 0; +} +int +coap_get_post_variable(void *packet, const char *name, const char **output) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(coap_pkt->payload_len) { + return coap_get_variable((const char *)coap_pkt->payload, + coap_pkt->payload_len, name, output); + } + return 0; +} +/*---------------------------------------------------------------------------*/ +int +coap_set_status_code(void *packet, unsigned int code) +{ + if(code <= 0xFF) { + ((coap_packet_t *)packet)->code = (uint8_t)code; + return 1; + } else { + return 0; + } +} +/*---------------------------------------------------------------------------*/ +int +coap_set_token(void *packet, const uint8_t *token, size_t token_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len); + memcpy(coap_pkt->token, token, coap_pkt->token_len); + + return coap_pkt->token_len; +} +/*---------------------------------------------------------------------------*/ +/*- CoAP REST Implementation API --------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +int +coap_get_header_content_format(void *packet, unsigned int *format) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_FORMAT)) { + return 0; + } + *format = coap_pkt->content_format; + return 1; +} +int +coap_set_header_content_format(void *packet, unsigned int format) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->content_format = (coap_content_format_t)format; + SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_FORMAT); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_accept(void *packet, unsigned int *accept) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) { + return 0; + } + *accept = coap_pkt->accept; + return 1; +} +int +coap_set_header_accept(void *packet, unsigned int accept) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->accept = (coap_content_format_t)accept; + SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_max_age(void *packet, uint32_t *age) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { + *age = COAP_DEFAULT_MAX_AGE; + } else { + *age = coap_pkt->max_age; + } return 1; +} +int +coap_set_header_max_age(void *packet, uint32_t age) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->max_age = age; + SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_etag(void *packet, const uint8_t **etag) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) { + return 0; + } + *etag = coap_pkt->etag; + return coap_pkt->etag_len; +} +int +coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len); + memcpy(coap_pkt->etag, etag, coap_pkt->etag_len); + + SET_OPTION(coap_pkt, COAP_OPTION_ETAG); + return coap_pkt->etag_len; +} +/*---------------------------------------------------------------------------*/ +/*FIXME support multiple ETags */ +int +coap_get_header_if_match(void *packet, const uint8_t **etag) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) { + return 0; + } + *etag = coap_pkt->if_match; + return coap_pkt->if_match_len; +} +int +coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len); + memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len); + + SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH); + return coap_pkt->if_match_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_if_none_match(void *packet) +{ + return IS_OPTION((coap_packet_t *)packet, + COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; +} +int +coap_set_header_if_none_match(void *packet) +{ + SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_proxy_uri(void *packet, const char **uri) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) { + return 0; + } + *uri = coap_pkt->proxy_uri; + return coap_pkt->proxy_uri_len; +} +int +coap_set_header_proxy_uri(void *packet, const char *uri) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + /*TODO Provide alternative that sets Proxy-Scheme and Uri-* options and provide er-coap-conf define */ + + coap_pkt->proxy_uri = uri; + coap_pkt->proxy_uri_len = strlen(uri); + + SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI); + return coap_pkt->proxy_uri_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_uri_host(void *packet, const char **host) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) { + return 0; + } + *host = coap_pkt->uri_host; + return coap_pkt->uri_host_len; +} +int +coap_set_header_uri_host(void *packet, const char *host) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->uri_host = host; + coap_pkt->uri_host_len = strlen(host); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST); + return coap_pkt->uri_host_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_uri_path(void *packet, const char **path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) { + return 0; + } + *path = coap_pkt->uri_path; + return coap_pkt->uri_path_len; +} +int +coap_set_header_uri_path(void *packet, const char *path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + while(path[0] == '/') + ++path; + + coap_pkt->uri_path = path; + coap_pkt->uri_path_len = strlen(path); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); + return coap_pkt->uri_path_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_uri_query(void *packet, const char **query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { + return 0; + } + *query = coap_pkt->uri_query; + return coap_pkt->uri_query_len; +} +int +coap_set_header_uri_query(void *packet, const char *query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + while(query[0] == '?') + ++query; + + coap_pkt->uri_query = query; + coap_pkt->uri_query_len = strlen(query); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + return coap_pkt->uri_query_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_location_path(void *packet, const char **path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) { + return 0; + } + *path = coap_pkt->location_path; + return coap_pkt->location_path_len; +} +int +coap_set_header_location_path(void *packet, const char *path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + char *query; + + while(path[0] == '/') + ++path; + + if((query = strchr(path, '?'))) { + coap_set_header_location_query(packet, query + 1); + coap_pkt->location_path_len = query - path; + } else { + coap_pkt->location_path_len = strlen(path); + } coap_pkt->location_path = path; + + if(coap_pkt->location_path_len > 0) { + SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH); + } + return coap_pkt->location_path_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_location_query(void *packet, const char **query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) { + return 0; + } + *query = coap_pkt->location_query; + return coap_pkt->location_query_len; +} +int +coap_set_header_location_query(void *packet, const char *query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + while(query[0] == '?') + ++query; + + coap_pkt->location_query = query; + coap_pkt->location_query_len = strlen(query); + + SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY); + return coap_pkt->location_query_len; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_observe(void *packet, uint32_t *observe) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) { + return 0; + } + *observe = coap_pkt->observe; + return 1; +} +int +coap_set_header_observe(void *packet, uint32_t observe) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->observe = observe; + SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, + uint16_t *size, uint32_t *offset) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) { + return 0; + } + /* pointers may be NULL to get only specific block parameters */ + if(num != NULL) { + *num = coap_pkt->block2_num; + } + if(more != NULL) { + *more = coap_pkt->block2_more; + } + if(size != NULL) { + *size = coap_pkt->block2_size; + } + if(offset != NULL) { + *offset = coap_pkt->block2_offset; + } + return 1; +} +int +coap_set_header_block2(void *packet, uint32_t num, uint8_t more, + uint16_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(size < 16) { + return 0; + } + if(size > 2048) { + return 0; + } + if(num > 0x0FFFFF) { + return 0; + } + coap_pkt->block2_num = num; + coap_pkt->block2_more = more ? 1 : 0; + coap_pkt->block2_size = size; + + SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, + uint16_t *size, uint32_t *offset) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) { + return 0; + } + /* pointers may be NULL to get only specific block parameters */ + if(num != NULL) { + *num = coap_pkt->block1_num; + } + if(more != NULL) { + *more = coap_pkt->block1_more; + } + if(size != NULL) { + *size = coap_pkt->block1_size; + } + if(offset != NULL) { + *offset = coap_pkt->block1_offset; + } + return 1; +} +int +coap_set_header_block1(void *packet, uint32_t num, uint8_t more, + uint16_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(size < 16) { + return 0; + } + if(size > 2048) { + return 0; + } + if(num > 0x0FFFFF) { + return 0; + } + coap_pkt->block1_num = num; + coap_pkt->block1_more = more; + coap_pkt->block1_size = size; + + SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_size2(void *packet, uint32_t *size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_SIZE2)) { + return 0; + } + *size = coap_pkt->size2; + return 1; +} +int +coap_set_header_size2(void *packet, uint32_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->size2 = size; + SET_OPTION(coap_pkt, COAP_OPTION_SIZE2); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_header_size1(void *packet, uint32_t *size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(!IS_OPTION(coap_pkt, COAP_OPTION_SIZE1)) { + return 0; + } + *size = coap_pkt->size1; + return 1; +} +int +coap_set_header_size1(void *packet, uint32_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->size1 = size; + SET_OPTION(coap_pkt, COAP_OPTION_SIZE1); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +coap_get_payload(void *packet, const uint8_t **payload) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + if(coap_pkt->payload) { + *payload = coap_pkt->payload; + return coap_pkt->payload_len; + } else { + *payload = NULL; + return 0; + } +} +int +coap_set_payload(void *packet, const void *payload, size_t length) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *)packet; + + coap_pkt->payload = (uint8_t *)payload; + coap_pkt->payload_len = MIN(REST_MAX_CHUNK_SIZE, length); + + return coap_pkt->payload_len; +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/er-coap/er-coap.h b/apps/er-coap/er-coap.h new file mode 100644 index 000000000..e74e521d2 --- /dev/null +++ b/apps/er-coap/er-coap.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An implementation of the Constrained Application Protocol (RFC). + * \author + * Matthias Kovatsch + */ + +#ifndef ER_COAP_H_ +#define ER_COAP_H_ + +#include /* for size_t */ +#include "contiki-net.h" +#include "er-coap-constants.h" +#include "er-coap-conf.h" + +/* sanity check for configured values */ +#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) +#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_IPH_LEN - UIP_UDPH_LEN) +#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" +#endif + +/* use Erbium CoAP for the REST Engine. Must come before include of rest-engine.h. */ +#define REST coap_rest_implementation +#include "rest-engine.h" + +/* REST_MAX_CHUNK_SIZE can be different from 2^x so we need to get next lower 2^x for COAP_MAX_BLOCK_SIZE */ +#ifndef COAP_MAX_BLOCK_SIZE +#define COAP_MAX_BLOCK_SIZE (REST_MAX_CHUNK_SIZE < 32 ? 16 : \ + (REST_MAX_CHUNK_SIZE < 64 ? 32 : \ + (REST_MAX_CHUNK_SIZE < 128 ? 64 : \ + (REST_MAX_CHUNK_SIZE < 256 ? 128 : \ + (REST_MAX_CHUNK_SIZE < 512 ? 256 : \ + (REST_MAX_CHUNK_SIZE < 1024 ? 512 : \ + (REST_MAX_CHUNK_SIZE < 2048 ? 1024 : 2048))))))) +#endif /* COAP_MAX_BLOCK_SIZE */ + +/* direct access into the buffer */ +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#if UIP_CONF_IPV6 +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#else +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN]) +#endif + +/* bitmap for set options */ +enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 }; + +#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE)) +#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE))) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +/* parsed message struct */ +typedef struct { + uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */ + + uint8_t version; + coap_message_type_t type; + uint8_t code; + uint16_t mid; + + uint8_t token_len; + uint8_t token[COAP_TOKEN_LEN]; + + uint8_t options[COAP_OPTION_SIZE1 / OPTION_MAP_SIZE + 1]; /* bitmap to check if option is set */ + + coap_content_format_t content_format; /* parse options once and store; allows setting options in random order */ + uint32_t max_age; + uint8_t etag_len; + uint8_t etag[COAP_ETAG_LEN]; + size_t proxy_uri_len; + const char *proxy_uri; + size_t proxy_scheme_len; + const char *proxy_scheme; + size_t uri_host_len; + const char *uri_host; + size_t location_path_len; + const char *location_path; + uint16_t uri_port; + size_t location_query_len; + const char *location_query; + size_t uri_path_len; + const char *uri_path; + int32_t observe; + coap_content_format_t accept; + uint8_t if_match_len; + uint8_t if_match[COAP_ETAG_LEN]; + uint32_t block2_num; + uint8_t block2_more; + uint16_t block2_size; + uint32_t block2_offset; + uint32_t block1_num; + uint8_t block1_more; + uint16_t block1_size; + uint32_t block1_offset; + uint32_t size2; + uint32_t size1; + size_t uri_query_len; + const char *uri_query; + uint8_t if_none_match; + + uint16_t payload_len; + uint8_t *payload; +} coap_packet_t; + +/* option format serialization */ +#define COAP_SERIALIZE_INT_OPTION(number, field, text) \ + if(IS_OPTION(coap_pkt, number)) { \ + PRINTF(text " [%u]\n", coap_pkt->field); \ + option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \ + current_number = number; \ + } +#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \ + if(IS_OPTION(coap_pkt, number)) { \ + PRINTF(text " %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \ + coap_pkt->field[0], \ + coap_pkt->field[1], \ + coap_pkt->field[2], \ + coap_pkt->field[3], \ + coap_pkt->field[4], \ + coap_pkt->field[5], \ + coap_pkt->field[6], \ + coap_pkt->field[7] \ + ); /* FIXME always prints 8 bytes */ \ + option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \ + current_number = number; \ + } +#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \ + if(IS_OPTION(coap_pkt, number)) { \ + PRINTF(text " [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \ + option += coap_serialize_array_option(number, current_number, option, (uint8_t *)coap_pkt->field, coap_pkt->field##_len, splitter); \ + current_number = number; \ + } +#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \ + if(IS_OPTION(coap_pkt, number)) \ + { \ + PRINTF(text " [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \ + uint32_t block = coap_pkt->field##_num << 4; \ + if(coap_pkt->field##_more) { block |= 0x8; } \ + block |= 0xF & coap_log_2(coap_pkt->field##_size / 16); \ + PRINTF(text " encoded: 0x%lX\n", block); \ + option += coap_serialize_int_option(number, current_number, option, block); \ + current_number = number; \ + } + +/* to store error code and human-readable payload */ +extern coap_status_t erbium_status_code; +extern char *coap_error_message; + +void coap_init_connection(uint16_t port); +uint16_t coap_get_mid(void); + +void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, + uint16_t mid); +size_t coap_serialize_message(void *packet, uint8_t *buffer); +void coap_send_message(uip_ipaddr_t *addr, uint16_t port, uint8_t *data, + uint16_t length); +coap_status_t coap_parse_message(void *request, uint8_t *data, + uint16_t data_len); + +int coap_get_query_variable(void *packet, const char *name, + const char **output); +int coap_get_post_variable(void *packet, const char *name, + const char **output); + +/*---------------------------------------------------------------------------*/ + +int coap_set_status_code(void *packet, unsigned int code); + +int coap_set_token(void *packet, const uint8_t *token, size_t token_len); + +int coap_get_header_content_format(void *packet, unsigned int *format); +int coap_set_header_content_format(void *packet, unsigned int format); + +int coap_get_header_accept(void *packet, unsigned int *accept); +int coap_set_header_accept(void *packet, unsigned int accept); + +int coap_get_header_max_age(void *packet, uint32_t *age); +int coap_set_header_max_age(void *packet, uint32_t age); + +int coap_get_header_etag(void *packet, const uint8_t **etag); +int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len); + +int coap_get_header_if_match(void *packet, const uint8_t **etag); +int coap_set_header_if_match(void *packet, const uint8_t *etag, + size_t etag_len); + +int coap_get_header_if_none_match(void *packet); +int coap_set_header_if_none_match(void *packet); + +int coap_get_header_proxy_uri(void *packet, const char **uri); /* in-place string might not be 0-terminated. */ +int coap_set_header_proxy_uri(void *packet, const char *uri); + +int coap_get_header_proxy_scheme(void *packet, const char **scheme); /* in-place string might not be 0-terminated. */ +int coap_set_header_proxy_scheme(void *packet, const char *scheme); + +int coap_get_header_uri_host(void *packet, const char **host); /* in-place string might not be 0-terminated. */ +int coap_set_header_uri_host(void *packet, const char *host); + +int coap_get_header_uri_path(void *packet, const char **path); /* in-place string might not be 0-terminated. */ +int coap_set_header_uri_path(void *packet, const char *path); + +int coap_get_header_uri_query(void *packet, const char **query); /* in-place string might not be 0-terminated. */ +int coap_set_header_uri_query(void *packet, const char *query); + +int coap_get_header_location_path(void *packet, const char **path); /* in-place string might not be 0-terminated. */ +int coap_set_header_location_path(void *packet, const char *path); /* also splits optional query into Location-Query option. */ + +int coap_get_header_location_query(void *packet, const char **query); /* in-place string might not be 0-terminated. */ +int coap_set_header_location_query(void *packet, const char *query); + +int coap_get_header_observe(void *packet, uint32_t *observe); +int coap_set_header_observe(void *packet, uint32_t observe); + +int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, + uint16_t *size, uint32_t *offset); +int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, + uint16_t size); + +int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, + uint16_t *size, uint32_t *offset); +int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, + uint16_t size); + +int coap_get_header_size2(void *packet, uint32_t *size); +int coap_set_header_size2(void *packet, uint32_t size); + +int coap_get_header_size1(void *packet, uint32_t *size); +int coap_set_header_size1(void *packet, uint32_t size); + +int coap_get_payload(void *packet, const uint8_t **payload); +int coap_set_payload(void *packet, const void *payload, size_t length); + +#endif /* ER_COAP_H_ */ diff --git a/apps/erbium/Makefile.erbium b/apps/erbium/Makefile.erbium deleted file mode 100644 index 4785008e1..000000000 --- a/apps/erbium/Makefile.erbium +++ /dev/null @@ -1 +0,0 @@ -erbium_src = erbium.c diff --git a/apps/erbium/erbium.c b/apps/erbium/erbium.c deleted file mode 100644 index f624dd594..000000000 --- a/apps/erbium/erbium.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An abstraction layer for RESTful Web services - * \author - * Matthias Kovatsch - */ - -#include "contiki.h" -#include /*for string operations in match_addresses*/ -#include /*for sprintf in rest_set_header_**/ - -#include "erbium.h" - -#define DEBUG 0 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - -PROCESS_NAME(rest_manager_process); - -LIST(restful_services); -LIST(restful_periodic_services); - - -void -rest_init_engine(void) -{ - list_init(restful_services); - - REST.set_service_callback(rest_invoke_restful_service); - - /* Start the RESTful server implementation. */ - REST.init(); - - /*Start rest manager process*/ - process_start(&rest_manager_process, NULL); -} - -void -rest_activate_resource(resource_t* resource) -{ - PRINTF("Activating: %s", resource->url); - - if (!resource->pre_handler) - { - rest_set_pre_handler(resource, REST.default_pre_handler); - } - if (!resource->post_handler) - { - rest_set_post_handler(resource, REST.default_post_handler); - } - - list_add(restful_services, resource); -} - -void -rest_activate_periodic_resource(periodic_resource_t* periodic_resource) -{ - list_add(restful_periodic_services, periodic_resource); - rest_activate_resource(periodic_resource->resource); - - rest_set_post_handler(periodic_resource->resource, REST.subscription_handler); -} - -void -rest_activate_event_resource(resource_t* resource) -{ - rest_activate_resource(resource); - rest_set_post_handler(resource, REST.subscription_handler); -} - -list_t -rest_get_resources(void) -{ - return restful_services; -} - - -void* -rest_get_user_data(resource_t* resource) -{ - return resource->user_data; -} - -void -rest_set_user_data(resource_t* resource, void* user_data) -{ - resource->user_data = user_data; -} - -void -rest_set_pre_handler(resource_t* resource, restful_pre_handler pre_handler) -{ - resource->pre_handler = pre_handler; -} - -void -rest_set_post_handler(resource_t* resource, restful_post_handler post_handler) -{ - resource->post_handler = post_handler; -} - -void -rest_set_special_flags(resource_t* resource, rest_resource_flags_t flags) -{ - resource->flags |= flags; -} - -int -rest_invoke_restful_service(void* request, void* response, uint8_t *buffer, uint16_t buffer_size, int32_t *offset) -{ - uint8_t found = 0; - uint8_t allowed = 0; - - PRINTF("rest_invoke_restful_service url /%.*s -->\n", url_len, url); - - resource_t* resource = NULL; - const char *url = NULL; - - for (resource = (resource_t*)list_head(restful_services); resource; resource = resource->next) - { - /*if the web service handles that kind of requests and urls matches*/ - if ((REST.get_url(request, &url)==strlen(resource->url) || (REST.get_url(request, &url)>strlen(resource->url) && (resource->flags & HAS_SUB_RESOURCES))) - && strncmp(resource->url, url, strlen(resource->url)) == 0) - { - found = 1; - rest_resource_flags_t method = REST.get_method_type(request); - - PRINTF("method %u, resource->flags %u\n", (uint16_t)method, resource->flags); - - if (resource->flags & method) - { - allowed = 1; - - /*call pre handler if it exists*/ - if (!resource->pre_handler || resource->pre_handler(resource, request, response)) - { - /* call handler function*/ - resource->handler(request, response, buffer, buffer_size, offset); - - /*call post handler if it exists*/ - if (resource->post_handler) - { - resource->post_handler(resource, request, response); - } - } - } else { - REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED); - } - break; - } - } - - if (!found) { - REST.set_response_status(response, REST.status.NOT_FOUND); - } - - return found & allowed; -} -/*-----------------------------------------------------------------------------------*/ - -PROCESS(rest_manager_process, "Rest Process"); - -PROCESS_THREAD(rest_manager_process, ev, data) -{ - PROCESS_BEGIN(); - - PROCESS_PAUSE(); - - /* Initialize the PERIODIC_RESOURCE timers, which will be handled by this process. */ - periodic_resource_t* periodic_resource = NULL; - for (periodic_resource = (periodic_resource_t*) list_head(restful_periodic_services); periodic_resource; periodic_resource = periodic_resource->next) { - if (periodic_resource->period) { - PRINTF("Periodic: Set timer for %s to %lu\n", periodic_resource->resource->url, periodic_resource->period); - etimer_set(&periodic_resource->periodic_timer, periodic_resource->period); - } - } - - while (1) { - PROCESS_WAIT_EVENT(); - if (ev == PROCESS_EVENT_TIMER) { - for (periodic_resource = (periodic_resource_t*)list_head(restful_periodic_services);periodic_resource;periodic_resource = periodic_resource->next) { - if (periodic_resource->period && etimer_expired(&periodic_resource->periodic_timer)) { - - PRINTF("Periodic: etimer expired for /%s (period: %lu)\n", periodic_resource->resource->url, periodic_resource->period); - - /* Call the periodic_handler function if it exists. */ - if (periodic_resource->periodic_handler) { - (periodic_resource->periodic_handler)(periodic_resource->resource); - } - etimer_reset(&periodic_resource->periodic_timer); - } - } - } - } - - PROCESS_END(); -} - diff --git a/apps/erbium/erbium.h b/apps/erbium/erbium.h deleted file mode 100644 index 0fecd960d..000000000 --- a/apps/erbium/erbium.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This file is part of the Contiki operating system. - */ - -/** - * \file - * An abstraction layer for RESTful Web services - * \author - * Matthias Kovatsch - */ - -#ifndef ERBIUM_H_ -#define ERBIUM_H_ - -/*includes*/ -#include -#include "contiki.h" -#include "contiki-lib.h" - -/* - * The maximum buffer size that is provided for resource responses and must be respected due to the limited IP buffer. - * Larger data must be handled by the resource and will be sent chunk-wise through a TCP stream or CoAP blocks. - */ -#ifndef REST_MAX_CHUNK_SIZE -#define REST_MAX_CHUNK_SIZE 128 -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b)? (a) : (b)) -#endif /* MIN */ - -/* REST method types */ -typedef enum { - /* methods to handle */ - METHOD_GET = (1 << 0), - METHOD_POST = (1 << 1), - METHOD_PUT = (1 << 2), - METHOD_DELETE = (1 << 3), - - /* special flags */ - HAS_SUB_RESOURCES = (1<<7) -} rest_resource_flags_t; - -struct resource_s; -struct periodic_resource_s; - -/* Signatures of handler functions. */ -typedef void (*restful_handler) (void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); -typedef int (*restful_pre_handler) (struct resource_s *resource, void* request, void* response); -typedef void (*restful_post_handler) (struct resource_s *resource, void* request, void* response); -typedef void (*restful_periodic_handler) (struct resource_s* resource); -typedef void (*restful_response_handler) (void *data, void* response); - -/* Signature of the rest-engine service function. */ -typedef int (* service_callback_t)(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); - -/** - * The structure of a MAC protocol driver in Contiki. - */ -struct rest_implementation_status -{ - const unsigned int OK; /* CONTENT_2_05, OK_200 */ - const unsigned int CREATED; /* CREATED_2_01, CREATED_201 */ - const unsigned int CHANGED; /* CHANGED_2_04, NO_CONTENT_204 */ - const unsigned int DELETED; /* DELETED_2_02, NO_CONTENT_204 */ - const unsigned int NOT_MODIFIED; /* VALID_2_03, NOT_MODIFIED_304 */ - - const unsigned int BAD_REQUEST; /* BAD_REQUEST_4_00, BAD_REQUEST_400 */ - const unsigned int UNAUTHORIZED; /* UNAUTHORIZED_4_01, UNAUTHORIZED_401 */ - const unsigned int BAD_OPTION; /* BAD_OPTION_4_02, BAD_REQUEST_400 */ - const unsigned int FORBIDDEN; /* FORBIDDEN_4_03, FORBIDDEN_403 */ - const unsigned int NOT_FOUND; /* NOT_FOUND_4_04, NOT_FOUND_404 */ - const unsigned int METHOD_NOT_ALLOWED; /* METHOD_NOT_ALLOWED_4_05, METHOD_NOT_ALLOWED_405 */ - const unsigned int NOT_ACCEPTABLE; /* NOT_ACCEPTABLE_4_06, NOT_ACCEPTABLE_406 */ - const unsigned int REQUEST_ENTITY_TOO_LARGE; /* REQUEST_ENTITY_TOO_LARGE_4_13, REQUEST_ENTITY_TOO_LARGE_413 */ - const unsigned int UNSUPPORTED_MEDIA_TYPE; /* UNSUPPORTED_MEDIA_TYPE_4_15, UNSUPPORTED_MEDIA_TYPE_415 */ - - const unsigned int INTERNAL_SERVER_ERROR; /* INTERNAL_SERVER_ERROR_5_00, INTERNAL_SERVER_ERROR_500 */ - const unsigned int NOT_IMPLEMENTED; /* NOT_IMPLEMENTED_5_01, NOT_IMPLEMENTED_501 */ - const unsigned int BAD_GATEWAY; /* BAD_GATEWAY_5_02, BAD_GATEWAY_502 */ - const unsigned int SERVICE_UNAVAILABLE; /* SERVICE_UNAVAILABLE_5_03, SERVICE_UNAVAILABLE_503 */ - const unsigned int GATEWAY_TIMEOUT; /* GATEWAY_TIMEOUT_5_04, GATEWAY_TIMEOUT_504 */ - const unsigned int PROXYING_NOT_SUPPORTED; /* PROXYING_NOT_SUPPORTED_5_05, INTERNAL_SERVER_ERROR_500 */ -}; -struct rest_implementation_type -{ - unsigned int TEXT_PLAIN; - unsigned int TEXT_XML; - unsigned int TEXT_CSV; - unsigned int TEXT_HTML; - unsigned int IMAGE_GIF; - unsigned int IMAGE_JPEG; - unsigned int IMAGE_PNG; - unsigned int IMAGE_TIFF; - unsigned int AUDIO_RAW; - unsigned int VIDEO_RAW; - unsigned int APPLICATION_LINK_FORMAT; - unsigned int APPLICATION_XML; - unsigned int APPLICATION_OCTET_STREAM; - unsigned int APPLICATION_RDF_XML; - unsigned int APPLICATION_SOAP_XML; - unsigned int APPLICATION_ATOM_XML; - unsigned int APPLICATION_XMPP_XML; - unsigned int APPLICATION_EXI; - unsigned int APPLICATION_FASTINFOSET; - unsigned int APPLICATION_SOAP_FASTINFOSET; - unsigned int APPLICATION_JSON; - unsigned int APPLICATION_X_OBIX_BINARY; -}; - -/* - * Data structure representing a resource in REST. - */ -struct resource_s { - struct resource_s *next; /* for LIST, points to next resource defined */ - rest_resource_flags_t flags; /* handled RESTful methods */ - const char* url; /*handled URL*/ - const char* attributes; /* link-format attributes */ - restful_handler handler; /* handler function */ - restful_pre_handler pre_handler; /* to be called before handler, may perform initializations */ - restful_post_handler post_handler; /* to be called after handler, may perform finalizations (cleanup, etc) */ - void* user_data; /* pointer to user specific data */ - unsigned int benchmark; /* to benchmark resource handler, used for separate response */ -}; -typedef struct resource_s resource_t; - -struct periodic_resource_s { - struct periodic_resource_s *next; /* for LIST, points to next resource defined */ - resource_t *resource; - uint32_t period; - struct etimer periodic_timer; - restful_periodic_handler periodic_handler; -}; -typedef struct periodic_resource_s periodic_resource_t; - -struct rest_implementation { - char *name; - - /** Initialize the REST implementation. */ - void (* init)(void); - - /** Register the RESTful service callback at implementation */ - void (* set_service_callback)(service_callback_t callback); - - /** Get request URI path */ - int (* get_url)(void *request, const char **url); - - int (* set_url)(void *request, const char *url); - - /** Get the method of a request. */ - rest_resource_flags_t (* get_method_type)(void *request); - - /** Set the status code of a response. */ - int (* set_response_status)(void *response, unsigned int code); - - /** Get the content-type of a request. */ - unsigned int (* get_header_content_type)(void *request); - - /** Set the Content-Type of a response. */ - int (* set_header_content_type)(void *response, unsigned int content_type); - - /** Get the Accept types of a request. */ - int (* get_header_accept)(void *request, const uint16_t **accept); - - /** Get the Length option of a request. */ - int (* get_header_length)(void *request, uint32_t *size); - - /** Set the Length option of a response. */ - int (* set_header_length)(void *response, uint32_t size); - - /** Get the Max-Age option of a request. */ - int (* get_header_max_age)(void *request, uint32_t *age); - - /** Set the Max-Age option of a response. */ - int (* set_header_max_age)(void *response, uint32_t age); - - /** Set the ETag option of a response. */ - int (* set_header_etag)(void *response, const uint8_t *etag, size_t length); - - /** Get the If-Match option of a request. */ - int (* get_header_if_match)(void *request, const uint8_t **etag); - - /** Get the If-Match option of a request. */ - int (* get_header_if_none_match)(void *request); - - /** Get the Host option of a request. */ - int (* get_header_host)(void *request, const char **host); - - /** Set the location option of a response. */ - int (* set_header_location)(void *response, const char *location); - - /** Get the payload option of a request. */ - int (* get_request_payload)(void *request, const uint8_t **payload); - - /** Set the payload option of a response. */ - int (* set_response_payload)(void *response, const void *payload, size_t length); - - /** Get the query string of a request. */ - int (* get_query)(void *request, const char **value); - - /** Get the value of a request query key-value pair. */ - int (* get_query_variable)(void *request, const char *name, const char **value); - - /** Get the value of a request POST key-value pair. */ - int (* get_post_variable)(void *request, const char *name, const char **value); - - /** Send the payload to all subscribers of the resource at url. */ - void (* notify_subscribers)(resource_t *resource, int32_t counter, void *notification); - - /** The handler for resource subscriptions. */ - restful_post_handler subscription_handler; - - /** A default pre-handler that is assigned with the RESOURCE macro. */ - restful_pre_handler default_pre_handler; - - /** A default post-handler that is assigned with the RESOURCE macro. */ - restful_post_handler default_post_handler; - - /* REST status codes. */ - const struct rest_implementation_status status; - - /* REST content-types. */ - const struct rest_implementation_type type; -}; - -/* - * Instance of REST implementation - */ -extern const struct rest_implementation REST; - -/* - * Macro to define a Resource - * Resources are statically defined for the sake of efficiency and better memory management. - */ -#define RESOURCE(name, flags, url, attributes) \ -void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \ -resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL} - -/* - * Macro to define a sub-resource - * Make sure to define its parent resource beforehand and set 'parent' to that name. - */ -#define SUB_RESOURCE(name, flags, url, attributes, parent) \ -resource_t resource_##name = {NULL, flags, url, attributes, parent##_handler, NULL, NULL, NULL} - -/* - * Macro to define an event resource - * Like periodic resources, event resources have a post_handler that manages a subscriber list. - * Instead of a periodic_handler, an event_callback must be provided. - */ -#define EVENT_RESOURCE(name, flags, url, attributes) \ -void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \ -void name##_event_handler(resource_t*); \ -resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL} - -/* - * Macro to define a periodic resource - * The corresponding [name]_periodic_handler() function will be called every period. - * For instance polling a sensor and publishing a changed value to subscribed clients would be done there. - * The subscriber list will be maintained by the post_handler rest_subscription_handler() (see rest-mapping header file). - */ -#define PERIODIC_RESOURCE(name, flags, url, attributes, period) \ -void name##_handler(void *, void *, uint8_t *, uint16_t, int32_t *); \ -resource_t resource_##name = {NULL, flags, url, attributes, name##_handler, NULL, NULL, NULL}; \ -void name##_periodic_handler(resource_t*); \ -periodic_resource_t periodic_resource_##name = {NULL, &resource_##name, period, {{0}}, name##_periodic_handler} - - -/* - * Initializes REST framework and starts HTTP or COAP process - */ -void rest_init_engine(void); - -/* - * Resources wanted to be accessible should be activated with the following code. - */ -void rest_activate_resource(resource_t* resource); -void rest_activate_periodic_resource(periodic_resource_t* periodic_resource); -void rest_activate_event_resource(resource_t* resource); - - -/* - * To be called by HTTP/COAP server as a callback function when a new service request appears. - * This function dispatches the corresponding RESTful service. - */ -int rest_invoke_restful_service(void* request, void* response, uint8_t *buffer, uint16_t buffer_size, int32_t *offset); - -/* - * Returns the resource list - */ -list_t rest_get_resources(void); - -/* - * Getter and setter methods for user specific data. - */ -void* rest_get_user_data(resource_t* resource); -void rest_set_user_data(resource_t* resource, void* user_data); - -/* - * Sets the pre handler function of the Resource. - * If set, this function will be called just before the original handler function. - * Can be used to setup work before resource handling. - */ -void rest_set_pre_handler(resource_t* resource, restful_pre_handler pre_handler); - -/* - * Sets the post handler function of the Resource. - * If set, this function will be called just after the original handler function. - * Can be used to do cleanup (deallocate memory, etc) after resource handling. - */ -void rest_set_post_handler(resource_t* resource, restful_post_handler post_handler); - -/* - * Sets resource flags for special properties, e.g., handling of sub-resources of URI-path. - */ -void rest_set_special_flags(resource_t* resource, rest_resource_flags_t flags); - -#endif /*ERBIUM_H_*/ diff --git a/apps/rest-engine/Makefile.rest-engine b/apps/rest-engine/Makefile.rest-engine new file mode 100755 index 000000000..dcf1e1519 --- /dev/null +++ b/apps/rest-engine/Makefile.rest-engine @@ -0,0 +1 @@ +rest-engine_src = rest-engine.c diff --git a/apps/rest-engine/rest-constants.h b/apps/rest-engine/rest-constants.h new file mode 100644 index 000000000..addc0970b --- /dev/null +++ b/apps/rest-engine/rest-constants.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Constants for the REST Engine (Erbium). + * \author + * Matthias Kovatsch + */ + +#ifndef REST_CONSTANTS_H_ +#define REST_CONSTANTS_H_ + +/** + * Generic status codes that are mapped to either HTTP or CoAP codes. + */ +struct rest_implementation_status { + const unsigned int OK; /* CONTENT_2_05, OK_200 */ + const unsigned int CREATED; /* CREATED_2_01, CREATED_201 */ + const unsigned int CHANGED; /* CHANGED_2_04, NO_CONTENT_204 */ + const unsigned int DELETED; /* DELETED_2_02, NO_CONTENT_204 */ + const unsigned int NOT_MODIFIED; /* VALID_2_03, NOT_MODIFIED_304 */ + + const unsigned int BAD_REQUEST; /* BAD_REQUEST_4_00, BAD_REQUEST_400 */ + const unsigned int UNAUTHORIZED; /* UNAUTHORIZED_4_01, UNAUTHORIZED_401 */ + const unsigned int BAD_OPTION; /* BAD_OPTION_4_02, BAD_REQUEST_400 */ + const unsigned int FORBIDDEN; /* FORBIDDEN_4_03, FORBIDDEN_403 */ + const unsigned int NOT_FOUND; /* NOT_FOUND_4_04, NOT_FOUND_404 */ + const unsigned int METHOD_NOT_ALLOWED; /* METHOD_NOT_ALLOWED_4_05, METHOD_NOT_ALLOWED_405 */ + const unsigned int NOT_ACCEPTABLE; /* NOT_ACCEPTABLE_4_06, NOT_ACCEPTABLE_406 */ + const unsigned int REQUEST_ENTITY_TOO_LARGE; /* REQUEST_ENTITY_TOO_LARGE_4_13, REQUEST_ENTITY_TOO_LARGE_413 */ + const unsigned int UNSUPPORTED_MEDIA_TYPE; /* UNSUPPORTED_MEDIA_TYPE_4_15, UNSUPPORTED_MEDIA_TYPE_415 */ + + const unsigned int INTERNAL_SERVER_ERROR; /* INTERNAL_SERVER_ERROR_5_00, INTERNAL_SERVER_ERROR_500 */ + const unsigned int NOT_IMPLEMENTED; /* NOT_IMPLEMENTED_5_01, NOT_IMPLEMENTED_501 */ + const unsigned int BAD_GATEWAY; /* BAD_GATEWAY_5_02, BAD_GATEWAY_502 */ + const unsigned int SERVICE_UNAVAILABLE; /* SERVICE_UNAVAILABLE_5_03, SERVICE_UNAVAILABLE_503 */ + const unsigned int GATEWAY_TIMEOUT; /* GATEWAY_TIMEOUT_5_04, GATEWAY_TIMEOUT_504 */ + const unsigned int PROXYING_NOT_SUPPORTED; /* PROXYING_NOT_SUPPORTED_5_05, INTERNAL_SERVER_ERROR_500 */ +}; + +/** + * List of Content-Formats which are Internet Media Types plus encoding. + * TODO This should be a constant enum taken from CoAP for both CoAP and HTTP. + */ +struct rest_implementation_type { + unsigned int TEXT_PLAIN; + unsigned int TEXT_XML; + unsigned int TEXT_CSV; + unsigned int TEXT_HTML; + unsigned int IMAGE_GIF; + unsigned int IMAGE_JPEG; + unsigned int IMAGE_PNG; + unsigned int IMAGE_TIFF; + unsigned int AUDIO_RAW; + unsigned int VIDEO_RAW; + unsigned int APPLICATION_LINK_FORMAT; + unsigned int APPLICATION_XML; + unsigned int APPLICATION_OCTET_STREAM; + unsigned int APPLICATION_RDF_XML; + unsigned int APPLICATION_SOAP_XML; + unsigned int APPLICATION_ATOM_XML; + unsigned int APPLICATION_XMPP_XML; + unsigned int APPLICATION_EXI; + unsigned int APPLICATION_FASTINFOSET; + unsigned int APPLICATION_SOAP_FASTINFOSET; + unsigned int APPLICATION_JSON; + unsigned int APPLICATION_X_OBIX_BINARY; +}; + +/** + * Resource flags for allowed methods and special functionalities. + */ +typedef enum { + NO_FLAGS = 0, + + /* methods to handle */ + METHOD_GET = (1 << 0), + METHOD_POST = (1 << 1), + METHOD_PUT = (1 << 2), + METHOD_DELETE = (1 << 3), + + /* special flags */ + HAS_SUB_RESOURCES = (1 << 4), + IS_SEPARATE = (1 << 5), + IS_OBSERVABLE = (1 << 6), + IS_PERIODIC = (1 << 7) +} rest_resource_flags_t; + +#endif /* REST_CONSTANTS_H_ */ diff --git a/apps/rest-engine/rest-engine.c b/apps/rest-engine/rest-engine.c new file mode 100644 index 000000000..7eb686d4d --- /dev/null +++ b/apps/rest-engine/rest-engine.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An abstraction layer for RESTful Web services (Erbium). + * Inspired by RESTful Contiki by Dogan Yazar. + * \author + * Matthias Kovatsch + */ + +#include +#include +#include "contiki.h" +#include "rest-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +PROCESS(rest_engine_process, "REST Engine"); +/*---------------------------------------------------------------------------*/ +LIST(restful_services); +LIST(restful_periodic_services); +/*---------------------------------------------------------------------------*/ +/*- REST Engine API ---------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +/** + * \brief Initializes and starts the REST Engine process + * + * This function must be called by server processes before any resources are + * registered through rest_activate_resource(). + */ +void +rest_init_engine(void) +{ + list_init(restful_services); + + REST.set_service_callback(rest_invoke_restful_service); + + /* Start the RESTful server implementation. */ + REST.init(); + + /*Start REST engine process */ + process_start(&rest_engine_process, NULL); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Makes a resource available under the given URI path + * \param resource A pointer to a resource implementation + * \param path The URI path string for this resource + * + * The resource implementation must be imported first using the + * extern keyword. The build system takes care of compiling every + * *.c file in the ./resources/ sub-directory (see example Makefile). + */ +void +rest_activate_resource(resource_t *resource, char *path) +{ + resource->url = path; + list_add(restful_services, resource); + + PRINTF("Activating: %s\n", resource->url); + + /* Only add periodic resources with a periodic_handler and a period > 0. */ + if(resource->flags & IS_PERIODIC && resource->periodic->periodic_handler + && resource->periodic->period) { + PRINTF("Periodic resource: %p (%s)\n", resource->periodic, + resource->periodic->resource->url); + list_add(restful_periodic_services, resource->periodic); + } +} +/*---------------------------------------------------------------------------*/ +/*- Internal API ------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +list_t +rest_get_resources(void) +{ + return restful_services; +} +/*---------------------------------------------------------------------------*/ +int +rest_invoke_restful_service(void *request, void *response, uint8_t *buffer, + uint16_t buffer_size, int32_t *offset) +{ + uint8_t found = 0; + uint8_t allowed = 1; + + resource_t *resource = NULL; + const char *url = NULL; + + for(resource = (resource_t *)list_head(restful_services); + resource; resource = resource->next) { + + /* if the web service handles that kind of requests and urls matches */ + if((REST.get_url(request, &url) == strlen(resource->url) + || (REST.get_url(request, &url) > strlen(resource->url) + && (resource->flags & HAS_SUB_RESOURCES))) + && strncmp(resource->url, url, strlen(resource->url)) == 0) { + found = 1; + rest_resource_flags_t method = REST.get_method_type(request); + + PRINTF("/%s, method %u, resource->flags %u\n", resource->url, + (uint16_t)method, resource->flags); + + if((method & METHOD_GET) && resource->get_handler != NULL) { + /* call handler function */ + resource->get_handler(request, response, buffer, buffer_size, offset); + } else if((method & METHOD_POST) && resource->post_handler != NULL) { + /* call handler function */ + resource->post_handler(request, response, buffer, buffer_size, + offset); + } else if((method & METHOD_PUT) && resource->put_handler != NULL) { + /* call handler function */ + resource->put_handler(request, response, buffer, buffer_size, offset); + } else if((method & METHOD_DELETE) && resource->delete_handler != NULL) { + /* call handler function */ + resource->delete_handler(request, response, buffer, buffer_size, + offset); + } else { + allowed = 0; + REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED); + } + break; + } + } + if(!found) { + REST.set_response_status(response, REST.status.NOT_FOUND); + } else if(allowed) { + /* final handler for special flags */ + if(resource->flags & IS_OBSERVABLE) { + REST.subscription_handler(resource, request, response); + } + } + return found & allowed; +} +/*-----------------------------------------------------------------------------------*/ +PROCESS_THREAD(rest_engine_process, ev, data) +{ + PROCESS_BEGIN(); + + /* pause to let REST server finish adding resources. */ + PROCESS_PAUSE(); + + /* initialize the PERIODIC_RESOURCE timers, which will be handled by this process. */ + periodic_resource_t *periodic_resource = NULL; + + for(periodic_resource = + (periodic_resource_t *)list_head(restful_periodic_services); + periodic_resource; periodic_resource = periodic_resource->next) { + if(periodic_resource->periodic_handler && periodic_resource->period) { + PRINTF("Periodic: Set timer for /%s to %lu\n", + periodic_resource->resource->url, periodic_resource->period); + etimer_set(&periodic_resource->periodic_timer, + periodic_resource->period); + } + } + while(1) { + PROCESS_WAIT_EVENT(); + + if(ev == PROCESS_EVENT_TIMER) { + for(periodic_resource = + (periodic_resource_t *)list_head(restful_periodic_services); + periodic_resource; periodic_resource = periodic_resource->next) { + if(periodic_resource->period + && etimer_expired(&periodic_resource->periodic_timer)) { + + PRINTF("Periodic: etimer expired for /%s (period: %lu)\n", + periodic_resource->resource->url, periodic_resource->period); + + /* Call the periodic_handler function, which was checked during adding to list. */ + (periodic_resource->periodic_handler)(); + + etimer_reset(&periodic_resource->periodic_timer); + } + } + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/rest-engine/rest-engine.h b/apps/rest-engine/rest-engine.h new file mode 100644 index 000000000..3a2848f58 --- /dev/null +++ b/apps/rest-engine/rest-engine.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An abstraction layer for RESTful Web services (Erbium). + * Inspired by RESTful Contiki by Dogan Yazar. + * \author + * Matthias Kovatsch + */ + +#ifndef REST_ENGINE_H_ +#define REST_ENGINE_H_ + +#include +#include "contiki.h" +#include "contiki-lib.h" +#include "rest-constants.h" + +/* list of valid REST Enigne implementations */ +#define REGISTERED_ENGINE_ERBIUM coap_rest_implementation +#define REGISTERED_ENGINE_HELIUM http_rest_implementation + +/* sanity check for configured implementation */ +#if !defined(REST) || (REST != REGISTERED_ENGINE_ERBIUM && REST != REGISTERED_ENGINE_HELIUM) +#error "Define a valid REST Engine implementation (REST define)!" +#endif + +/* + * The maximum buffer size that is provided for resource responses and must be respected due to the limited IP buffer. + * Larger data must be handled by the resource and will be sent chunk-wise through a TCP stream or CoAP blocks. + */ +#ifndef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 64 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +struct resource_s; +struct periodic_resource_s; + +/* signatures of handler functions */ +typedef void (*restful_handler)(void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, + int32_t *offset); +typedef void (*restful_final_handler)(struct resource_s *resource, + void *request, void *response); +typedef void (*restful_periodic_handler)(void); +typedef void (*restful_response_handler)(void *data, void *response); +typedef void (*restful_trigger_handler)(void); + +/* signature of the rest-engine service function */ +typedef int (*service_callback_t)(void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, + int32_t *offset); + +/* data structure representing a resource in REST */ +struct resource_s { + struct resource_s *next; /* for LIST, points to next resource defined */ + const char *url; /*handled URL */ + rest_resource_flags_t flags; /* handled RESTful methods */ + const char *attributes; /* link-format attributes */ + restful_handler get_handler; /* handler function */ + restful_handler post_handler; /* handler function */ + restful_handler put_handler; /* handler function */ + restful_handler delete_handler; /* handler function */ + union { + struct periodic_resource_s *periodic; /* special data depending on flags */ + restful_trigger_handler trigger; + restful_trigger_handler resume; + }; +}; +typedef struct resource_s resource_t; + +struct periodic_resource_s { + struct periodic_resource_s *next; /* for LIST, points to next resource defined */ + const resource_t *resource; + uint32_t period; + struct etimer periodic_timer; + const restful_periodic_handler periodic_handler; +}; +typedef struct periodic_resource_s periodic_resource_t; + +/* + * Macro to define a RESTful resource. + * Resources are statically defined for the sake of efficiency and better memory management. + */ +#define RESOURCE(name, attributes, get_handler, post_handler, put_handler, delete_handler) \ + resource_t name = { NULL, NULL, NO_FLAGS, attributes, get_handler, post_handler, put_handler, delete_handler, { NULL } } + +#define PARENT_RESOURCE(name, attributes, get_handler, post_handler, put_handler, delete_handler) \ + resource_t name = { NULL, NULL, HAS_SUB_RESOURCES, attributes, get_handler, post_handler, put_handler, delete_handler, { NULL } } + +#define SEPARATE_RESOURCE(name, attributes, get_handler, post_handler, put_handler, delete_handler, resume_handler) \ + resource_t name = { NULL, NULL, IS_SEPARATE, attributes, get_handler, post_handler, put_handler, delete_handler, { .resume = resume_handler } } + +#define EVENT_RESOURCE(name, attributes, get_handler, post_handler, put_handler, delete_handler, event_handler) \ + resource_t name = { NULL, NULL, IS_OBSERVABLE, attributes, get_handler, post_handler, put_handler, delete_handler, { .trigger = event_handler } } + +/* + * Macro to define a periodic resource. + * The corresponding [name]_periodic_handler() function will be called every period. + * For instance polling a sensor and publishing a changed value to subscribed clients would be done there. + * The subscriber list will be maintained by the final_handler rest_subscription_handler() (see rest-mapping header file). + */ +#define PERIODIC_RESOURCE(name, attributes, get_handler, post_handler, put_handler, delete_handler, period, periodic_handler) \ + periodic_resource_t periodic_##name; \ + resource_t name = { NULL, NULL, IS_OBSERVABLE | IS_PERIODIC, attributes, get_handler, post_handler, put_handler, delete_handler, { .periodic = &periodic_##name } }; \ + periodic_resource_t periodic_##name = { NULL, &name, period, { { 0 } }, periodic_handler }; + +struct rest_implementation { + char *name; + + /** Initialize the REST implementation. */ + void (*init)(void); + + /** Register the RESTful service callback at implementation. */ + void (*set_service_callback)(service_callback_t callback); + + /** Get request URI path. */ + int (*get_url)(void *request, const char **url); + + /** Get the method of a request. */ + rest_resource_flags_t (*get_method_type)(void *request); + + /** Set the status code of a response. */ + int (*set_response_status)(void *response, unsigned int code); + + /** Get the content-type of a request. */ + int (*get_header_content_type)(void *request, + unsigned int *content_format); + + /** Set the Content-Type of a response. */ + int (*set_header_content_type)(void *response, + unsigned int content_format); + + /** Get the Accept types of a request. */ + int (*get_header_accept)(void *request, unsigned int *accept); + + /** Get the Length option of a request. */ + int (*get_header_length)(void *request, uint32_t *size); + + /** Set the Length option of a response. */ + int (*set_header_length)(void *response, uint32_t size); + + /** Get the Max-Age option of a request. */ + int (*get_header_max_age)(void *request, uint32_t *age); + + /** Set the Max-Age option of a response. */ + int (*set_header_max_age)(void *response, uint32_t age); + + /** Set the ETag option of a response. */ + int (*set_header_etag)(void *response, const uint8_t *etag, + size_t length); + + /** Get the If-Match option of a request. */ + int (*get_header_if_match)(void *request, const uint8_t **etag); + + /** Get the If-Match option of a request. */ + int (*get_header_if_none_match)(void *request); + + /** Get the Host option of a request. */ + int (*get_header_host)(void *request, const char **host); + + /** Set the location option of a response. */ + int (*set_header_location)(void *response, const char *location); + + /** Get the payload option of a request. */ + int (*get_request_payload)(void *request, const uint8_t **payload); + + /** Set the payload option of a response. */ + int (*set_response_payload)(void *response, const void *payload, + size_t length); + + /** Get the query string of a request. */ + int (*get_query)(void *request, const char **value); + + /** Get the value of a request query key-value pair. */ + int (*get_query_variable)(void *request, const char *name, + const char **value); + + /** Get the value of a request POST key-value pair. */ + int (*get_post_variable)(void *request, const char *name, + const char **value); + + /** Send the payload to all subscribers of the resource at url. */ + void (*notify_subscribers)(resource_t *resource); + + /** The handler for resource subscriptions. */ + restful_final_handler subscription_handler; + + /* REST status codes. */ + const struct rest_implementation_status status; + + /* REST content-types. */ + const struct rest_implementation_type type; +}; + +/* instance of REST implementation */ +extern const struct rest_implementation REST; + +/* + * To be called by HTTP/COAP server as a callback function when a new service request appears. + * This function dispatches the corresponding RESTful service. + */ +int rest_invoke_restful_service(void *request, void *response, + uint8_t *buffer, uint16_t buffer_size, + int32_t *offset); +/*---------------------------------------------------------------------------*/ +/** + * \brief Initializes REST framework and starts the HTTP or CoAP process. + */ +void rest_init_engine(void); +/*---------------------------------------------------------------------------*/ +/** + * + * \brief Resources wanted to be accessible should be activated with the following code. + * \param resource + * A RESTful resource defined through the RESOURCE macros. + * \param path + * The local URI path where to provide the resource. + */ +void rest_activate_resource(resource_t *resource, char *path); +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the list of registered RESTful resources. + * \return The resource list. + */ +list_t rest_get_resources(void); +/*---------------------------------------------------------------------------*/ + +#endif /*REST_ENGINE_H_ */ diff --git a/examples/er-rest-example/Makefile b/examples/er-rest-example/Makefile index 9a72bf63a..7c533a8e4 100644 --- a/examples/er-rest-example/Makefile +++ b/examples/er-rest-example/Makefile @@ -1,71 +1,37 @@ all: er-example-server er-example-client -# use this target explicitly if requried: er-plugtest-server - - -# variable for this Makefile -# configure CoAP implementation (3|7|12|13) (er-coap-07 also supports CoAP draft 08) -WITH_COAP=13 - - -# for some platforms -UIP_CONF_IPV6=1 -# IPv6 make config disappeared completely -CFLAGS += -DUIP_CONF_IPV6=1 +# use target "er-plugtest-server" explicitly when requried CONTIKI=../.. + +# Contiki IPv6 configuration +WITH_UIP6=1 +UIP_CONF_IPV6=1 +CFLAGS += -DUIP_CONF_IPV6=1 +CFLAGS += -DUIP_CONF_IPV6_RPL=1 + CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" -# variable for Makefile.include -ifneq ($(TARGET), minimal-net) -CFLAGS += -DUIP_CONF_IPV6_RPL=1 +# automatically build RESTful resources +REST_RESOURCES_DIR = ./resources +ifndef TARGET +REST_RESOURCES_FILES = $(notdir $(shell find $(REST_RESOURCES_DIR) -name '*.c')) else -# minimal-net does not support RPL under Linux and is mostly used to test CoAP only -${info INFO: compiling without RPL} -CFLAGS += -DUIP_CONF_IPV6_RPL=0 -CFLAGS += -DHARD_CODED_ADDRESS=\"fdfd::10\" -${info INFO: compiling with large buffers} -CFLAGS += -DUIP_CONF_BUFFER_SIZE=2048 -CFLAGS += -DREST_MAX_CHUNK_SIZE=1024 -CFLAGS += -DCOAP_MAX_HEADER_SIZE=640 +ifeq ($(TARGET), native) +REST_RESOURCES_FILES = $(notdir $(shell find $(REST_RESOURCES_DIR) -name '*.c')) +else +REST_RESOURCES_FILES = $(notdir $(shell find $(REST_RESOURCES_DIR) -name '*.c' ! -name 'res-plugtest*')) endif +endif + +PROJECTDIRS += $(REST_RESOURCES_DIR) +PROJECT_SOURCEFILES += $(REST_RESOURCES_FILES) # linker optimizations SMALL=1 -# REST framework, requires WITH_COAP -ifeq ($(WITH_COAP), 13) -${info INFO: compiling with CoAP-13} -CFLAGS += -DWITH_COAP=13 -CFLAGS += -DREST=coap_rest_implementation -CFLAGS += -DUIP_CONF_TCP=0 -APPS += er-coap-13 -else ifeq ($(WITH_COAP), 12) -${info INFO: compiling with CoAP-12} -CFLAGS += -DWITH_COAP=12 -CFLAGS += -DREST=coap_rest_implementation -CFLAGS += -DUIP_CONF_TCP=0 -APPS += er-coap-12 -else ifeq ($(WITH_COAP), 7) -${info INFO: compiling with CoAP-08} -CFLAGS += -DWITH_COAP=7 -CFLAGS += -DREST=coap_rest_implementation -CFLAGS += -DUIP_CONF_TCP=0 -APPS += er-coap-07 -else ifeq ($(WITH_COAP), 3) -${info INFO: compiling with CoAP-03} -CFLAGS += -DWITH_COAP=3 -CFLAGS += -DREST=coap_rest_implementation -CFLAGS += -DUIP_CONF_TCP=0 -APPS += er-coap-03 -else -${info INFO: compiling with HTTP} -CFLAGS += -DWITH_HTTP -CFLAGS += -DREST=http_rest_implementation -CFLAGS += -DUIP_CONF_TCP=1 -APPS += er-http-engine -endif - -APPS += erbium +# REST Engine shall use Erbium CoAP implementation +APPS += er-coap +APPS += rest-engine # optional rules to get assembly #CUSTOM_RULE_C_TO_OBJECTDIR_O = 1 @@ -73,6 +39,16 @@ APPS += erbium include $(CONTIKI)/Makefile.include +# minimal-net target is currently broken in Contiki +ifeq ($(TARGET), minimal-net) +CFLAGS += -DHARD_CODED_ADDRESS=\"fdfd::10\" +${info INFO: er-example compiling with large buffers} +CFLAGS += -DUIP_CONF_BUFFER_SIZE=1300 +CFLAGS += -DREST_MAX_CHUNK_SIZE=1024 +CFLAGS += -DCOAP_MAX_HEADER_SIZE=176 +CFLAGS += -DUIP_CONF_IPV6_RPL=0 +endif + # optional rules to get assembly #$(OBJECTDIR)/%.o: asmdir/%.S # $(CC) $(CFLAGS) -MMD -c $< -o $@ @@ -89,7 +65,10 @@ connect-router: $(CONTIKI)/tools/tunslip6 sudo $(CONTIKI)/tools/tunslip6 aaaa::1/64 connect-router-cooja: $(CONTIKI)/tools/tunslip6 - sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 aaaa::1/64 + sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 -p 60001 aaaa::1/64 + +connect-router-native: $(CONTIKI)/examples/ipv6/native-border-router/border-router.native + sudo $(CONTIKI)/exmples/ipv6/native-border-router/border-router.native -a 127.0.0.1 -p 60001 aaaa::1/64 connect-minimal: - sudo ip address add fdfd::1/64 dev tap0 + sudo ip address add fdfd::1/64 dev tap0 diff --git a/examples/er-rest-example/README.md b/examples/er-rest-example/README.md index 1aa35b091..fe82ed9b5 100644 --- a/examples/er-rest-example/README.md +++ b/examples/er-rest-example/README.md @@ -21,6 +21,7 @@ PRELIMINARIES You can disable RDC in border-router project-conf.h (not really required as BR keeps radio turned on). #undef NETSTACK_CONF_RDC #define NETSTACK_CONF_RDC nullrdc_driver +- Alternatively, you can use the native-border-router together with the slip-radio. - For convenience, define the Cooja addresses in /etc/hosts aaaa::0212:7401:0001:0101 cooja1 aaaa::0212:7402:0002:0202 cooja2 @@ -125,10 +126,10 @@ Under Windows/Cygwin, WPCAP might need a patch in DETAILS ------- -Erbium currently implements draft 13. Central features are commented in -er-example-server.c. In general, apps/er-coap-13 supports: +Erbium implements the Proposed Standard of CoAP. Central features are commented +in er-example-server.c. In general, apps/er-coap supports: -- All draft 13 header options +- All draft-18 header options - CON Retransmissions (note COAP_MAX_OPEN_TRANSACTIONS) - Blockwise Transfers (note REST_MAX_CHUNK_SIZE, see er-plugtest-server.c for Block1 uploads) @@ -138,24 +139,6 @@ er-example-server.c. In general, apps/er-coap-13 supports: - Observing Resources (see EVENT_ and PRERIODIC_RESOURCE, note COAP_MAX_OBSERVERS) -REST IMPLEMENTATIONS --------------------- - -The Makefile uses WITH_COAP to configure different implementations for the -Erbium (Er) REST Engine. - -- WITH_COAP=13 uses Erbium CoAP 13 apps/er-coap-13/. The default port for - coap-13 is 5683. -- WITH_COAP=12 uses Erbium CoAP 12 apps/er-coap-12/. The default port for - coap-12 is 5683. -- WITH_COAP=7 uses Erbium CoAP 08 apps/er-coap-07/. The default port for - coap-07/-08 is 5683. -- WITH_COAP=3 uses Erbium CoAP 03 apps/er-coap-03/. The default port for - coap-03 is 61616. er-coap-03 produces some warnings, as it not fully - maintained anymore. -- WITH_COAP=0 is a stub to link an Erbium HTTP engine that uses the same - resource abstraction (REST.x() functions and RESOURCE macros. - TODOs ----- diff --git a/examples/er-rest-example/er-example-client.c b/examples/er-rest-example/er-example-client.c index d177bbe54..fa3df6a22 100644 --- a/examples/er-rest-example/er-example-client.c +++ b/examples/er-rest-example/er-example-client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Matthias Kovatsch + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ /** * \file - * Erbium (Er) CoAP client example + * Erbium (Er) CoAP client example. * \author * Matthias Kovatsch */ @@ -39,49 +39,34 @@ #include #include #include - #include "contiki.h" #include "contiki-net.h" - +#include "er-coap-engine.h" #include "dev/button-sensor.h" -#if WITH_COAP == 3 -#include "er-coap-03-engine.h" -#elif WITH_COAP == 6 -#include "er-coap-06-engine.h" -#elif WITH_COAP == 7 -#include "er-coap-07-engine.h" -#elif WITH_COAP == 12 -#include "er-coap-12-engine.h" -#elif WITH_COAP == 13 -#include "er-coap-13-engine.h" -#else -#error "CoAP version defined by WITH_COAP not implemented" -#endif - - #define DEBUG 0 #if DEBUG +#include #define PRINTF(...) printf(__VA_ARGS__) #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) #else #define PRINTF(...) #define PRINT6ADDR(addr) #define PRINTLLADDR(addr) #endif -/* TODO: This server address is hard-coded for Cooja. */ -#define SERVER_NODE(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7402, 0x0002, 0x0202) /* cooja2 */ +/* FIXME: This server address is hard-coded for Cooja and link-local for unconnected border router. */ +#define SERVER_NODE(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7402, 0x0002, 0x0202) /* cooja2 */ +/* #define SERVER_NODE(ipaddr) uip_ip6addr(ipaddr, 0xbbbb, 0, 0, 0, 0, 0, 0, 0x1) */ -#define LOCAL_PORT UIP_HTONS(COAP_DEFAULT_PORT+1) +#define LOCAL_PORT UIP_HTONS(COAP_DEFAULT_PORT + 1) #define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT) #define TOGGLE_INTERVAL 10 -PROCESS(coap_client_example, "COAP Client Example"); -AUTOSTART_PROCESSES(&coap_client_example); - +PROCESS(er_example_client, "Erbium Example Client"); +AUTOSTART_PROCESSES(&er_example_client); uip_ipaddr_t server_ipaddr; static struct etimer et; @@ -89,7 +74,8 @@ static struct etimer et; /* Example URIs that can be queried. */ #define NUMBER_OF_URLS 4 /* leading and ending slashes only for demo purposes, get cropped automatically when setting the Uri-Path */ -char* service_urls[NUMBER_OF_URLS] = {".well-known/core", "/actuators/toggle", "battery/", "error/in//path"}; +char *service_urls[NUMBER_OF_URLS] = +{ ".well-known/core", "/actuators/toggle", "battery/", "error/in//path" }; #if PLATFORM_HAS_BUTTON static int uri_switch = 0; #endif @@ -101,19 +87,19 @@ client_chunk_handler(void *response) const uint8_t *chunk; int len = coap_get_payload(response, &chunk); + printf("|%.*s", len, (char *)chunk); } - - -PROCESS_THREAD(coap_client_example, ev, data) +PROCESS_THREAD(er_example_client, ev, data) { PROCESS_BEGIN(); - static coap_packet_t request[1]; /* This way the packet can be treated as pointer as usual. */ + static coap_packet_t request[1]; /* This way the packet can be treated as pointer as usual. */ + SERVER_NODE(&server_ipaddr); /* receives all CoAP messages */ - coap_receiver_init(); + coap_init_engine(); etimer_set(&et, TOGGLE_INTERVAL * CLOCK_SECOND); @@ -125,28 +111,29 @@ PROCESS_THREAD(coap_client_example, ev, data) while(1) { PROCESS_YIELD(); - if (etimer_expired(&et)) { + if(etimer_expired(&et)) { printf("--Toggle timer--\n"); /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */ - coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0 ); + coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0); coap_set_header_uri_path(request, service_urls[1]); const char msg[] = "Toggle!"; - coap_set_payload(request, (uint8_t *)msg, sizeof(msg)-1); + coap_set_payload(request, (uint8_t *)msg, sizeof(msg) - 1); PRINT6ADDR(&server_ipaddr); PRINTF(" : %u\n", UIP_HTONS(REMOTE_PORT)); - COAP_BLOCKING_REQUEST(&server_ipaddr, REMOTE_PORT, request, client_chunk_handler); + COAP_BLOCKING_REQUEST(&server_ipaddr, REMOTE_PORT, request, + client_chunk_handler); printf("\n--Done--\n"); etimer_reset(&et); #if PLATFORM_HAS_BUTTON - } else if (ev == sensors_event && data == &button_sensor) { + } else if(ev == sensors_event && data == &button_sensor) { /* send a request to notify the end of the process */ @@ -158,13 +145,13 @@ PROCESS_THREAD(coap_client_example, ev, data) PRINT6ADDR(&server_ipaddr); PRINTF(" : %u\n", UIP_HTONS(REMOTE_PORT)); - COAP_BLOCKING_REQUEST(&server_ipaddr, REMOTE_PORT, request, client_chunk_handler); + COAP_BLOCKING_REQUEST(&server_ipaddr, REMOTE_PORT, request, + client_chunk_handler); printf("\n--Done--\n"); - uri_switch = (uri_switch+1) % NUMBER_OF_URLS; + uri_switch = (uri_switch + 1) % NUMBER_OF_URLS; #endif - } } diff --git a/examples/er-rest-example/er-example-server.c b/examples/er-rest-example/er-example-server.c index f5d9ea9a6..ad922000a 100644 --- a/examples/er-rest-example/er-example-server.c +++ b/examples/er-rest-example/er-example-server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Matthias Kovatsch + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ /** * \file - * Erbium (Er) REST Engine example (with CoAP-specific code) + * Erbium (Er) REST Engine example. * \author * Matthias Kovatsch */ @@ -41,757 +41,53 @@ #include #include "contiki.h" #include "contiki-net.h" +#include "rest-engine.h" - -/* Define which resources to include to meet memory constraints. */ -#define REST_RES_HELLO 0 -#define REST_RES_CHUNKS 1 -#define REST_RES_SEPARATE 1 -#define REST_RES_PUSHING 1 -#define REST_RES_EVENT 1 -#define REST_RES_SUB 1 -#define REST_RES_LEDS 0 -#define REST_RES_TOGGLE 1 -#define REST_RES_LIGHT 0 -#define REST_RES_BATTERY 0 -#define REST_RES_RADIO 0 -#define REST_RES_MIRROR 0 /* causes largest code size */ - - - -#include "erbium.h" - - -#if defined (PLATFORM_HAS_BUTTON) +#if PLATFORM_HAS_BUTTON #include "dev/button-sensor.h" #endif -#if defined (PLATFORM_HAS_LEDS) -#include "dev/leds.h" -#endif -#if defined (PLATFORM_HAS_LIGHT) -#include "dev/light-sensor.h" -#endif -#if defined (PLATFORM_HAS_BATTERY) -#include "dev/battery-sensor.h" -#endif -#if defined (PLATFORM_HAS_SHT11) -#include "dev/sht11/sht11-sensor.h" -#endif -#if defined (PLATFORM_HAS_RADIO) -#include "dev/radio-sensor.h" -#endif - - -/* For CoAP-specific example: not required for normal RESTful Web service. */ -#if WITH_COAP == 3 -#include "er-coap-03.h" -#elif WITH_COAP == 7 -#include "er-coap-07.h" -#elif WITH_COAP == 12 -#include "er-coap-12.h" -#elif WITH_COAP == 13 -#include "er-coap-13.h" -#else -#warning "Erbium example without CoAP-specifc functionality" -#endif /* CoAP-specific example */ #define DEBUG 0 #if DEBUG +#include #define PRINTF(...) printf(__VA_ARGS__) #define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) #else #define PRINTF(...) #define PRINT6ADDR(addr) #define PRINTLLADDR(addr) #endif -/******************************************************************************/ -#if REST_RES_HELLO /* - * Resources are defined by the RESOURCE macro. - * Signature: resource name, the RESTful methods it handles, and its URI path (omitting the leading slash). + * Resources to be activated need to be imported through the extern keyword. + * The build system automatically compiles the resources in the corresponding sub-directory. */ -RESOURCE(helloworld, METHOD_GET, "hello", "title=\"Hello world: ?len=0..\";rt=\"Text\""); - -/* - * A handler function named [resource name]_handler must be implemented for each RESOURCE. - * A buffer for the response payload is provided through the buffer pointer. Simple resources can ignore - * preferred_size and offset, but must respect the REST_MAX_CHUNK_SIZE limit for the buffer. - * If a smaller block size is requested for CoAP, the REST framework automatically splits the data. - */ -void -helloworld_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - const char *len = NULL; - /* Some data that has the length up to REST_MAX_CHUNK_SIZE. For more, see the chunk resource. */ - char const * const message = "Hello World! ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy"; - int length = 12; /* |<-------->| */ - - /* The query string can be retrieved by rest_get_query() or parsed for its key-value pairs. */ - if (REST.get_query_variable(request, "len", &len)) { - length = atoi(len); - if (length<0) length = 0; - if (length>REST_MAX_CHUNK_SIZE) length = REST_MAX_CHUNK_SIZE; - memcpy(buffer, message, length); - } else { - memcpy(buffer, message, length); - } - - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); /* text/plain is the default, hence this option could be omitted. */ - REST.set_header_etag(response, (uint8_t *) &length, 1); - REST.set_response_payload(response, buffer, length); -} +extern resource_t + res_hello, + res_mirror, + res_chunks, + res_separate, + res_push, + res_event, + res_sub, + res_b1_sep_b2; +#if PLATFORM_HAS_LEDS +extern resource_t res_leds, res_toggle; +#endif +#if PLATFORM_HAS_LIGHT +extern resource_t res_light; #endif -/******************************************************************************/ -#if REST_RES_MIRROR -/* This resource mirrors the incoming request. It shows how to access the options and how to set them for the response. */ -RESOURCE(mirror, METHOD_GET | METHOD_POST | METHOD_PUT | METHOD_DELETE, "debug/mirror", "title=\"Returns your decoded message\";rt=\"Debug\""); +PROCESS(er_example_server, "Erbium Example Server"); +AUTOSTART_PROCESSES(&er_example_server); -void -mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - /* The ETag and Token is copied to the header. */ - uint8_t opaque[] = {0x0A, 0xBC, 0xDE}; - - /* 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. */ - unsigned int content_type = REST.get_header_content_type(request); - - /* The other getters copy the value (or string/array pointer) to the given pointers and return 1 for success or the length of strings/arrays. */ - uint32_t max_age_and_size = 0; - const char *str = NULL; - uint32_t observe = 0; - const uint8_t *bytes = NULL; - uint32_t block_num = 0; - uint8_t block_more = 0; - uint16_t block_size = 0; - const char *query = ""; - int len = 0; - - /* Mirror the received header options in the response payload. Unsupported getters (e.g., rest_get_header_observe() with HTTP) will return 0. */ - - int strpos = 0; - /* snprintf() counts the terminating '\0' to the size parameter. - * The additional byte is taken care of by allocating REST_MAX_CHUNK_SIZE+1 bytes in the REST framework. - * Add +1 to fill the complete buffer, as the payload does not need a terminating '\0'. */ - if (content_type!=-1) - { - strpos += snprintf((char *)buffer, REST_MAX_CHUNK_SIZE+1, "CT %u\n", content_type); - } - - /* Some getters such as for ETag or Location are omitted, as these options should not appear in a request. - * Max-Age might appear in HTTP requests or used for special purposes in CoAP. */ - if (strpos<=REST_MAX_CHUNK_SIZE && REST.get_header_max_age(request, &max_age_and_size)) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "MA %lu\n", max_age_and_size); - } - /* For HTTP this is the Length option, for CoAP it is the Size option. */ - if (strpos<=REST_MAX_CHUNK_SIZE && REST.get_header_length(request, &max_age_and_size)) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "SZ %lu\n", max_age_and_size); - } - - if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_header_host(request, &str))) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "UH %.*s\n", len, str); - } - -/* CoAP-specific example: actions not required for normal RESTful Web service. */ -#if WITH_COAP > 1 - if (strpos<=REST_MAX_CHUNK_SIZE && coap_get_header_observe(request, &observe)) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Ob %lu\n", observe); - } - if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_token(request, &bytes))) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "To 0x"); - int index = 0; - for (index = 0; index 03 */ -#endif /* CoAP-specific example */ - - if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_query(request, &query))) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Qu %.*s\n", len, query); - } - if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_request_payload(request, &bytes))) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%.*s", len, bytes); - } - - if (strpos >= REST_MAX_CHUNK_SIZE) - { - buffer[REST_MAX_CHUNK_SIZE-1] = 0xBB; /* '»' to indicate truncation */ - } - - REST.set_response_payload(response, buffer, strpos); - - PRINTF("/mirror options received: %s\n", buffer); - - /* Set dummy header options for response. Like getters, some setters are not implemented for HTTP and have no effect. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_header_max_age(response, 17); /* For HTTP, browsers will not re-request the page for 17 seconds. */ - REST.set_header_etag(response, opaque, 2); - REST.set_header_location(response, location); /* Initial slash is omitted by framework */ - REST.set_header_length(response, strpos); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */ - -/* CoAP-specific example: actions not required for normal RESTful Web service. */ -#if WITH_COAP > 1 - coap_set_header_uri_host(response, "tiki"); - coap_set_header_observe(response, 10); -#if WITH_COAP == 3 - coap_set_header_block(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */ -#else - coap_set_header_proxy_uri(response, "ftp://x"); - coap_set_header_block2(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */ - coap_set_header_block1(response, 23, 0, 16); - coap_set_header_accept(response, TEXT_PLAIN); - coap_set_header_if_none_match(response); -#endif /* CoAP > 03 */ -#endif /* CoAP-specific example */ -} -#endif /* REST_RES_MIRROR */ - -/******************************************************************************/ -#if REST_RES_CHUNKS -/* - * For data larger than REST_MAX_CHUNK_SIZE (e.g., stored in flash) resources must be aware of the buffer limitation - * and split their responses by themselves. To transfer the complete resource through a TCP stream or CoAP's blockwise transfer, - * the byte offset where to continue is provided to the handler as int32_t pointer. - * These chunk-wise resources must set the offset value to its new position or -1 of the end is reached. - * (The offset for CoAP's blockwise transfer can go up to 2'147'481'600 = ~2047 M for block size 2048 (reduced to 1024 in observe-03.) - */ -RESOURCE(chunks, METHOD_GET, "test/chunks", "title=\"Blockwise demo\";rt=\"Data\""); - -#define CHUNKS_TOTAL 2050 - -void -chunks_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - int32_t strpos = 0; - - /* Check the offset for boundaries of the resource data. */ - if (*offset>=CHUNKS_TOTAL) - { - REST.set_response_status(response, REST.status.BAD_OPTION); - /* A block error message should not exceed the minimum block size (16). */ - - const char *error_msg = "BlockOutOfScope"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - - /* Generate data until reaching CHUNKS_TOTAL. */ - while (strpos preferred_size) - { - strpos = preferred_size; - } - - /* Truncate if above CHUNKS_TOTAL bytes. */ - if (*offset+(int32_t)strpos > CHUNKS_TOTAL) - { - strpos = CHUNKS_TOTAL - *offset; - } - - REST.set_response_payload(response, buffer, strpos); - - /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ - *offset += strpos; - - /* Signal end of resource representation. */ - if (*offset>=CHUNKS_TOTAL) - { - *offset = -1; - } -} -#endif - -/******************************************************************************/ -#if REST_RES_SEPARATE && defined (PLATFORM_HAS_BUTTON) && WITH_COAP > 3 -/* Required to manually (=not by the engine) handle the response transaction. */ -#if WITH_COAP == 7 -#include "er-coap-07-separate.h" -#include "er-coap-07-transactions.h" -#elif WITH_COAP == 12 -#include "er-coap-12-separate.h" -#include "er-coap-12-transactions.h" -#elif WITH_COAP == 13 -#include "er-coap-13-separate.h" -#include "er-coap-13-transactions.h" -#endif -/* - * CoAP-specific example for separate responses. - * Note the call "rest_set_pre_handler(&resource_separate, coap_separate_handler);" in the main process. - * The pre-handler takes care of the empty ACK and updates the MID and message type for CON requests. - * The resource handler must store all information that required to finalize the response later. - */ -RESOURCE(separate, METHOD_GET, "test/separate", "title=\"Separate demo\""); - -/* A structure to store the required information */ -typedef struct application_separate_store { - /* Provided by Erbium to store generic request information such as remote address and token. */ - coap_separate_t request_metadata; - /* Add fields for addition information to be stored for finalizing, e.g.: */ - char buffer[16]; -} application_separate_store_t; - -static uint8_t separate_active = 0; -static application_separate_store_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) - { - coap_separate_reject(); - } - else - { - separate_active = 1; - - /* Take over and skip response by engine. */ - coap_separate_accept(request, &separate_store->request_metadata); - /* Be aware to respect the Block2 option, which is also stored in the coap_separate_t. */ - - /* - * 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->request_metadata.mid, &separate_store->request_metadata.addr, separate_store->request_metadata.port)) ) - { - coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ - - /* Restore the request information for the response. */ - coap_separate_resume(response, &separate_store->request_metadata, REST.status.OK); - - coap_set_payload(response, separate_store->buffer, strlen(separate_store->buffer)); - - /* - * Be aware to respect the Block2 option, which is also stored in the coap_separate_t. - * As it is a critical option, this example resource pretends to handle it for compliance. - */ - coap_set_header_block2(response, separate_store->request_metadata.block2_num, 0, separate_store->request_metadata.block2_size); - - /* 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 REST_RES_PUSHING -/* - * Example for a periodic resource. - * It takes an additional period parameter, which defines the interval to call [name]_periodic_handler(). - * A default post_handler takes care of subscriptions by managing a list of subscribers to notify. - */ -PERIODIC_RESOURCE(pushing, METHOD_GET, "test/push", "title=\"Periodic demo\";obs", 5*CLOCK_SECOND); - -void -pushing_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - - /* Usually, a CoAP server would response with the resource representation matching the periodic_handler. */ - const char *msg = "It's periodic!"; - REST.set_response_payload(response, msg, strlen(msg)); - - /* A post_handler that handles subscriptions will be called for periodic resources by the REST framework. */ -} - -/* - * Additionally, a handler function named [resource name]_handler must be implemented for each PERIODIC_RESOURCE. - * It will be called by the REST manager process with the defined period. - */ -void -pushing_periodic_handler(resource_t *r) -{ - static uint16_t obs_counter = 0; - static char content[11]; - - ++obs_counter; - - PRINTF("TICK %u for /%s\n", obs_counter, r->url); - - /* Build notification. */ - coap_packet_t notification[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(notification, COAP_TYPE_NON, REST.status.OK, 0 ); - coap_set_payload(notification, content, snprintf(content, sizeof(content), "TICK %u", obs_counter)); - - /* Notify the registered observers with the given message type, observe option, and payload. */ - REST.notify_subscribers(r, obs_counter, notification); -} -#endif - -/******************************************************************************/ -#if REST_RES_EVENT && defined (PLATFORM_HAS_BUTTON) -/* - * Example for an event resource. - * Additionally takes a period parameter that defines the interval to call [name]_periodic_handler(). - * A default post_handler takes care of subscriptions and manages a list of subscribers to notify. - */ -EVENT_RESOURCE(event, METHOD_GET, "sensors/button", "title=\"Event demo\";obs"); - -void -event_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - /* Usually, a CoAP server would response with the current resource representation. */ - const char *msg = "It's eventful!"; - REST.set_response_payload(response, (uint8_t *)msg, strlen(msg)); - - /* A post_handler that handles subscriptions/observing will be called for periodic resources by the framework. */ -} - -/* Additionally, a handler function named [resource name]_event_handler must be implemented for each EVENT_RESOURCE defined. - * It will be called by the REST manager process with the defined period. */ -void -event_event_handler(resource_t *r) -{ - static uint16_t event_counter = 0; - static char content[12]; - - ++event_counter; - - PRINTF("TICK %u for /%s\n", event_counter, r->url); - - /* Build notification. */ - coap_packet_t notification[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(notification, COAP_TYPE_CON, REST.status.OK, 0 ); - coap_set_payload(notification, content, snprintf(content, sizeof(content), "EVENT %u", event_counter)); - - /* Notify the registered observers with the given message type, observe option, and payload. */ - REST.notify_subscribers(r, event_counter, notification); -} -#endif /* PLATFORM_HAS_BUTTON */ - -/******************************************************************************/ -#if REST_RES_SUB -/* - * Example for a resource that also handles all its sub-resources. - * Use REST.get_url() to multiplex the handling of the request depending on the Uri-Path. - */ -RESOURCE(sub, METHOD_GET | HAS_SUB_RESOURCES, "test/path", "title=\"Sub-resource demo\""); - -void -sub_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - - const char *uri_path = NULL; - int len = REST.get_url(request, &uri_path); - int base_len = strlen(resource_sub.url); - - if (len==base_len) - { - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "Request any sub-resource of /%s", resource_sub.url); - } - else - { - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, ".%.*s", len-base_len, uri_path+base_len); - } - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); -} -#endif - -/******************************************************************************/ -#if defined (PLATFORM_HAS_LEDS) -/******************************************************************************/ -#if REST_RES_LEDS -/*A simple actuator example, depending on the color query parameter and post variable mode, corresponding led is activated or deactivated*/ -RESOURCE(leds, METHOD_POST | METHOD_PUT , "actuators/leds", "title=\"LEDs: ?color=r|g|b, POST/PUT mode=on|off\";rt=\"Control\""); - -void -leds_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - size_t len = 0; - const char *color = NULL; - const char *mode = NULL; - uint8_t led = 0; - int success = 1; - - if ((len=REST.get_query_variable(request, "color", &color))) { - PRINTF("color %.*s\n", len, color); - - if (strncmp(color, "r", len)==0) { - led = LEDS_RED; - } else if(strncmp(color,"g", len)==0) { - led = LEDS_GREEN; - } else if (strncmp(color,"b", len)==0) { - led = LEDS_BLUE; - } else { - success = 0; - } - } else { - success = 0; - } - - if (success && (len=REST.get_post_variable(request, "mode", &mode))) { - PRINTF("mode %s\n", mode); - - if (strncmp(mode, "on", len)==0) { - leds_on(led); - } else if (strncmp(mode, "off", len)==0) { - leds_off(led); - } else { - success = 0; - } - } else { - success = 0; - } - - if (!success) { - REST.set_response_status(response, REST.status.BAD_REQUEST); - } -} -#endif - -/******************************************************************************/ -#if REST_RES_TOGGLE -/* A simple actuator example. Toggles the red led */ -RESOURCE(toggle, METHOD_POST, "actuators/toggle", "title=\"Red LED\";rt=\"Control\""); -void -toggle_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - leds_toggle(LEDS_RED); -} -#endif -#endif /* PLATFORM_HAS_LEDS */ - -/******************************************************************************/ -#if REST_RES_LIGHT && defined (PLATFORM_HAS_LIGHT) -/* A simple getter example. Returns the reading from light sensor with a simple etag */ -RESOURCE(light, METHOD_GET, "sensors/light", "title=\"Photosynthetic and solar light (supports JSON)\";rt=\"LightSensor\""); -void -light_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - uint16_t light_photosynthetic = light_sensor.value(LIGHT_SENSOR_PHOTOSYNTHETIC); - uint16_t light_solar = light_sensor.value(LIGHT_SENSOR_TOTAL_SOLAR); - - const uint16_t *accept = NULL; - int num = REST.get_header_accept(request, &accept); - - if ((num==0) || (num && accept[0]==REST.type.TEXT_PLAIN)) - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%u;%u", light_photosynthetic, light_solar); - - REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); - } - else if (num && (accept[0]==REST.type.APPLICATION_XML)) - { - REST.set_header_content_type(response, REST.type.APPLICATION_XML); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "", light_photosynthetic, light_solar); - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); - } - else if (num && (accept[0]==REST.type.APPLICATION_JSON)) - { - REST.set_header_content_type(response, REST.type.APPLICATION_JSON); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'light':{'photosynthetic':%u,'solar':%u}}", light_photosynthetic, light_solar); - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); - } - else - { - REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); - const char *msg = "Supporting content-types text/plain, application/xml, and application/json"; - REST.set_response_payload(response, msg, strlen(msg)); - } -} -#endif /* PLATFORM_HAS_LIGHT */ - -/******************************************************************************/ -#if REST_RES_BATTERY && defined (PLATFORM_HAS_BATTERY) -/* A simple getter example. Returns the reading from light sensor with a simple etag */ -RESOURCE(battery, METHOD_GET, "sensors/battery", "title=\"Battery status\";rt=\"Battery\""); -void -battery_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - int battery = battery_sensor.value(0); - - const uint16_t *accept = NULL; - int num = REST.get_header_accept(request, &accept); - - if ((num==0) || (num && accept[0]==REST.type.TEXT_PLAIN)) - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%d", battery); - - REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); - } - else if (num && (accept[0]==REST.type.APPLICATION_JSON)) - { - REST.set_header_content_type(response, REST.type.APPLICATION_JSON); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'battery':%d}", battery); - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); - } - else - { - REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); - const char *msg = "Supporting content-types text/plain and application/json"; - REST.set_response_payload(response, msg, strlen(msg)); - } -} -#endif /* PLATFORM_HAS_BATTERY */ - - -#if defined (PLATFORM_HAS_RADIO) && REST_RES_RADIO -/* A simple getter example. Returns the reading of the rssi/lqi from radio sensor */ -RESOURCE(radio, METHOD_GET, "sensor/radio", "title=\"RADIO: ?p=lqi|rssi\";rt=\"RadioSensor\""); - -void -radio_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - size_t len = 0; - const char *p = NULL; - uint8_t param = 0; - int success = 1; - - const uint16_t *accept = NULL; - int num = REST.get_header_accept(request, &accept); - - if ((len=REST.get_query_variable(request, "p", &p))) { - PRINTF("p %.*s\n", len, p); - if (strncmp(p, "lqi", len)==0) { - param = RADIO_SENSOR_LAST_VALUE; - } else if(strncmp(p,"rssi", len)==0) { - param = RADIO_SENSOR_LAST_PACKET; - } else { - success = 0; - } - } else { - success = 0; - } - - if (success) { - if ((num==0) || (num && accept[0]==REST.type.TEXT_PLAIN)) - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%d", radio_sensor.value(param)); - - REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); - } - else if (num && (accept[0]==REST.type.APPLICATION_JSON)) - { - REST.set_header_content_type(response, REST.type.APPLICATION_JSON); - - if (param == RADIO_SENSOR_LAST_VALUE) { - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'lqi':%d}", radio_sensor.value(param)); - } else if (param == RADIO_SENSOR_LAST_PACKET) { - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'rssi':%d}", radio_sensor.value(param)); - } - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); - } - else - { - REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); - const char *msg = "Supporting content-types text/plain and application/json"; - REST.set_response_payload(response, msg, strlen(msg)); - } - } else { - REST.set_response_status(response, REST.status.BAD_REQUEST); - } -} -#endif - - - -PROCESS(rest_server_example, "Erbium Example Server"); -AUTOSTART_PROCESSES(&rest_server_example); - -PROCESS_THREAD(rest_server_example, ev, data) +PROCESS_THREAD(er_example_server, ev, data) { PROCESS_BEGIN(); + PROCESS_PAUSE(); + PRINTF("Starting Erbium Example Server\n"); #ifdef RF_CHANNEL @@ -809,70 +105,42 @@ PROCESS_THREAD(rest_server_example, ev, data) /* Initialize the REST engine. */ rest_init_engine(); - /* Activate the application-specific resources. */ -#if REST_RES_HELLO - rest_activate_resource(&resource_helloworld); + /* + * Bind the resources to their Uri-Path. + * WARNING: Activating twice only means alternate path, not two instances! + * All static variables are the same for each URI path. + */ + rest_activate_resource(&res_hello, "test/hello"); +/* rest_activate_resource(&res_mirror, "debug/mirror"); */ +/* rest_activate_resource(&res_chunks, "test/chunks"); */ +/* rest_activate_resource(&res_separate, "test/separate"); */ + rest_activate_resource(&res_push, "test/push"); +/* rest_activate_resource(&res_event, "sensors/button"); */ +/* rest_activate_resource(&res_sub, "test/sub"); */ +/* rest_activate_resource(&res_b1_sep_b2, "test/b1sepb2"); */ +#if PLATFORM_HAS_LEDS +/* rest_activate_resource(&res_leds, "actuators/leds"); */ + rest_activate_resource(&res_toggle, "actuators/toggle"); #endif -#if REST_RES_MIRROR - rest_activate_resource(&resource_mirror); -#endif -#if REST_RES_CHUNKS - rest_activate_resource(&resource_chunks); -#endif -#if REST_RES_PUSHING - rest_activate_periodic_resource(&periodic_resource_pushing); -#endif -#if defined (PLATFORM_HAS_BUTTON) && REST_RES_EVENT - rest_activate_event_resource(&resource_event); -#endif -#if defined (PLATFORM_HAS_BUTTON) && REST_RES_SEPARATE && WITH_COAP > 3 - /* No pre-handler anymore, user coap_separate_accept() and coap_separate_reject(). */ - rest_activate_resource(&resource_separate); -#endif -#if defined (PLATFORM_HAS_BUTTON) && (REST_RES_EVENT || (REST_RES_SEPARATE && WITH_COAP > 3)) - SENSORS_ACTIVATE(button_sensor); -#endif -#if REST_RES_SUB - rest_activate_resource(&resource_sub); -#endif -#if defined (PLATFORM_HAS_LEDS) -#if REST_RES_LEDS - rest_activate_resource(&resource_leds); -#endif -#if REST_RES_TOGGLE - rest_activate_resource(&resource_toggle); -#endif -#endif /* PLATFORM_HAS_LEDS */ -#if defined (PLATFORM_HAS_LIGHT) && REST_RES_LIGHT - SENSORS_ACTIVATE(light_sensor); - rest_activate_resource(&resource_light); -#endif -#if defined (PLATFORM_HAS_BATTERY) && REST_RES_BATTERY - SENSORS_ACTIVATE(battery_sensor); - rest_activate_resource(&resource_battery); -#endif -#if defined (PLATFORM_HAS_RADIO) && REST_RES_RADIO - SENSORS_ACTIVATE(radio_sensor); - rest_activate_resource(&resource_radio); +#if PLATFORM_HAS_LIGHT +/* rest_activate_resource(&res_light, "sensors/light"); */ #endif /* Define application-specific events here. */ while(1) { PROCESS_WAIT_EVENT(); -#if defined (PLATFORM_HAS_BUTTON) - if (ev == sensors_event && data == &button_sensor) { - PRINTF("BUTTON\n"); -#if REST_RES_EVENT +#if PLATFORM_HAS_BUTTON + if(ev == sensors_event && data == &button_sensor) { + PRINTF("*******BUTTON*******\n"); + /* Call the event_handler for this application-specific event. */ - event_event_handler(&resource_event); -#endif -#if REST_RES_SEPARATE && WITH_COAP>3 + res_event.trigger(); + /* Also call the separate response example handler. */ - separate_finalize_handler(); -#endif + res_separate.resume(); } #endif /* PLATFORM_HAS_BUTTON */ - } /* while (1) */ + } /* while (1) */ PROCESS_END(); } diff --git a/examples/er-rest-example/er-plugtest-server.c b/examples/er-rest-example/er-plugtest-server.c index 5a791a09c..ae637dc4c 100644 --- a/examples/er-rest-example/er-plugtest-server.c +++ b/examples/er-rest-example/er-plugtest-server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Matthias Kovatsch + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ /** * \file - * Server for the ETSI IoT CoAP Plugtests, Paris, France, 24 - 25 March 2012 + * Server for the ETSI IoT CoAP Plugtests, Las Vegas, NV, USA, Nov 2013. * \author * Matthias Kovatsch */ @@ -41,1179 +41,37 @@ #include #include "contiki.h" #include "contiki-net.h" - -#define MAX_PLUGFEST_PAYLOAD 64+1 /* +1 for the terminating zero, which is not transmitted */ -#define MAX_PLUGFEST_BODY 2048 -#define CHUNKS_TOTAL 2012 - -/* Define which resources to include to meet memory constraints. */ -#define REST_RES_TEST 1 -#define REST_RES_LONG 1 -#define REST_RES_QUERY 1 -#define REST_RES_LOC_QUERY 1 -#define REST_RES_MULTI 1 -#define REST_RES_LINKS 1 -#define REST_RES_PATH 1 -#define REST_RES_SEPARATE 1 -#define REST_RES_LARGE 1 -#define REST_RES_LARGE_UPDATE 1 -#define REST_RES_LARGE_CREATE 1 -#define REST_RES_OBS 1 - -#define REST_RES_MIRROR 1 - - - -#if !defined (CONTIKI_TARGET_MINIMAL_NET) -#warning "Should only be compiled for minimal-net!" -#endif - - - -#include "erbium.h" - -/* For CoAP-specific example: not required for normal RESTful Web service. */ -#if WITH_COAP==7 -#include "er-coap-07.h" -#elif WITH_COAP == 12 -#include "er-coap-12.h" -#elif WITH_COAP == 13 -#include "er-coap-13.h" -#else -#error "Plugtests server without CoAP" -#endif /* CoAP-specific example */ - -#define DEBUG 1 -#if DEBUG -#define PRINTF(...) printf(__VA_ARGS__) -#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) -#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) -#else -#define PRINTF(...) -#define PRINT6ADDR(addr) -#define PRINTLLADDR(addr) -#endif - - -#if REST_RES_TEST -/* - * Default test resource - */ -RESOURCE(test, METHOD_GET|METHOD_POST|METHOD_PUT|METHOD_DELETE, "test", "title=\"Default test resource\""); - -static uint8_t test_etag[8] = {0}; -static uint8_t test_etag_len = 1; -static uint8_t test_change = 1; -static uint8_t test_none_match_okay = 1; - -static -void -test_update_etag() -{ - int i; - test_etag_len = (random_rand() % 8) + 1; - for (i=0; i0 && len==test_etag_len && memcmp(test_etag, bytes, len)==0) - { - PRINTF("validate "); - REST.set_response_status(response, REST.status.NOT_MODIFIED); - REST.set_header_etag(response, test_etag, test_etag_len); - - test_change = 1; - PRINTF("### SERVER ACTION ### Resouce will change\n"); - } - else - { - /* Code 2.05 CONTENT is default. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_header_etag(response, test_etag, test_etag_len); - REST.set_header_max_age(response, 30); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); - } - } - else if (method & METHOD_POST) - { - PRINTF("POST "); - REST.set_response_status(response, REST.status.CREATED); - REST.set_header_location(response, "/location1/location2/location3"); - } - else if (method & METHOD_PUT) - { - PRINTF("PUT "); - - if (coap_get_header_if_none_match(request)) - { - if (test_none_match_okay) - { - REST.set_response_status(response, REST.status.CREATED); - - test_none_match_okay = 0; - PRINTF("### SERVER ACTION ### If-None-Match will FAIL\n"); - } - else - { - REST.set_response_status(response, PRECONDITION_FAILED_4_12); - - test_none_match_okay = 1; - PRINTF("### SERVER ACTION ### If-None-Match will SUCCEED\n"); - } - } - else if (((len = coap_get_header_if_match(request, &bytes))>0 && (len==test_etag_len && memcmp(test_etag, bytes, len)==0)) || len==0) - { - test_update_etag(); - REST.set_header_etag(response, test_etag, test_etag_len); - - REST.set_response_status(response, REST.status.CHANGED); - - if (len>0) - { - test_change = 1; - PRINTF("### SERVER ACTION ### Resouce will change\n"); - } - } - else - { - - PRINTF("Check %u/%u\n [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n [0x%02X%02X%02X%02X%02X%02X%02X%02X] ", len, test_etag_len, - bytes[0], - bytes[1], - bytes[2], - bytes[3], - bytes[4], - bytes[5], - bytes[6], - bytes[7], - test_etag[0], - test_etag[1], - test_etag[2], - test_etag[3], - test_etag[4], - test_etag[5], - test_etag[6], - test_etag[7] ); - - REST.set_response_status(response, PRECONDITION_FAILED_4_12); - } - } - else if (method & METHOD_DELETE) - { - PRINTF("DELETE "); - REST.set_response_status(response, REST.status.DELETED); - } - - PRINTF("(%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); -} - - -RESOURCE(create1, METHOD_PUT|METHOD_DELETE, "create1", "title=\"Default test resource\""); - -static uint8_t create1_exists = 0; - -void -create1_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - uint8_t method = REST.get_method_type(request); - - if (test_change) - { - test_update_etag(); - } - - PRINTF("/create1 "); - - if (method & METHOD_PUT) - { - PRINTF("PUT "); - - if (coap_get_header_if_none_match(request)) - { - if (!create1_exists) - { - REST.set_response_status(response, REST.status.CREATED); - - create1_exists = 1; - } - else - { - REST.set_response_status(response, PRECONDITION_FAILED_4_12); - } - } - else - { - REST.set_response_status(response, REST.status.CHANGED); - } - } - else if (method & METHOD_DELETE) - { - PRINTF("DELETE "); - REST.set_response_status(response, REST.status.DELETED); - - create1_exists = 0; - } -} - -RESOURCE(create2, METHOD_POST, "create2", "title=\"Creates on POST\""); - -void -create2_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - if (test_change) - { - test_update_etag(); - } - - PRINTF("/create2 "); - - REST.set_response_status(response, REST.status.CREATED); - REST.set_header_location(response, "/location1/location2/location3"); -} - -RESOURCE(create3, METHOD_PUT|METHOD_DELETE, "create3", "title=\"Default test resource\""); - -static uint8_t create3_exists = 0; - -void -create3_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - uint8_t method = REST.get_method_type(request); - - if (test_change) - { - test_update_etag(); - } - - PRINTF("/create3 "); - - if (method & METHOD_PUT) - { - PRINTF("PUT "); - - if (coap_get_header_if_none_match(request)) - { - if (!create3_exists) - { - REST.set_response_status(response, REST.status.CREATED); - - create3_exists = 1; - } - else - { - REST.set_response_status(response, PRECONDITION_FAILED_4_12); - } - } - else - { - REST.set_response_status(response, REST.status.CHANGED); - } - } - else if (method & METHOD_DELETE) - { - PRINTF("DELETE "); - REST.set_response_status(response, REST.status.DELETED); - - create3_exists = 0; - } -} - - - - - -RESOURCE(validate, METHOD_GET|METHOD_PUT, "validate", "title=\"Default test resource\""); - -static uint8_t validate_etag[8] = {0}; -static uint8_t validate_etag_len = 1; -static uint8_t validate_change = 1; - -static -void -validate_update_etag() -{ - int i; - validate_etag_len = (random_rand() % 8) + 1; - for (i=0; i0 && len==validate_etag_len && memcmp(validate_etag, bytes, len)==0) - { - PRINTF("validate "); - REST.set_response_status(response, REST.status.NOT_MODIFIED); - REST.set_header_etag(response, validate_etag, validate_etag_len); - - validate_change = 1; - PRINTF("### SERVER ACTION ### Resouce will change\n"); - } - else - { - /* Code 2.05 CONTENT is default. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_header_etag(response, validate_etag, validate_etag_len); - REST.set_header_max_age(response, 30); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); - } - } - else if (method & METHOD_PUT) - { - PRINTF("PUT "); - - if (((len = coap_get_header_if_match(request, &bytes))>0 && (len==validate_etag_len && memcmp(validate_etag, bytes, len)==0)) || len==0) - { - validate_update_etag(); - REST.set_header_etag(response, validate_etag, validate_etag_len); - - REST.set_response_status(response, REST.status.CHANGED); - - if (len>0) - { - validate_change = 1; - PRINTF("### SERVER ACTION ### Resouce will change\n"); - } - } - else - { - PRINTF("Check %u/%u\n [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n [0x%02X%02X%02X%02X%02X%02X%02X%02X] ", len, validate_etag_len, - bytes[0], - bytes[1], - bytes[2], - bytes[3], - bytes[4], - bytes[5], - bytes[6], - bytes[7], - validate_etag[0], - validate_etag[1], - validate_etag[2], - validate_etag[3], - validate_etag[4], - validate_etag[5], - validate_etag[6], - validate_etag[7] ); - - REST.set_response_status(response, PRECONDITION_FAILED_4_12); - } - } - - PRINTF("(%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); -} -#endif - -#if REST_RES_LONG -/* - * Long path resource - */ -RESOURCE(longpath, METHOD_GET, "seg1/seg2/seg3", "title=\"Long path resource\""); - -void -longpath_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - - uint8_t method = REST.get_method_type(request); - - PRINTF("/seg1/seg2/seg3 "); - if (method & METHOD_GET) - { - PRINTF("GET "); - /* Code 2.05 CONTENT is default. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); - } - PRINTF("(%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); -} -#endif - -#if REST_RES_QUERY -/* - * Resource accepting query parameters - */ -RESOURCE(query, METHOD_GET, "query", "title=\"Resource accepting query parameters\""); - -void -query_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - int len = 0; - const char *query = NULL; - - PRINTF("/query GET (%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); - - if ((len = REST.get_query(request, &query))) - { - PRINTF("Query: %.*s\n", len, query); - } - - /* Code 2.05 CONTENT is default. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u\nQuery: %.*s", coap_req->type, coap_req->code, coap_req->mid, len, query)); -} -#endif - -#if REST_RES_LOC_QUERY -/* - * Resource accepting query parameters - */ -RESOURCE(locquery, METHOD_POST, "location-query", "title=\"Resource accepting query parameters\""); - -void -locquery_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - - PRINTF("/location-query POST (%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); - - REST.set_response_status(response, REST.status.CREATED); - REST.set_header_location(response, "?first=1&second=2"); -} -#endif - -#if REST_RES_MULTI -/* - * Resource providing text/plain and application/xml - */ -RESOURCE(multi, METHOD_GET, "multi-format", "title=\"Resource providing text/plain and application/xml\";ct=\"0 41\""); -void -multi_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - - const uint16_t *accept = NULL; - int num = REST.get_header_accept(request, &accept); - - PRINTF("/multi-format GET (%s %u) %d\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid, num); - - if (num==0 || (num && accept[0]==REST.type.TEXT_PLAIN)) - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u%s", coap_req->type, coap_req->code, coap_req->mid, num ? "\nAccept: 0" : "")); -PRINTF("PLAIN\n"); - } - else if (num && (accept[0]==REST.type.APPLICATION_XML)) - { - REST.set_header_content_type(response, REST.type.APPLICATION_XML); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "", coap_req->type, coap_req->code, coap_req->mid, accept[0])); -PRINTF("XML\n"); - } - else - { - REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); - const char *msg = "Supporting content-types text/plain and application/xml"; - REST.set_response_payload(response, msg, strlen(msg)); - PRINTF("ERROR\n"); - } -} -#endif - -#if REST_RES_LINKS -/* - * Resources providing text/plain and application/xml - */ -RESOURCE(link1, METHOD_GET, "link1", "rt=\"Type1 Type2\";if=\"If1\""); -SUB_RESOURCE(link2, METHOD_GET, "link2", "rt=\"Type2 Type3\";if=\"If2\"", link1); -SUB_RESOURCE(link3, METHOD_GET, "link3", "rt=\"Type1 Type3\";if=\"foo\"", link1); - -void -link1_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - const char *msg = "Dummy link"; - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, msg, strlen(msg)); -} -#endif - -#if REST_RES_PATH -/* - * Resources providing text/plain and application/xml - */ -RESOURCE(path, METHOD_GET | HAS_SUB_RESOURCES, "path", "ct=\"40\""); - -void -path_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - - const char *uri_path = NULL; - int len = REST.get_url(request, &uri_path); - int base_len = strlen(resource_path.url); - - if (len==base_len) - { - REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT); - snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, ",,"); - } - else - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "/%.*s", len, uri_path); - } - - REST.set_response_payload(response, buffer, strlen((char *)buffer)); -} -#endif - -#if REST_RES_SEPARATE -/* Required to manually (=not by the engine) handle the response transaction. */ -#if WITH_COAP == 7 -#include "er-coap-07-separate.h" -#include "er-coap-07-transactions.h" -#elif WITH_COAP == 12 -#include "er-coap-12-separate.h" -#include "er-coap-12-transactions.h" -#elif WITH_COAP == 13 -#include "er-coap-13-separate.h" -#include "er-coap-13-transactions.h" -#endif -/* - * Resource which cannot be served immediately and which cannot be acknowledged in a piggy-backed way - */ -PERIODIC_RESOURCE(separate, METHOD_GET, "separate", "title=\"Resource which cannot be served immediately and which cannot be acknowledged in a piggy-backed way\"", 3*CLOCK_SECOND); - -/* A structure to store the required information */ -typedef struct application_separate_store { - /* Provided by Erbium to store generic request information such as remote address and token. */ - coap_separate_t request_metadata; - /* Add fields for addition information to be stored for finalizing, e.g.: */ - char buffer[MAX_PLUGFEST_PAYLOAD]; -} application_separate_store_t; - -static uint8_t separate_active = 0; -static application_separate_store_t separate_store[1]; - -void -separate_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - - PRINTF("/separate "); - if (separate_active) - { - PRINTF("REJECTED "); - coap_separate_reject(); - } - else - { - PRINTF("STORED "); - separate_active = 1; - - /* Take over and skip response by engine. */ - coap_separate_accept(request, &separate_store->request_metadata); - /* Be aware to respect the Block2 option, which is also stored in the coap_separate_t. */ - - snprintf(separate_store->buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid); - } - - PRINTF("(%s %u)\n", coap_req->type==COAP_TYPE_CON?"CON":"NON", coap_req->mid); -} - -void -separate_periodic_handler(resource_t *resource) -{ - if (separate_active) - { - PRINTF("/separate "); - coap_transaction_t *transaction = NULL; - if ( (transaction = coap_new_transaction(separate_store->request_metadata.mid, &separate_store->request_metadata.addr, separate_store->request_metadata.port)) ) - { - PRINTF("RESPONSE (%s %u)\n", separate_store->request_metadata.type==COAP_TYPE_CON?"CON":"NON", separate_store->request_metadata.mid); - - coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ - - /* Restore the request information for the response. */ - coap_separate_resume(response, &separate_store->request_metadata, CONTENT_2_05); - - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - coap_set_payload(response, separate_store->buffer, strlen(separate_store->buffer)); - - /* - * Be aware to respect the Block2 option, which is also stored in the coap_separate_t. - * As it is a critical option, this example resource pretends to handle it for compliance. - */ - coap_set_header_block2(response, separate_store->request_metadata.block2_num, 0, separate_store->request_metadata.block2_size); - - /* 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 { - PRINTF("ERROR (transaction)\n"); - } - } /* if (separate_active) */ -} -#endif - -#if REST_RES_LARGE - -/* double expansion */ -#define TO_STRING2(x) #x -#define TO_STRING(x) TO_STRING2(x) +#include "er-coap.h" +#include "er-coap-transactions.h" +#include "er-coap-separate.h" +#include "rest-engine.h" +#include "er-plugtest.h" /* - * Large resource + * Resources to be activated need to be imported through the extern keyword. + * The build system automatically compiles the resources in the corresponding + * sub-directory. */ -RESOURCE(large, METHOD_GET, "large", "title=\"Large resource\";rt=\"block\";sz=\"" TO_STRING(CHUNKS_TOTAL) "\""); - -void -large_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - int32_t strpos = 0; - - /* Check the offset for boundaries of the resource data. */ - if (*offset>=CHUNKS_TOTAL) - { - REST.set_response_status(response, REST.status.BAD_OPTION); - /* A block error message should not exceed the minimum block size (16). */ - - const char *error_msg = "BlockOutOfScope"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - - /* Generate data until reaching CHUNKS_TOTAL. */ - while (strpos preferred_size) - { - strpos = preferred_size; - } - - /* Truncate if above CHUNKS_TOTAL bytes. */ - if (*offset+(int32_t)strpos > CHUNKS_TOTAL) - { - strpos = CHUNKS_TOTAL - *offset; - } - - REST.set_response_payload(response, buffer, strpos); - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - - /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ - *offset += strpos; - - /* Signal end of resource representation. */ - if (*offset>=CHUNKS_TOTAL) - { - *offset = -1; - } -} -#endif - -#if REST_RES_LARGE_UPDATE -/* - * Large resource that can be updated using PUT method - */ -RESOURCE(large_update, METHOD_GET|METHOD_PUT, "large-update", "title=\"Large resource that can be updated using PUT method\";rt=\"block\";sz=\"" TO_STRING(MAX_PLUGFEST_BODY) "\""); - -static int32_t large_update_size = 0; -static uint8_t large_update_store[MAX_PLUGFEST_BODY] = {0}; -static unsigned int large_update_ct = -1; - -void -large_update_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - uint8_t method = REST.get_method_type(request); - - if (method & METHOD_GET) - { - /* Check the offset for boundaries of the resource data. */ - if (*offset>=large_update_size) - { - REST.set_response_status(response, REST.status.BAD_OPTION); - /* A block error message should not exceed the minimum block size (16). */ - - const char *error_msg = "BlockOutOfScope"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - - REST.set_response_payload(response, large_update_store+*offset, MIN(large_update_size - *offset, preferred_size)); - REST.set_header_content_type(response, large_update_ct); - - /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ - *offset += preferred_size; - - /* Signal end of resource representation. */ - if (*offset>=large_update_size) - { - *offset = -1; - } - } else { - uint8_t *incoming = NULL; - size_t len = 0; - - unsigned int ct = REST.get_header_content_type(request); - if (ct==-1) - { - REST.set_response_status(response, REST.status.BAD_REQUEST); - const char *error_msg = "NoContentType"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - - if ((len = REST.get_request_payload(request, (const uint8_t **) &incoming))) - { - if (coap_req->block1_num*coap_req->block1_size+len <= sizeof(large_update_store)) - { - memcpy(large_update_store+coap_req->block1_num*coap_req->block1_size, incoming, len); - large_update_size = coap_req->block1_num*coap_req->block1_size+len; - large_update_ct = REST.get_header_content_type(request); - - REST.set_response_status(response, REST.status.CHANGED); - coap_set_header_block1(response, coap_req->block1_num, 0, coap_req->block1_size); - } - else - { - REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE); - REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "%uB max.", sizeof(large_update_store))); - return; - } - } - else - { - REST.set_response_status(response, REST.status.BAD_REQUEST); - const char *error_msg = "NoPayload"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - } -} -#endif - -#if REST_RES_LARGE_CREATE -/* - * Large resource that can be created using POST method - */ -RESOURCE(large_create, METHOD_POST, "large-create", "title=\"Large resource that can be created using POST method\";rt=\"block\""); - -void -large_create_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - coap_packet_t *const coap_req = (coap_packet_t *) request; - - uint8_t *incoming = NULL; - size_t len = 0; - - unsigned int ct = REST.get_header_content_type(request); - if (ct==-1) - { - REST.set_response_status(response, REST.status.BAD_REQUEST); - const char *error_msg = "NoContentType"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - - if ((len = REST.get_request_payload(request, (const uint8_t **) &incoming))) - { - if (coap_req->block1_num*coap_req->block1_size+len <= 2048) - { - REST.set_response_status(response, REST.status.CREATED); - REST.set_header_location(response, "/nirvana"); - coap_set_header_block1(response, coap_req->block1_num, 0, coap_req->block1_size); - } - else - { - REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE); - const char *error_msg = "2048B max."; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } - } - else - { - REST.set_response_status(response, REST.status.BAD_REQUEST); - const char *error_msg = "NoPayload"; - REST.set_response_payload(response, error_msg, strlen(error_msg)); - return; - } -} -#endif - -#if REST_RES_OBS - -#if WITH_COAP == 12 -#include "er-coap-12-observing.h" -#elif WITH_COAP == 13 -#include "er-coap-13-observing.h" -#endif -/* - * Observable resource which changes every 5 seconds - */ -PERIODIC_RESOURCE(obs, METHOD_GET|METHOD_PUT|METHOD_DELETE, "obs", "title=\"Observable resource which changes every 5 seconds\";obs", 5*CLOCK_SECOND); - -static uint16_t obs_counter = 0; -static char obs_content[MAX_PLUGFEST_BODY]; -static size_t obs_content_len = 0; -static unsigned int obs_format = 0; - -static char obs_status = 0; - -static -void -obs_purge_list() -{ - PRINTF("### SERVER ACTION ### Purging obs list"); - coap_remove_observer_by_url(NULL, 0, resource_obs.url); -} - -void -obs_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - uint8_t method = request==NULL ? METHOD_GET : REST.get_method_type(request); - - /* Keep server log clean from ticking events */ - if (request!=NULL) - { - PRINTF("/obs "); - } - - if (method & METHOD_GET) - { - /* Keep server log clean from ticking events */ - if (request!=NULL) - { - PRINTF("GET "); - } - - REST.set_header_content_type(response, obs_format); - REST.set_header_max_age(response, 5); - - if (obs_content_len) - { - REST.set_header_content_type(response, obs_format); - REST.set_response_payload(response, obs_content, obs_content_len); - } - else - { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_response_payload(response, obs_content, snprintf(obs_content, MAX_PLUGFEST_PAYLOAD, "TICK %lu", obs_counter)); - } - /* A post_handler that handles subscriptions will be called for periodic resources by the REST framework. */ - } - else if (method & METHOD_PUT) - { - uint8_t *incoming = NULL; - unsigned int ct = REST.get_header_content_type(request); - - PRINTF("PUT "); - - if (ct!=obs_format) - { - obs_status = 1; - - obs_format = ct; - } else { - - obs_format = ct; - obs_content_len = REST.get_request_payload(request, (const uint8_t **) &incoming); - memcpy(obs_content, incoming, obs_content_len); - obs_periodic_handler(&resource_obs); - } - - REST.set_response_status(response, REST.status.CHANGED); - } - else if (method & METHOD_DELETE) - { - PRINTF("DELETE "); - - obs_status = 2; - - REST.set_response_status(response, REST.status.DELETED); - } - - /* Keep server log clean from ticking events */ - if (request!=NULL) - { - PRINTF("\n"); - } -} - -/* - * Additionally, a handler function named [resource name]_handler must be implemented for each PERIODIC_RESOURCE. - * It will be called by the REST manager process with the defined period. - */ -void -obs_periodic_handler(resource_t *r) -{ - ++obs_counter; - - //PRINTF("TICK %u for /%s\n", obs_counter, r->url); - - if (obs_status==1) - { - coap_packet_t notification[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(notification, COAP_TYPE_NON, INTERNAL_SERVER_ERROR_5_00, 0 ); - - /* Notify the registered observers with the given message type, observe option, and payload. */ - REST.notify_subscribers(&resource_obs, -1, notification); - - PRINTF("######### sending 5.00\n"); - - obs_purge_list(); - } - else if (obs_status==2) - { - - coap_packet_t notification[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(notification, COAP_TYPE_NON, NOT_FOUND_4_04, 0 ); - - - /* Notify the registered observers with the given message type, observe option, and payload. */ - REST.notify_subscribers(&resource_obs, -1, notification); - - obs_purge_list(); - - obs_counter = 0; - obs_content_len = 0; - } - else - { - /* Build notification. */ - /*TODO: REST.new_response() */ - coap_packet_t notification[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(notification, COAP_TYPE_NON, CONTENT_2_05, 0 ); - - /* Better use a generator function for both handlers that only takes *resonse. */ - obs_handler(NULL, notification, NULL, 0, NULL); - - /* Notify the registered observers with the given message type, observe option, and payload. */ - REST.notify_subscribers(r, obs_counter, notification); - } - obs_status = 0; -} -#endif - -#if REST_RES_MIRROR -/* This resource mirrors the incoming request. It shows how to access the options and how to set them for the response. */ -RESOURCE(mirror, METHOD_GET | METHOD_POST | METHOD_PUT | METHOD_DELETE, "debug/mirror", "title=\"Returns your decoded message\";rt=\"Debug\""); - -void -mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) -{ - /* The ETag and Token is copied to the header. */ - uint8_t opaque[] = {0x0A, 0xBC, 0xDE}; - - /* 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. */ - unsigned int content_type = REST.get_header_content_type(request); - - /* The other getters copy the value (or string/array pointer) to the given pointers and return 1 for success or the length of strings/arrays. */ - uint32_t max_age_and_size = 0; - const char *str = NULL; - uint32_t observe = 0; - const uint8_t *bytes = NULL; - const uint16_t *words = NULL; - uint32_t block_num = 0; - uint8_t block_more = 0; - uint16_t block_size = 0; - const char *query = ""; - int len = 0; - - /* Mirror the received header options in the response payload. Unsupported getters (e.g., rest_get_header_observe() with HTTP) will return 0. */ - - int strpos = 0; - /* snprintf() counts the terminating '\0' to the size parameter. - * The additional byte is taken care of by allocating REST_MAX_CHUNK_SIZE+1 bytes in the REST framework. - * Add +1 to fill the complete buffer, as the payload does not need a terminating '\0'. */ - - - if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_header_if_match(request, &bytes))) - { - strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "If-Match 0x"); - int index = 0; - for (index = 0; index= REST_MAX_CHUNK_SIZE) - { - buffer[REST_MAX_CHUNK_SIZE-1] = 0xBB; /* '»' to indicate truncation */ - } - - REST.set_response_payload(response, buffer, strpos); - - PRINTF("/mirror options received: %s\n", buffer); - - /* Set dummy header options for response. Like getters, some setters are not implemented for HTTP and have no effect. */ - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_header_max_age(response, 17); /* For HTTP, browsers will not re-request the page for 17 seconds. */ - REST.set_header_etag(response, opaque, 2); - REST.set_header_location(response, location); /* Initial slash is omitted by framework */ - REST.set_header_length(response, strpos); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */ - -/* CoAP-specific example: actions not required for normal RESTful Web service. */ - coap_set_header_uri_host(response, "Contiki"); - coap_set_header_observe(response, 10); - coap_set_header_proxy_uri(response, "ftp://x"); - //coap_set_header_block2(response, 42, 0, 64); - //coap_set_header_block1(response, 23, 0, 16); - coap_set_header_accept(response, APPLICATION_XML); - coap_set_header_accept(response, APPLICATION_ATOM_XML); - coap_set_header_if_none_match(response); -} -#endif /* REST_RES_MIRROR */ - - - - +extern resource_t + res_plugtest_test, + res_plugtest_validate, + res_plugtest_create1, + res_plugtest_create2, + res_plugtest_create3, + res_plugtest_longpath, + res_plugtest_query, + res_plugtest_locquery, + res_plugtest_multi, + res_plugtest_link1, + res_plugtest_link2, + res_plugtest_link3, + res_plugtest_path, + res_plugtest_separate, + res_plugtest_large, + res_plugtest_large_update, + res_plugtest_large_create, + res_plugtest_obs, + res_mirror; PROCESS(plugtest_server, "PlugtestServer"); AUTOSTART_PROCESSES(&plugtest_server); @@ -1240,59 +98,31 @@ PROCESS_THREAD(plugtest_server, ev, data) rest_init_engine(); /* Activate the application-specific resources. */ -#if REST_RES_TEST - rest_activate_resource(&resource_test); - rest_activate_resource(&resource_validate); - rest_activate_resource(&resource_create1); - rest_activate_resource(&resource_create2); - rest_activate_resource(&resource_create3); -#endif -#if REST_RES_LONG - rest_activate_resource(&resource_longpath); -#endif -#if REST_RES_QUERY - rest_activate_resource(&resource_query); -#endif -#if REST_RES_LOC_QUERY - rest_activate_resource(&resource_locquery); -#endif -#if REST_RES_MULTI - rest_activate_resource(&resource_multi); -#endif -#if REST_RES_LINKS - rest_activate_resource(&resource_link1); - rest_activate_resource(&resource_link2); - rest_activate_resource(&resource_link3); -#endif -#if REST_RES_PATH - rest_activate_resource(&resource_path); -#endif -#if REST_RES_SEPARATE - rest_activate_periodic_resource(&periodic_resource_separate); -#endif -#if REST_RES_LARGE - rest_activate_resource(&resource_large); -#endif -#if REST_RES_LARGE_UPDATE - large_update_ct = REST.type.APPLICATION_OCTET_STREAM; - rest_activate_resource(&resource_large_update); -#endif -#if REST_RES_LARGE_CREATE - rest_activate_resource(&resource_large_create); -#endif -#if REST_RES_OBS - rest_activate_periodic_resource(&periodic_resource_obs); -#endif + rest_activate_resource(&res_plugtest_test, "test"); + rest_activate_resource(&res_plugtest_validate, "validate"); + rest_activate_resource(&res_plugtest_create1, "create1"); + rest_activate_resource(&res_plugtest_create2, "create2"); + rest_activate_resource(&res_plugtest_create3, "create3"); + rest_activate_resource(&res_plugtest_longpath, "seg1/seg2/seg3"); + rest_activate_resource(&res_plugtest_query, "query"); + rest_activate_resource(&res_plugtest_locquery, "location-query"); + rest_activate_resource(&res_plugtest_multi, "multi-format"); + rest_activate_resource(&res_plugtest_link1, "link1"); + rest_activate_resource(&res_plugtest_link2, "link2"); + rest_activate_resource(&res_plugtest_link3, "link3"); + rest_activate_resource(&res_plugtest_path, "path"); + rest_activate_resource(&res_plugtest_separate, "separate"); + rest_activate_resource(&res_plugtest_large, "large"); + rest_activate_resource(&res_plugtest_large_update, "large-update"); + rest_activate_resource(&res_plugtest_large_create, "large-create"); + rest_activate_resource(&res_plugtest_obs, "obs"); -#if REST_RES_MIRROR - rest_activate_resource(&resource_mirror); -#endif + rest_activate_resource(&res_mirror, "mirror"); /* Define application-specific events here. */ while(1) { PROCESS_WAIT_EVENT(); - - } /* while (1) */ + } /* while (1) */ PROCESS_END(); } diff --git a/examples/er-rest-example/er-plugtest.h b/examples/er-rest-example/er-plugtest.h new file mode 100644 index 000000000..0b156656a --- /dev/null +++ b/examples/er-rest-example/er-plugtest.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Erbium (Er) CoAP client example + * \author + * Matthias Kovatsch + */ + +#ifndef __ER_PLUGTEST_H__ +#define __ER_PLUGTEST_H__ + +#if !defined(CONTIKI_TARGET_NATIVE) +#warning "Should only be compiled for native!" +#endif + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +/* double expansion */ +#define TO_STRING2(x) # x +#define TO_STRING(x) TO_STRING2(x) + +#define MAX_PLUGFEST_PAYLOAD 64 + 1 /* +1 for the terminating zero, which is not transmitted */ +#define MAX_PLUGFEST_BODY 2048 +#define CHUNKS_TOTAL 2012 + +#endif /* __ER_PLUGTEST_H__ */ diff --git a/examples/er-rest-example/project-conf.h b/examples/er-rest-example/project-conf.h index 128ccd0b2..040dc5525 100644 --- a/examples/er-rest-example/project-conf.h +++ b/examples/er-rest-example/project-conf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Matthias Kovatsch + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,63 +26,67 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * + * This file is part of the Contiki operating system. */ -#ifndef PROJECT_ERBIUM_CONF_H_ -#define PROJECT_ERBIUM_CONF_H_ +/** + * \file + * Erbium (Er) example project configuration. + * \author + * Matthias Kovatsch + */ -/* Some platforms have weird includes. */ -#undef IEEE802154_CONF_PANID +#ifndef __PROJECT_ERBIUM_CONF_H__ +#define __PROJECT_ERBIUM_CONF_H__ + +/* Custom channel and PAN ID configuration for your project. */ +/* + #undef RF_CHANNEL + #define RF_CHANNEL 26 + + #undef IEEE802154_CONF_PANID + #define IEEE802154_CONF_PANID 0xABCD + */ + +/* IP buffer size must match all other hops, in particular the border router. */ +/* + #undef UIP_CONF_BUFFER_SIZE + #define UIP_CONF_BUFFER_SIZE 256 + */ /* Disabling RDC for demo purposes. Core updates often require more memory. */ /* For projects, optimize memory and enable RDC again. */ #undef NETSTACK_CONF_RDC -#define NETSTACK_CONF_RDC nullrdc_driver +#define NETSTACK_CONF_RDC nullrdc_driver + +/* Disabling TCP on CoAP nodes. */ +#undef UIP_CONF_TCP +#define UIP_CONF_TCP 0 /* Increase rpl-border-router IP-buffer when using more than 64. */ #undef REST_MAX_CHUNK_SIZE -#define REST_MAX_CHUNK_SIZE 64 +#define REST_MAX_CHUNK_SIZE 48 /* Estimate your header size, especially when using Proxy-Uri. */ /* -#undef COAP_MAX_HEADER_SIZE -#define COAP_MAX_HEADER_SIZE 70 -*/ - -/* The IP buffer size must fit all other hops, in particular the border router. */ -/* -#undef UIP_CONF_BUFFER_SIZE -#define UIP_CONF_BUFFER_SIZE 1280 -*/ + #undef COAP_MAX_HEADER_SIZE + #define COAP_MAX_HEADER_SIZE 70 + */ /* Multiplies with chunk size, be aware of memory constraints. */ #undef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 4 +#define COAP_MAX_OPEN_TRANSACTIONS 4 -/* Must be <= open transaction number, default is COAP_MAX_OPEN_TRANSACTIONS-1. */ +/* Must be <= open transactions, default is COAP_MAX_OPEN_TRANSACTIONS-1. */ /* -#undef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS 2 -*/ + #undef COAP_MAX_OBSERVERS + #define COAP_MAX_OBSERVERS 2 + */ /* Filtering .well-known/core per query can be disabled to save space. */ -/* #undef COAP_LINK_FORMAT_FILTERING -#define COAP_LINK_FORMAT_FILTERING 0 -*/ +#define COAP_LINK_FORMAT_FILTERING 0 +#undef COAP_PROXY_OPTION_PROCESSING +#define COAP_PROXY_OPTION_PROCESSING 0 -/* Save some memory for the sky platform. */ -#undef NBR_TABLE_CONF_MAX_NEIGHBORS -#define NBR_TABLE_CONF_MAX_NEIGHBORS 10 -#undef UIP_CONF_MAX_ROUTES -#define UIP_CONF_MAX_ROUTES 10 - -/* Reduce 802.15.4 frame queue to save RAM. */ -#undef QUEUEBUF_CONF_NUM -#define QUEUEBUF_CONF_NUM 4 - -#undef SICSLOWPAN_CONF_FRAG -#define SICSLOWPAN_CONF_FRAG 1 - -#endif /* PROJECT_ERBIUM_CONF_H_ */ +#endif /* __PROJECT_ERBIUM_CONF_H__ */ diff --git a/examples/er-rest-example/resources/res-b1-sep-b2.c b/examples/er-rest-example/resources/res-b1-sep-b2.c new file mode 100644 index 000000000..be4d8f943 --- /dev/null +++ b/examples/er-rest-example/resources/res-b1-sep-b2.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, Lars Schmertmann . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Lars Schmertmann + */ + +#include +#include "rest-engine.h" +#include "er-coap-block1.h" +#include "er-coap-separate.h" +#include "er-coap-transactions.h" + +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +SEPARATE_RESOURCE(res_b1_sep_b2, "title=\"Block1 + Separate + Block2 demo\"", NULL, res_post_handler, NULL, NULL, NULL); + +#define MAX_DATA_LEN 256 + +static uint8_t big_msg[MAX_DATA_LEN]; +static size_t big_msg_len = 0; +static coap_separate_t request_metadata; + +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* Example allows only one request on time. There are no checks for multiply access !!! */ + if(*offset == 0) { + /* Incoming Data */ + if(coap_block1_handler(request, response, big_msg, &big_msg_len, MAX_DATA_LEN)) { + /* More Blocks will follow. Example waits for + * the last block and stores data into big_msg. + */ + return; + } + /* Last block was received. */ + coap_separate_accept(request, &request_metadata); + + /* Need Time for calculation now */ + uint32_t i; + for(i = 0; i <= 4096; i++) { + printf("\r% 4u\r", i); + } + printf("\n"); + + /* Send first block */ + coap_transaction_t *transaction = NULL; + if((transaction = coap_new_transaction(request_metadata.mid, &request_metadata.addr, request_metadata.port))) { + coap_packet_t resp[1]; /* This way the packet can be treated as pointer as usual. */ + + /* Restore the request information for the response. */ + coap_separate_resume(resp, &request_metadata, CONTENT_2_05); + + /* Set payload and block info */ + coap_set_payload(resp, big_msg, big_msg_len > request_metadata.block2_size ? request_metadata.block2_size : big_msg_len); + if(big_msg_len > request_metadata.block2_size) { + coap_set_header_block2(resp, 0, 1, request_metadata.block2_size); + } + + /* Warning: No check for serialization error. */ + transaction->packet_len = coap_serialize_message(resp, transaction->packet); + coap_send_transaction(transaction); + } + } else { + /* request for more blocks */ + if(*offset >= big_msg_len) { + coap_set_status_code(response, BAD_OPTION_4_02); + coap_set_payload(response, "BlockOutOfScope", 15); + return; + } + + memcpy(buffer, big_msg + *offset, 32); + if(big_msg_len - *offset < preferred_size) { + preferred_size = big_msg_len - *offset; + *offset = -1; + } else { + *offset += preferred_size; + } + coap_set_payload(response, buffer, preferred_size); + } +} diff --git a/examples/er-rest-example/resources/res-battery.c b/examples/er-rest-example/resources/res-battery.c new file mode 100644 index 000000000..3024246ed --- /dev/null +++ b/examples/er-rest-example/resources/res-battery.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include "contiki.h" + +#if PLATFORM_HAS_BATTERY + +#include +#include "rest-engine.h" +#include "dev/battery-sensor.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* A simple getter example. Returns the reading from light sensor with a simple etag */ +RESOURCE(battery, + "title=\"Battery status\";rt=\"Battery\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + int battery = battery_sensor.value(0); + + unsigned int accept = -1; + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%d", battery); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'battery':%d}", battery); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + const char *msg = "Supporting content-types text/plain and application/json"; + REST.set_response_payload(response, msg, strlen(msg)); + } +} +#endif /* PLATFORM_HAS_BATTERY */ diff --git a/examples/er-rest-example/resources/res-chunks.c b/examples/er-rest-example/resources/res-chunks.c new file mode 100644 index 000000000..79ad55094 --- /dev/null +++ b/examples/er-rest-example/resources/res-chunks.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* + * For data larger than REST_MAX_CHUNK_SIZE (e.g., when stored in flash) resources must be aware of the buffer limitation + * and split their responses by themselves. To transfer the complete resource through a TCP stream or CoAP's blockwise transfer, + * the byte offset where to continue is provided to the handler as int32_t pointer. + * These chunk-wise resources must set the offset value to its new position or -1 of the end is reached. + * (The offset for CoAP's blockwise transfer can go up to 2'147'481'600 = ~2047 M for block size 2048 (reduced to 1024 in observe-03.) + */ +RESOURCE(res_chunks, + "title=\"Blockwise demo\";rt=\"Data\"", + res_get_handler, + NULL, + NULL, + NULL); + +#define CHUNKS_TOTAL 2050 + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + int32_t strpos = 0; + + /* Check the offset for boundaries of the resource data. */ + if(*offset >= CHUNKS_TOTAL) { + REST.set_response_status(response, REST.status.BAD_OPTION); + /* A block error message should not exceed the minimum block size (16). */ + + const char *error_msg = "BlockOutOfScope"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + /* Generate data until reaching CHUNKS_TOTAL. */ + while(strpos < preferred_size) { + strpos += snprintf((char *)buffer + strpos, preferred_size - strpos + 1, "|%ld|", *offset); + } + + /* snprintf() does not adjust return value if truncated by size. */ + if(strpos > preferred_size) { + strpos = preferred_size; + /* Truncate if above CHUNKS_TOTAL bytes. */ + } + if(*offset + (int32_t)strpos > CHUNKS_TOTAL) { + strpos = CHUNKS_TOTAL - *offset; + } + REST.set_response_payload(response, buffer, strpos); + + /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ + *offset += strpos; + + /* Signal end of resource representation. */ + if(*offset >= CHUNKS_TOTAL) { + *offset = -1; + } +} diff --git a/examples/er-rest-example/resources/res-event.c b/examples/er-rest-example/resources/res-event.c new file mode 100644 index 000000000..d20f0caa9 --- /dev/null +++ b/examples/er-rest-example/resources/res-event.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_event_handler(); + +/* + * Example for an event resource. + * Additionally takes a period parameter that defines the interval to call [name]_periodic_handler(). + * A default post_handler takes care of subscriptions and manages a list of subscribers to notify. + */ +EVENT_RESOURCE(res_event, + "title=\"Event demo\";obs", + res_get_handler, + NULL, + NULL, + NULL, + res_event_handler); + +/* + * Use local resource state that is accessed by res_get_handler() and altered by res_event_handler() or PUT or POST. + */ +static int32_t event_counter = 0; + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload(response, buffer, snprintf((char *)buffer, preferred_size, "EVENT %lu", event_counter)); + + /* A post_handler that handles subscriptions/observing will be called for periodic resources by the framework. */ +} +/* + * Additionally, res_event_handler must be implemented for each EVENT_RESOURCE. + * It is called through .trigger(), usually from the server process. + */ +static void +res_event_handler() +{ + /* Do the update triggered by the event here, e.g., sampling a sensor. */ + ++event_counter; + + /* Usually a condition is defined under with subscribers are notified, e.g., event was above a threshold. */ + if(1) { + PRINTF("TICK %u for /%s\n", event_counter, res_event.url); + + /* Notify the registered observers which will trigger the res_get_handler to create the response. */ + REST.notify_subscribers(&res_event); + } +} diff --git a/examples/er-rest-example/resources/res-hello.c b/examples/er-rest-example/resources/res-hello.c new file mode 100644 index 000000000..9208f8b74 --- /dev/null +++ b/examples/er-rest-example/resources/res-hello.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include +#include "rest-engine.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* + * A handler function named [resource name]_handler must be implemented for each RESOURCE. + * A buffer for the response payload is provided through the buffer pointer. Simple resources can ignore + * preferred_size and offset, but must respect the REST_MAX_CHUNK_SIZE limit for the buffer. + * If a smaller block size is requested for CoAP, the REST framework automatically splits the data. + */ +RESOURCE(res_hello, + "title=\"Hello world: ?len=0..\";rt=\"Text\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + const char *len = NULL; + /* Some data that has the length up to REST_MAX_CHUNK_SIZE. For more, see the chunk resource. */ + char const *const message = "Hello World! ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy"; + int length = 12; /* |<-------->| */ + + /* The query string can be retrieved by rest_get_query() or parsed for its key-value pairs. */ + if(REST.get_query_variable(request, "len", &len)) { + length = atoi(len); + if(length < 0) { + length = 0; + } + if(length > REST_MAX_CHUNK_SIZE) { + length = REST_MAX_CHUNK_SIZE; + } + memcpy(buffer, message, length); + } else { + memcpy(buffer, message, length); + } REST.set_header_content_type(response, REST.type.TEXT_PLAIN); /* text/plain is the default, hence this option could be omitted. */ + REST.set_header_etag(response, (uint8_t *)&length, 1); + REST.set_response_payload(response, buffer, length); +} diff --git a/examples/er-rest-example/resources/res-leds.c b/examples/er-rest-example/resources/res-leds.c new file mode 100644 index 000000000..5fbcf6c77 --- /dev/null +++ b/examples/er-rest-example/resources/res-leds.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include "contiki.h" + +#if PLATFORM_HAS_LEDS + +#include +#include "rest-engine.h" +#include "dev/leds.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +static void res_post_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/*A simple actuator example, depending on the color query parameter and post variable mode, corresponding led is activated or deactivated*/ +RESOURCE(res_leds, + "title=\"LEDs: ?color=r|g|b, POST/PUT mode=on|off\";rt=\"Control\"", + NULL, + res_post_put_handler, + res_post_put_handler, + NULL); + +static void +res_post_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + size_t len = 0; + const char *color = NULL; + const char *mode = NULL; + uint8_t led = 0; + int success = 1; + + if((len = REST.get_query_variable(request, "color", &color))) { + PRINTF("color %.*s\n", len, color); + + if(strncmp(color, "r", len) == 0) { + led = LEDS_RED; + } else if(strncmp(color, "g", len) == 0) { + led = LEDS_GREEN; + } else if(strncmp(color, "b", len) == 0) { + led = LEDS_BLUE; + } else { + success = 0; + } + } else { + success = 0; + } if(success && (len = REST.get_post_variable(request, "mode", &mode))) { + PRINTF("mode %s\n", mode); + + if(strncmp(mode, "on", len) == 0) { + leds_on(led); + } else if(strncmp(mode, "off", len) == 0) { + leds_off(led); + } else { + success = 0; + } + } else { + success = 0; + } if(!success) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + } +} +#endif /* PLATFORM_HAS_LEDS */ diff --git a/examples/er-rest-example/resources/res-light.c b/examples/er-rest-example/resources/res-light.c new file mode 100644 index 000000000..226d814bb --- /dev/null +++ b/examples/er-rest-example/resources/res-light.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include "contiki.h" + +#if PLATFORM_HAS_LIGHT + +#include +#include "rest-engine.h" +#include "dev/light-sensor.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* A simple getter example. Returns the reading from light sensor with a simple etag */ +RESOURCE(res_light, + "title=\"Photosynthetic and solar light (supports JSON)\";rt=\"LightSensor\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + uint16_t light_photosynthetic = light_sensor.value(LIGHT_SENSOR_PHOTOSYNTHETIC); + uint16_t light_solar = light_sensor.value(LIGHT_SENSOR_TOTAL_SOLAR); + + unsigned int accept = -1; + REST.get_header_accept(request, &accept); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%u;%u", light_photosynthetic, light_solar); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "", light_photosynthetic, light_solar); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'light':{'photosynthetic':%u,'solar':%u}}", light_photosynthetic, light_solar); + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + const char *msg = "Supporting content-types text/plain, application/xml, and application/json"; + REST.set_response_payload(response, msg, strlen(msg)); + } +} +#endif /* PLATFORM_HAS_LIGHT */ diff --git a/examples/er-rest-example/resources/res-mirror.c b/examples/er-rest-example/resources/res-mirror.c new file mode 100644 index 000000000..e33bda6d3 --- /dev/null +++ b/examples/er-rest-example/resources/res-mirror.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]", (lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3], (lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +static void res_any_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* This resource mirrors the incoming request. It shows how to access the options and how to set them for the response. */ +RESOURCE(res_mirror, + "title=\"Returns your decoded message\";rt=\"Debug\"", + res_any_handler, + res_any_handler, + res_any_handler, + res_any_handler); + +static void +res_any_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* The ETag and Token is copied to the header. */ + uint8_t opaque[] = { 0x0A, 0xBC, 0xDE }; + + /* 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 }; + + /* No default my be assumed for the Content-Format. (Unsigned -1 means all bits set.) */ + unsigned int content_format = -1; + + /* The other getters copy the value (or string/array pointer) to the given pointers and return 1 for success or the length of strings/arrays. */ + uint32_t longint = 0; + const char *str = NULL; + const uint8_t *bytes = NULL; + uint32_t block_num = 0; + uint8_t block_more = 0; + uint16_t block_size = 0; + int len = 0; + + /* Mirror the received header options in the response payload. Unsupported getters (e.g., rest_get_header_observe() with HTTP) will return 0. */ + + int strpos = 0; + /* snprintf() counts the terminating '\0' to the size parameter. + * The additional byte is taken care of by allocating REST_MAX_CHUNK_SIZE+1 bytes in the REST framework. + * Add +1 to fill the complete buffer, as the payload does not need a terminating '\0'. */ + if(REST.get_header_content_type(request, &content_format)) { + strpos += snprintf((char *)buffer, REST_MAX_CHUNK_SIZE + 1, "CF %u\n", content_format); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = REST.get_header_accept(request, &content_format))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "Ac %u\n", content_format); + /* Some getters such as for ETag or Location are omitted, as these options should not appear in a request. + * Max-Age might appear in HTTP requests or used for special purposes in CoAP. */ + } + if(strpos <= REST_MAX_CHUNK_SIZE && REST.get_header_max_age(request, &longint)) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "MA %lu\n", longint); + /* For HTTP this is the Length option, for CoAP it is the Size option. */ + } + if(strpos <= REST_MAX_CHUNK_SIZE && REST.get_header_length(request, &longint)) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "SZ %lu\n", longint); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = REST.get_header_host(request, &str))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "UH %.*s\n", len, str); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = REST.get_url(request, &str))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "UP %.*s\n", len, str); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = REST.get_query(request, &str))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "UQ %.*s\n", len, str); + /* Undefined request options for debugging: actions not required for normal RESTful Web service. */ + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = coap_get_header_location_path(request, &str))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "LP %.*s\n", len, str); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = coap_get_header_location_query(request, &str))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "LQ %.*s\n", len, str); + /* CoAP-specific example: actions not required for normal RESTful Web service. */ + } + coap_packet_t *const coap_pkt = (coap_packet_t *)request; + + if(strpos <= REST_MAX_CHUNK_SIZE && coap_pkt->token_len > 0) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "To 0x"); + int index = 0; + for(index = 0; index < coap_pkt->token_len; ++index) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "%02X", coap_pkt->token[index]); + } + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "\n"); + } + + if(strpos <= REST_MAX_CHUNK_SIZE && IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "Ob %lu\n", coap_pkt->observe); + } + if(strpos <= REST_MAX_CHUNK_SIZE && IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "ET 0x"); + int index = 0; + for(index = 0; index < coap_pkt->etag_len; ++index) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "%02X", coap_pkt->etag[index]); + } + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "\n"); + } + if(strpos <= REST_MAX_CHUNK_SIZE && coap_get_header_block2(request, &block_num, &block_more, &block_size, NULL)) { /* This getter allows NULL pointers to get only a subset of the block parameters. */ + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "B2 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size); + } + if(strpos <= REST_MAX_CHUNK_SIZE && coap_get_header_block1(request, &block_num, &block_more, &block_size, NULL)) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "B1 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size); + } + if(strpos <= REST_MAX_CHUNK_SIZE && (len = REST.get_request_payload(request, &bytes))) { + strpos += snprintf((char *)buffer + strpos, REST_MAX_CHUNK_SIZE - strpos + 1, "%.*s", len, bytes); + } + if(strpos >= REST_MAX_CHUNK_SIZE) { + buffer[REST_MAX_CHUNK_SIZE - 1] = 0xBB; /* '»' to indicate truncation */ + } + REST.set_response_payload(response, buffer, strpos); + + PRINTF("/mirror options received: %s\n", buffer); + + /* Set dummy header options for response. Like getters, some setters are not implemented for HTTP and have no effect. */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_header_max_age(response, 17); /* For HTTP, browsers will not re-request the page for 17 seconds. */ + REST.set_header_etag(response, opaque, 2); + REST.set_header_location(response, location); /* Initial slash is omitted by framework */ + REST.set_header_length(response, strpos); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */ + +/* CoAP-specific example: actions not required for normal RESTful Web service. */ + coap_set_header_uri_host(response, "tiki"); + coap_set_header_observe(response, 10); + coap_set_header_proxy_uri(response, "ftp://x"); + coap_set_header_block2(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */ + coap_set_header_block1(response, 23, 0, 16); + coap_set_header_accept(response, TEXT_PLAIN); + coap_set_header_if_none_match(response); +} diff --git a/apps/er-coap-03/er-coap-03-transactions.h b/examples/er-rest-example/resources/res-plugtest-create1.c similarity index 55% rename from apps/er-coap-03/er-coap-03-transactions.h rename to examples/er-rest-example/resources/res-plugtest-create1.c index b109fa992..4ba5f6181 100644 --- a/apps/er-coap-03/er-coap-03-transactions.h +++ b/examples/er-rest-example/resources/res-plugtest-create1.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,48 +31,50 @@ /** * \file - * CoAP module for reliable transport + * ETSI Plugtest resource * \author * Matthias Kovatsch */ -#ifndef COAP_TRANSACTIONS_H_ -#define COAP_TRANSACTIONS_H_ +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" -#include "er-coap-03.h" +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); -/* - * The number of concurrent messages that can be stored for retransmission in the transaction layer. - */ -#ifndef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 4 -#endif /* COAP_MAX_OPEN_TRANSACTIONS */ +RESOURCE(res_plugtest_create1, + "title=\"Creates on PUT\"", + NULL, + NULL, + res_put_handler, + res_delete_handler); -/* container for transactions with message buffer and retransmission info */ -typedef struct coap_transaction { - struct coap_transaction *next; /* for LIST */ +static uint8_t create1_exists = 0; - uint16_t tid; - struct etimer retrans_timer; - uint8_t retrans_counter; +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/create1 PUT"); - uip_ipaddr_t addr; - uint16_t port; + if(coap_get_header_if_none_match(request)) { + if(!create1_exists) { + REST.set_response_status(response, REST.status.CREATED); - restful_response_handler callback; - void *callback_data; + create1_exists = 1; + } else { + REST.set_response_status(response, PRECONDITION_FAILED_4_12); + } + } else { + REST.set_response_status(response, REST.status.CHANGED); + } +} +static void +res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/create1 DELETE "); + REST.set_response_status(response, REST.status.DELETED); - uint16_t packet_len; - uint8_t packet[COAP_MAX_PACKET_SIZE+1]; /* +1 for the terminating '\0' to simply and savely use snprintf(buf, len+1, "", ...) in the resource handler. */ -} coap_transaction_t; - -void coap_register_as_transaction_handler(); - -coap_transaction_t *coap_new_transaction(uint16_t tid, uip_ipaddr_t *addr, uint16_t port); -void coap_send_transaction(coap_transaction_t *t); -void coap_clear_transaction(coap_transaction_t *t); -coap_transaction_t *coap_get_transaction_by_tid(uint16_t tid); - -void coap_check_transactions(); - -#endif /* COAP_TRANSACTIONS_H_ */ + create1_exists = 0; +} diff --git a/apps/er-coap-07/er-coap-07-separate.h b/examples/er-rest-example/resources/res-plugtest-create2.c similarity index 68% rename from apps/er-coap-07/er-coap-07-separate.h rename to examples/er-rest-example/resources/res-plugtest-create2.c index 896485d42..25877ac70 100644 --- a/apps/er-coap-07/er-coap-07-separate.h +++ b/examples/er-rest-example/resources/res-plugtest-create2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,36 +31,30 @@ /** * \file - * CoAP module for separate responses + * ETSI Plugtest resource * \author * Matthias Kovatsch */ -#ifndef COAP_SEPARATE_H_ -#define COAP_SEPARATE_H_ +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" -#include "er-coap-07.h" +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); -typedef struct coap_separate { +RESOURCE(res_plugtest_create2, + "title=\"Creates on POST\"", + NULL, + res_post_handler, + NULL, + NULL); - uip_ipaddr_t addr; - uint16_t port; +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/create2 "); - coap_message_type_t type; - uint16_t mid; - - uint8_t token_len; - uint8_t token[COAP_TOKEN_LEN]; - - /* separate + blockwise is untested! */ - uint32_t block2_num; - uint16_t block2_size; - -} coap_separate_t; - -int coap_separate_handler(resource_t *resource, void *request, void *response); -void coap_separate_reject(); -int coap_separate_accept(void *request, coap_separate_t *separate_store); -void coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code); - -#endif /* COAP_SEPARATE_H_ */ + REST.set_response_status(response, REST.status.CREATED); + REST.set_header_location(response, "/location1/location2/location3"); +} diff --git a/examples/er-rest-example/resources/res-plugtest-create3.c b/examples/er-rest-example/resources/res-plugtest-create3.c new file mode 100644 index 000000000..2498c065a --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-create3.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_create3, + "title=\"Default test resource\"", + NULL, + NULL, + res_put_handler, + res_delete_handler); + +static uint8_t create3_exists = 0; + +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/create3 PUT "); + + if(coap_get_header_if_none_match(request)) { + if(!create3_exists) { + REST.set_response_status(response, REST.status.CREATED); + + create3_exists = 1; + } else { + REST.set_response_status(response, PRECONDITION_FAILED_4_12); + } + } else { + REST.set_response_status(response, REST.status.CHANGED); + } +} +static void +res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/create3 DELETE "); + REST.set_response_status(response, REST.status.DELETED); + + create3_exists = 0; +} diff --git a/examples/er-rest-example/resources/res-plugtest-large-create.c b/examples/er-rest-example/resources/res-plugtest-large-create.c new file mode 100644 index 000000000..888dfaa31 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-large-create.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* + * Large resource that can be created using POST method + */ +RESOURCE(res_plugtest_large_create, + "title=\"Large resource that can be created using POST method\";rt=\"block\"", + NULL, + res_post_handler, + NULL, + NULL); + +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + uint8_t *incoming = NULL; + size_t len = 0; + + unsigned int ct = -1; + + if(!REST.get_header_content_type(request, &ct)) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "NoContentType"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + if((len = REST.get_request_payload(request, (const uint8_t **)&incoming))) { + if(coap_req->block1_num * coap_req->block1_size + len <= 2048) { + REST.set_response_status(response, REST.status.CREATED); + REST.set_header_location(response, "/nirvana"); + coap_set_header_block1(response, coap_req->block1_num, 0, + coap_req->block1_size); + } else { + REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE); + const char *error_msg = "2048B max."; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + } else { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "NoPayload"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } +} diff --git a/examples/er-rest-example/resources/res-plugtest-large-update.c b/examples/er-rest-example/resources/res-plugtest-large-update.c new file mode 100644 index 000000000..e48d08b5f --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-large-update.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE( + res_plugtest_large_update, + "title=\"Large resource that can be updated using PUT method\";rt=\"block\";sz=\"" TO_STRING(MAX_PLUGFEST_BODY) "\"", + res_get_handler, + NULL, + res_put_handler, + NULL); + +static int32_t large_update_size = 0; +static uint8_t large_update_store[MAX_PLUGFEST_BODY] = { 0 }; +static unsigned int large_update_ct = APPLICATION_OCTET_STREAM; + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* Check the offset for boundaries of the resource data. */ + if(*offset >= large_update_size) { + REST.set_response_status(response, REST.status.BAD_OPTION); + /* A block error message should not exceed the minimum block size (16). */ + + const char *error_msg = "BlockOutOfScope"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + REST.set_response_payload(response, large_update_store + *offset, + MIN(large_update_size - *offset, preferred_size)); + REST.set_header_content_type(response, large_update_ct); + + /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ + *offset += preferred_size; + + /* Signal end of resource representation. */ + if(*offset >= large_update_size) { + *offset = -1; + } +} +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + uint8_t *incoming = NULL; + size_t len = 0; + + unsigned int ct = -1; + + if(!REST.get_header_content_type(request, &ct)) { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "NoContentType"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + if((len = REST.get_request_payload(request, (const uint8_t **)&incoming))) { + if(coap_req->block1_num * coap_req->block1_size + len <= sizeof(large_update_store)) { + memcpy( + large_update_store + coap_req->block1_num * coap_req->block1_size, + incoming, len); + large_update_size = coap_req->block1_num * coap_req->block1_size + len; + large_update_ct = ct; + + REST.set_response_status(response, REST.status.CHANGED); + coap_set_header_block1(response, coap_req->block1_num, 0, + coap_req->block1_size); + } else { + REST.set_response_status(response, + REST.status.REQUEST_ENTITY_TOO_LARGE); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "%uB max.", + sizeof(large_update_store))); + return; + } + } else { + REST.set_response_status(response, REST.status.BAD_REQUEST); + const char *error_msg = "NoPayload"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } +} diff --git a/examples/er-rest-example/resources/res-plugtest-large.c b/examples/er-rest-example/resources/res-plugtest-large.c new file mode 100644 index 000000000..d997dd927 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-large.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_large, + "title=\"Large resource\";rt=\"block\";sz=\"" TO_STRING(CHUNKS_TOTAL) "\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + int32_t strpos = 0; + + /* Check the offset for boundaries of the resource data. */ + if(*offset >= CHUNKS_TOTAL) { + REST.set_response_status(response, REST.status.BAD_OPTION); + /* A block error message should not exceed the minimum block size (16). */ + + const char *error_msg = "BlockOutOfScope"; + REST.set_response_payload(response, error_msg, strlen(error_msg)); + return; + } + + /* Generate data until reaching CHUNKS_TOTAL. */ + while(strpos < preferred_size) { + strpos += snprintf((char *)buffer + strpos, preferred_size - strpos + 1, + "|%ld|", *offset); + } + + /* snprintf() does not adjust return value if truncated by size. */ + if(strpos > preferred_size) { + strpos = preferred_size; + /* Truncate if above CHUNKS_TOTAL bytes. */ + } + if(*offset + (int32_t)strpos > CHUNKS_TOTAL) { + strpos = CHUNKS_TOTAL - *offset; + } + REST.set_response_payload(response, buffer, strpos); + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + + /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */ + *offset += strpos; + + /* Signal end of resource representation. */ + if(*offset >= CHUNKS_TOTAL) { + *offset = -1; + } +} diff --git a/examples/er-rest-example/resources/res-plugtest-links.c b/examples/er-rest-example/resources/res-plugtest-links.c new file mode 100644 index 000000000..be68aadf7 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-links.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_link1, + "rt=\"Type1 Type2\";if=\"If1\"", + res_get_handler, + NULL, + NULL, + NULL); +RESOURCE(res_plugtest_link2, + "rt=\"Type2 Type3\";if=\"If2\"", + res_get_handler, + NULL, + NULL, + NULL); +RESOURCE(res_plugtest_link3, + "rt=\"Type1 Type3\";if=\"foo\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + const char *msg = "Dummy link"; + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload(response, msg, strlen(msg)); +} diff --git a/examples/er-rest-example/resources/res-plugtest-locquery.c b/examples/er-rest-example/resources/res-plugtest-locquery.c new file mode 100644 index 000000000..64e79ff25 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-locquery.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_locquery, + "title=\"Resource accepting query parameters\"", + NULL, + res_post_handler, + NULL, + NULL); + +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF( + "/location-query POST (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + REST.set_response_status(response, REST.status.CREATED); + REST.set_header_location(response, "?first=1&second=2"); +} diff --git a/examples/er-rest-example/resources/res-plugtest-longpath.c b/examples/er-rest-example/resources/res-plugtest-longpath.c new file mode 100644 index 000000000..d6c4fb855 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-longpath.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_longpath, + "title=\"Long path resource\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/seg1/seg2/seg3 GET "); + /* Code 2.05 CONTENT is default. */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); + + PRINTF("(%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); +} diff --git a/examples/er-rest-example/resources/res-plugtest-multi.c b/examples/er-rest-example/resources/res-plugtest-multi.c new file mode 100644 index 000000000..281a2268f --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-multi.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_multi, + "title=\"Resource providing text/plain and application/xml\";ct=\"0 41\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + unsigned int accept = -1; + REST.get_header_accept(request, &accept); + + PRINTF("/multi-format GET (%s %u) ", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + "Type: %u\nCode: %u\nMID: %u%s", coap_req->type, coap_req->code, + coap_req->mid, accept != -1 ? "\nAccept: 0" : "")); + PRINTF("PLAIN\n"); + } else if(accept == REST.type.APPLICATION_XML) { + REST.set_header_content_type(response, REST.type.APPLICATION_XML); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + "", + coap_req->type, coap_req->code, coap_req->mid, accept)); + PRINTF("XML\n"); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + const char *msg = "Supporting content-types text/plain and application/xml"; + REST.set_response_payload(response, msg, strlen(msg)); + PRINTF("ERROR\n"); + } +} diff --git a/examples/er-rest-example/resources/res-plugtest-obs.c b/examples/er-rest-example/resources/res-plugtest-obs.c new file mode 100644 index 000000000..e202c2043 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-obs.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-coap-observe.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_periodic_handler(void); + +PERIODIC_RESOURCE(res_plugtest_obs, + "title=\"Observable resource which changes every 5 seconds\";obs", + res_get_handler, + NULL, + res_put_handler, + res_delete_handler, + 5 * CLOCK_SECOND, + res_periodic_handler); + +static int32_t obs_counter = 0; +static char obs_content[MAX_PLUGFEST_BODY]; +static size_t obs_content_len = 0; +static unsigned int obs_format = 0; + +static char obs_status = 0; + +static void +obs_purge_list() +{ + PRINTF("### SERVER ACTION ### Purging obs list"); + coap_remove_observer_by_uri(NULL, 0, res_plugtest_obs.url); +} +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* Keep server log clean from ticking events */ + if(request != NULL) { + PRINTF("/obs GET\n"); + } + REST.set_header_content_type(response, obs_format); + REST.set_header_max_age(response, 5); + + if(obs_content_len) { + REST.set_header_content_type(response, obs_format); + REST.set_response_payload(response, obs_content, obs_content_len); + } else { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload(response, obs_content, + snprintf(obs_content, MAX_PLUGFEST_PAYLOAD, "TICK %lu", obs_counter)); + } + /* A post_handler that handles subscriptions will be called for periodic resources by the REST framework. */ +} +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + uint8_t *incoming = NULL; + unsigned int ct = -1; + + REST.get_header_content_type(request, &ct); + + PRINTF("/obs PUT\n"); + + if(ct != obs_format) { + obs_status = 1; + obs_format = ct; + } else { + obs_content_len = REST.get_request_payload(request, + (const uint8_t **)&incoming); + memcpy(obs_content, incoming, obs_content_len); + res_periodic_handler(); + } + + REST.set_response_status(response, REST.status.CHANGED); +} +static void +res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + PRINTF("/obs DELETE\n"); + + obs_status = 2; + + REST.set_response_status(response, REST.status.DELETED); +} +/* + * Additionally, a handler function named [resource name]_handler must be implemented for each PERIODIC_RESOURCE. + * It will be called by the REST manager process with the defined period. + */ +static void +res_periodic_handler() +{ + ++obs_counter; + + /* PRINTF("TICK %u for /%s\n", obs_counter, r->url); */ + + if(obs_status == 1) { + + /* Notify the registered observers with the given message type, observe option, and payload. */ + REST.notify_subscribers(&res_plugtest_obs); + + PRINTF("######### sending 5.00\n"); + + obs_purge_list(); + } else if(obs_status == 2) { + + /* Notify the registered observers with the given message type, observe option, and payload. */ + REST.notify_subscribers(&res_plugtest_obs); + + obs_purge_list(); + + obs_counter = 0; + obs_content_len = 0; + } else { + /* Notify the registered observers with the given message type, observe option, and payload. */ + REST.notify_subscribers(&res_plugtest_obs); + } obs_status = 0; +} diff --git a/examples/er-rest-example/resources/res-plugtest-path.c b/examples/er-rest-example/resources/res-plugtest-path.c new file mode 100644 index 000000000..3566a51b9 --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-path.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +PARENT_RESOURCE(res_plugtest_path, + "title=\"Path test resource\";ct=\"40\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + + const char *uri_path = NULL; + int len = REST.get_url(request, &uri_path); + int base_len = strlen(res_plugtest_path.url); + + if(len == base_len) { + REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT); + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + ",,"); + } else { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "/%.*s", len, uri_path); + } + + REST.set_response_payload(response, buffer, strlen((char *)buffer)); +} diff --git a/examples/er-rest-example/resources/res-plugtest-query.c b/examples/er-rest-example/resources/res-plugtest-query.c new file mode 100644 index 000000000..538646c6e --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-query.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_query, + "title=\"Resource accepting query parameters\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + int len = 0; + const char *query = NULL; + + PRINTF( + "/query GET (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if((len = REST.get_query(request, &query))) { + PRINTF("Query: %.*s\n", len, query); + /* Code 2.05 CONTENT is default. */ + } + REST.set_header_content_type(response, + REST.type.TEXT_PLAIN); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + "Type: %u\nCode: %u\nMID: %u\nQuery: %.*s", coap_req->type, + coap_req->code, coap_req->mid, len, query)); +} diff --git a/examples/er-rest-example/resources/res-plugtest-separate.c b/examples/er-rest-example/resources/res-plugtest-separate.c new file mode 100644 index 000000000..a32311bba --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-separate.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-coap-transactions.h" +#include "er-coap-separate.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_resume_handler(void); + +PERIODIC_RESOURCE(res_plugtest_separate, + "title=\"Resource which cannot be served immediately and which cannot be acknowledged in a piggy-backed way\"", + res_get_handler, + NULL, + NULL, + NULL, + 3 * CLOCK_SECOND, + res_resume_handler); + +/* A structure to store the required information */ +typedef struct application_separate_store { + /* Provided by Erbium to store generic request information such as remote address and token. */ + coap_separate_t request_metadata; + /* Add fields for addition information to be stored for finalizing, e.g.: */ + char buffer[MAX_PLUGFEST_PAYLOAD]; +} application_separate_store_t; + +static uint8_t separate_active = 0; +static application_separate_store_t separate_store[1]; + +void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/separate "); + if(separate_active) { + PRINTF("REJECTED "); + coap_separate_reject(); + } else { + PRINTF("STORED "); + separate_active = 1; + + /* Take over and skip response by engine. */ + coap_separate_accept(request, &separate_store->request_metadata); + /* Be aware to respect the Block2 option, which is also stored in the coap_separate_t. */ + + snprintf(separate_store->buffer, MAX_PLUGFEST_PAYLOAD, + "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, + coap_req->mid); + } + + PRINTF("(%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); +} +static void +res_resume_handler() +{ + if(separate_active) { + PRINTF("/separate "); + coap_transaction_t *transaction = NULL; + if((transaction = coap_new_transaction(separate_store->request_metadata.mid, + &separate_store->request_metadata.addr, + separate_store->request_metadata.port))) { + PRINTF( + "RESPONSE (%s %u)\n", separate_store->request_metadata.type == COAP_TYPE_CON ? "CON" : "NON", separate_store->request_metadata.mid); + + coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ + + /* Restore the request information for the response. */ + coap_separate_resume(response, &separate_store->request_metadata, CONTENT_2_05); + + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + coap_set_payload(response, separate_store->buffer, + strlen(separate_store->buffer)); + + /* + * Be aware to respect the Block2 option, which is also stored in the coap_separate_t. + * As it is a critical option, this example resource pretends to handle it for compliance. + */ + coap_set_header_block2(response, + separate_store->request_metadata.block2_num, 0, + separate_store->request_metadata.block2_size); + + /* 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 { + PRINTF("ERROR (transaction)\n"); + } + } /* if (separate_active) */ +} diff --git a/examples/er-rest-example/resources/res-plugtest-test.c b/examples/er-rest-example/resources/res-plugtest-test.c new file mode 100644 index 000000000..27bba1f3b --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-test.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_test, "title=\"Default test resource\"", res_get_handler, res_post_handler, res_put_handler, res_delete_handler); + +static uint8_t test_etag[8] = { 0 }; +static uint8_t test_etag_len = 1; +static uint8_t test_change = 1; +static uint8_t test_none_match_okay = 1; + +static const uint8_t *bytes = NULL; +static size_t len = 0; + +static void +test_update_etag() +{ + int i; + test_etag_len = (random_rand() % 8) + 1; + for(i = 0; i < test_etag_len; ++i) { + test_etag[i] = random_rand(); + } + test_change = 0; + + PRINTF("### SERVER ACTION ### Changed ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", test_etag_len, test_etag[0], test_etag[1], test_etag[2], test_etag[3], test_etag[4], test_etag[5], test_etag[6], test_etag[7]); +} +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + if(test_change) { + test_update_etag(); + } + PRINTF("/test GET (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if((len = coap_get_header_etag(request, &bytes)) > 0 + && len == test_etag_len + && memcmp(test_etag, bytes, len) == 0) { + PRINTF("validate "); + REST.set_response_status(response, REST.status.NOT_MODIFIED); + REST.set_header_etag(response, test_etag, test_etag_len); + + test_change = 1; + PRINTF("### SERVER ACTION ### Resource will change\n"); + } else { + /* Code 2.05 CONTENT is default. */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_header_etag(response, test_etag, test_etag_len); + REST.set_header_max_age(response, 30); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, coap_req->mid)); + } +} +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/test POST (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + REST.set_response_status(response, REST.status.CREATED); + REST.set_header_location(response, "/location1/location2/location3"); +} +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/test PUT (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if(coap_get_header_if_none_match(request)) { + if(test_none_match_okay) { + REST.set_response_status(response, REST.status.CREATED); + + test_none_match_okay = 0; + PRINTF("### SERVER ACTION ### If-None-Match will FAIL\n"); + } else { + REST.set_response_status(response, PRECONDITION_FAILED_4_12); + + test_none_match_okay = 1; + PRINTF("### SERVER ACTION ### If-None-Match will SUCCEED\n"); + } + } else if(((len = coap_get_header_if_match(request, &bytes)) > 0 + && (len == test_etag_len + && memcmp(test_etag, bytes, len) == 0)) + || len == 0) { + test_update_etag(); + REST.set_header_etag(response, test_etag, test_etag_len); + + REST.set_response_status(response, REST.status.CHANGED); + + if(len > 0) { + test_change = 1; + PRINTF("### SERVER ACTION ### Resource will change\n"); + } + } else { + PRINTF("Check %u/%u\n [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", + len, + test_etag_len, + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + test_etag[0], test_etag[1], test_etag[2], test_etag[3], test_etag[4], test_etag[5], test_etag[6], test_etag[7]); + + REST.set_response_status(response, PRECONDITION_FAILED_4_12); + } +} +static void +res_delete_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/test DELETE (%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + REST.set_response_status(response, REST.status.DELETED); +} diff --git a/examples/er-rest-example/resources/res-plugtest-validate.c b/examples/er-rest-example/resources/res-plugtest-validate.c new file mode 100644 index 000000000..b92ffe74d --- /dev/null +++ b/examples/er-rest-example/resources/res-plugtest-validate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * ETSI Plugtest resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" +#include "er-plugtest.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +RESOURCE(res_plugtest_validate, + "title=\"Validation test resource\"", + res_get_handler, + NULL, + res_put_handler, + NULL); + +static uint8_t validate_etag[8] = { 0 }; +static uint8_t validate_etag_len = 1; +static uint8_t validate_change = 1; + +static const uint8_t *bytes = NULL; +static size_t len = 0; + +static void +validate_update_etag() +{ + int i; + validate_etag_len = (random_rand() % 8) + 1; + for(i = 0; i < validate_etag_len; ++i) { + validate_etag[i] = random_rand(); + } + validate_change = 0; + + PRINTF("### SERVER ACTION ### Changed ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", + validate_etag_len, validate_etag[0], validate_etag[1], validate_etag[2], validate_etag[3], validate_etag[4], validate_etag[5], validate_etag[6], validate_etag[7]); +} +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + if(validate_change) { + validate_update_etag(); + } + PRINTF("/validate GET"); + PRINTF("(%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if((len = coap_get_header_etag(request, &bytes)) > 0 + && len == validate_etag_len && memcmp(validate_etag, bytes, len) == 0) { + PRINTF("validate "); + REST.set_response_status(response, REST.status.NOT_MODIFIED); + REST.set_header_etag(response, validate_etag, validate_etag_len); + + validate_change = 1; + PRINTF("### SERVER ACTION ### Resouce will change\n"); + } else { + /* Code 2.05 CONTENT is default. */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_header_etag(response, validate_etag, validate_etag_len); + REST.set_header_max_age(response, 30); + REST.set_response_payload( + response, + buffer, + snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, + "Type: %u\nCode: %u\nMID: %u", coap_req->type, coap_req->code, + coap_req->mid)); + } +} +static void +res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + coap_packet_t *const coap_req = (coap_packet_t *)request; + + PRINTF("/validate PUT "); + PRINTF("(%s %u)\n", coap_req->type == COAP_TYPE_CON ? "CON" : "NON", coap_req->mid); + + if(((len = coap_get_header_if_match(request, &bytes)) > 0 + && (len == validate_etag_len + && memcmp(validate_etag, bytes, len) == 0)) + || len == 0) { + validate_update_etag(); + REST.set_header_etag(response, validate_etag, validate_etag_len); + + REST.set_response_status(response, REST.status.CHANGED); + + if(len > 0) { + validate_change = 1; + PRINTF("### SERVER ACTION ### Resouce will change\n"); + } + } else { + PRINTF( + "Check %u/%u\n [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n [0x%02X%02X%02X%02X%02X%02X%02X%02X] ", + len, + validate_etag_len, + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + validate_etag[0], validate_etag[1], validate_etag[2], validate_etag[3], validate_etag[4], validate_etag[5], validate_etag[6], validate_etag[7]); + + REST.set_response_status(response, PRECONDITION_FAILED_4_12); + } +} diff --git a/examples/er-rest-example/resources/res-push.c b/examples/er-rest-example/resources/res-push.c new file mode 100644 index 000000000..c169b407c --- /dev/null +++ b/examples/er-rest-example/resources/res-push.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_periodic_handler(void); + +PERIODIC_RESOURCE(res_push, + "title=\"Periodic demo\";obs", + res_get_handler, + NULL, + NULL, + NULL, + 5 * CLOCK_SECOND, + res_periodic_handler); + +/* + * Use local resource state that is accessed by res_get_handler() and altered by res_periodic_handler() or PUT or POST. + */ +static int32_t event_counter = 0; + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + /* + * For minimal complexity, request query and options should be ignored for GET on observable resources. + * Otherwise the requests must be stored with the observer list and passed by REST.notify_subscribers(). + * This would be a TODO in the corresponding files in contiki/apps/erbium/! + */ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_header_max_age(response, res_push.periodic->period / CLOCK_SECOND); + REST.set_response_payload(response, buffer, snprintf((char *)buffer, preferred_size, "VERY LONG EVENT %lu", event_counter)); + + /* The REST.subscription_handler() will be called for observable resources by the REST framework. */ +} +/* + * Additionally, a handler function named [resource name]_handler must be implemented for each PERIODIC_RESOURCE. + * It will be called by the REST manager process with the defined period. + */ +static void +res_periodic_handler() +{ + /* Do a periodic task here, e.g., sampling a sensor. */ + ++event_counter; + + /* Usually a condition is defined under with subscribers are notified, e.g., large enough delta in sensor reading. */ + if(1) { + /* Notify the registered observers which will trigger the res_get_handler to create the response. */ + REST.notify_subscribers(&res_push); + } +} diff --git a/examples/er-rest-example/resources/res-radio.c b/examples/er-rest-example/resources/res-radio.c new file mode 100644 index 000000000..ac09319a3 --- /dev/null +++ b/examples/er-rest-example/resources/res-radio.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include "contiki.h" + +#if PLATFORM_HAS_RADIO + +#include +#include "rest-engine.h" +#include "dev/radio-sensor.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* A simple getter example. Returns the reading of the rssi/lqi from radio sensor */ +RESOURCE(res_radio, + "title=\"RADIO: ?p=lqi|rssi\";rt=\"RadioSensor\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + size_t len = 0; + const char *p = NULL; + uint8_t param = 0; + int success = 1; + + unsigned int accept = -1; + REST.get_header_accept(request, &accept); + + if((len = REST.get_query_variable(request, "p", &p))) { + if(strncmp(p, "lqi", len) == 0) { + param = RADIO_SENSOR_LAST_VALUE; + } else if(strncmp(p, "rssi", len) == 0) { + param = RADIO_SENSOR_LAST_PACKET; + } else { + success = 0; + } + } else { + success = 0; + } if(success) { + if(accept == -1 || accept == REST.type.TEXT_PLAIN) { + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "%d", radio_sensor.value(param)); + + REST.set_response_payload(response, (uint8_t *)buffer, strlen((char *)buffer)); + } else if(accept == REST.type.APPLICATION_JSON) { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + + if(param == RADIO_SENSOR_LAST_VALUE) { + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'lqi':%d}", radio_sensor.value(param)); + } else if(param == RADIO_SENSOR_LAST_PACKET) { + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "{'rssi':%d}", radio_sensor.value(param)); + } + REST.set_response_payload(response, buffer, strlen((char *)buffer)); + } else { + REST.set_response_status(response, REST.status.NOT_ACCEPTABLE); + const char *msg = "Supporting content-types text/plain and application/json"; + REST.set_response_payload(response, msg, strlen(msg)); + } + } else { + REST.set_response_status(response, REST.status.BAD_REQUEST); + } +} +#endif /* PLATFORM_HAS_RADIO */ diff --git a/examples/er-rest-example/resources/res-separate.c b/examples/er-rest-example/resources/res-separate.c new file mode 100644 index 000000000..bb2248c92 --- /dev/null +++ b/examples/er-rest-example/resources/res-separate.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" +#include "er-coap-separate.h" +#include "er-coap-transactions.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); +static void res_resume_handler(void); + +SEPARATE_RESOURCE(res_separate, + "title=\"Separate demo\"", + res_get_handler, + NULL, + NULL, + NULL, + res_resume_handler); + +/* A structure to store the information required for the separate handler */ +typedef struct application_separate_store { + + /* Provided by Erbium to store generic request information such as remote address and token. */ + coap_separate_t request_metadata; + + /* Add fields for addition information to be stored for finalizing, e.g.: */ + char buffer[16]; +} application_separate_store_t; + +#define COAP_MAX_OPEN_SEPARATE 2 + +static uint8_t separate_active = 0; +static application_separate_store_t separate_store[COAP_MAX_OPEN_SEPARATE]; + +static void +res_get_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 >= COAP_MAX_OPEN_SEPARATE) { + coap_separate_reject(); + } else { + ++separate_active; + + /* Take over and skip response by engine. */ + coap_separate_accept(request, &separate_store->request_metadata); + /* Be aware to respect the Block2 option, which is also stored in the coap_separate_t. */ + + /* + * 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"); + } +} +static void +res_resume_handler() +{ + if(separate_active) { + coap_transaction_t *transaction = NULL; + if((transaction = coap_new_transaction(separate_store->request_metadata.mid, &separate_store->request_metadata.addr, separate_store->request_metadata.port))) { + coap_packet_t response[1]; /* This way the packet can be treated as pointer as usual. */ + + /* Restore the request information for the response. */ + coap_separate_resume(response, &separate_store->request_metadata, REST.status.OK); + + coap_set_payload(response, separate_store->buffer, strlen(separate_store->buffer)); + + /* + * Be aware to respect the Block2 option, which is also stored in the coap_separate_t. + * As it is a critical option, this example resource pretends to handle it for compliance. + */ + coap_set_header_block2(response, separate_store->request_metadata.block2_num, 0, separate_store->request_metadata.block2_size); + + /* 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). */ + + /* FIXME there could me more! */ + separate_active = 0; + } else { + /* + * Set timer for retry, send error message, ... + * The example simply waits for another button press. + */ + } + } /* if (separate_active) */ +} diff --git a/examples/er-rest-example/resources/res-sub.c b/examples/er-rest-example/resources/res-sub.c new file mode 100644 index 000000000..1d6f17aeb --- /dev/null +++ b/examples/er-rest-example/resources/res-sub.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + */ + +#include +#include "rest-engine.h" + +static void res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); + +/* + * Example for a resource that also handles all its sub-resources. + * Use REST.get_url() to multiplex the handling of the request depending on the Uri-Path. + */ +PARENT_RESOURCE(res_sub, + "title=\"Sub-resource demo\"", + res_get_handler, + NULL, + NULL, + NULL); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + + const char *uri_path = NULL; + int len = REST.get_url(request, &uri_path); + int base_len = strlen(res_sub.url); + + if(len == base_len) { + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, "Request any sub-resource of /%s", res_sub.url); + } else { + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, ".%.*s", len - base_len, uri_path + base_len); + } REST.set_response_payload(response, buffer, strlen((char *)buffer)); +} diff --git a/apps/er-coap-12/er-coap-12-separate.h b/examples/er-rest-example/resources/res-toggle.c similarity index 68% rename from apps/er-coap-12/er-coap-12-separate.h rename to examples/er-rest-example/resources/res-toggle.c index 7277cb0f0..a8ada909f 100644 --- a/apps/er-coap-12/er-coap-12-separate.h +++ b/examples/er-rest-example/resources/res-toggle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,36 +31,33 @@ /** * \file - * CoAP module for separate responses + * Example resource * \author * Matthias Kovatsch */ -#ifndef COAP_SEPARATE_H_ -#define COAP_SEPARATE_H_ +#include "contiki.h" -#include "er-coap-12.h" +#if PLATFORM_HAS_LEDS -typedef struct coap_separate { +#include +#include "contiki.h" +#include "rest-engine.h" +#include "dev/leds.h" - uip_ipaddr_t addr; - uint16_t port; +static void res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); - coap_message_type_t type; - uint16_t mid; +/* A simple actuator example. Toggles the red led */ +RESOURCE(res_toggle, + "title=\"Red LED\";rt=\"Control\"", + NULL, + res_post_handler, + NULL, + NULL); - uint8_t token_len; - uint8_t token[COAP_TOKEN_LEN]; - - /* separate + blockwise is untested! */ - uint32_t block2_num; - uint16_t block2_size; - -} coap_separate_t; - -int coap_separate_handler(resource_t *resource, void *request, void *response); -void coap_separate_reject(); -int coap_separate_accept(void *request, coap_separate_t *separate_store); -void coap_separate_resume(void *response, coap_separate_t *separate_store, uint8_t code); - -#endif /* COAP_SEPARATE_H_ */ +static void +res_post_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + leds_invert(LEDS_RED); +} +#endif /* PLATFORM_HAS_LEDS */ diff --git a/examples/er-rest-example/server-client-native.csc b/examples/er-rest-example/server-client-native.csc new file mode 100644 index 000000000..71e314075 --- /dev/null +++ b/examples/er-rest-example/server-client-native.csc @@ -0,0 +1,231 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + REST with RPL router + 1.0 + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 50.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.SkyMoteType + slipradio + Sky SLIP radio + [CONTIKI_DIR]/examples/ipv6/slip-radio/slip-radio.c + make slip-radio.sky TARGET=sky + [CONTIKI_DIR]/examples/ipv6/slip-radio/slip-radio.sky + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.SkyButton + org.contikios.cooja.mspmote.interfaces.SkyFlash + org.contikios.cooja.mspmote.interfaces.SkyCoffeeFilesystem + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspSerial + org.contikios.cooja.mspmote.interfaces.SkyLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + org.contikios.cooja.mspmote.interfaces.SkyTemperature + + + org.contikios.cooja.mspmote.SkyMoteType + server + Erbium Server + [CONTIKI_DIR]/examples/er-rest-example/er-example-server.c + make er-example-server.sky TARGET=sky + [CONTIKI_DIR]/examples/er-rest-example/er-example-server.sky + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.SkyButton + org.contikios.cooja.mspmote.interfaces.SkyFlash + org.contikios.cooja.mspmote.interfaces.SkyCoffeeFilesystem + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspSerial + org.contikios.cooja.mspmote.interfaces.SkyLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + org.contikios.cooja.mspmote.interfaces.SkyTemperature + + + org.contikios.cooja.mspmote.SkyMoteType + client + Erbium Client + [CONTIKI_DIR]/examples/er-rest-example/er-example-client.c + make er-example-client.sky TARGET=sky + [CONTIKI_DIR]/examples/er-rest-example/er-example-client.sky + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.SkyButton + org.contikios.cooja.mspmote.interfaces.SkyFlash + org.contikios.cooja.mspmote.interfaces.SkyCoffeeFilesystem + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspSerial + org.contikios.cooja.mspmote.interfaces.SkyLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + org.contikios.cooja.mspmote.interfaces.SkyTemperature + + + + + org.contikios.cooja.interfaces.Position + 30.303994886410642 + 17.22128424003353 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + slipradio + + + + + org.contikios.cooja.interfaces.Position + 46.57186415376375 + 37.25589203828498 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + server + + + + + org.contikios.cooja.interfaces.Position + 18.194682268367348 + 50.210548118402656 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + client + + + + org.contikios.cooja.plugins.SimControl + 259 + 0 + 179 + 1 + 2 + + + org.contikios.cooja.plugins.Visualizer + + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + org.contikios.cooja.plugins.skins.MoteTypeVisualizerSkin + org.contikios.cooja.plugins.skins.AttributeVisualizerSkin + org.contikios.cooja.plugins.skins.LEDVisualizerSkin + org.contikios.cooja.plugins.skins.AddressVisualizerSkin + 2.255467003316979 0.0 0.0 2.255467003316979 59.30641698643764 -13.478401994502008 + + 300 + 2 + 178 + 262 + 1 + + + org.contikios.cooja.plugins.LogListener + + + + + + 762 + 4 + 491 + 2 + 182 + + + org.contikios.cooja.plugins.RadioLogger + + 150 + + false + false + + + 451 + -1 + 305 + 73 + 140 + true + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + + + + + 25.49079397896416 + + 1624 + 5 + 252 + 6 + 712 + + + org.contikios.cooja.plugins.MoteInterfaceViewer + 1 + + Serial port + 0,0 + + 853 + 3 + 491 + 765 + 182 + + + SerialSocketServer + 0 + 422 + 1 + 82 + 606 + 51 + + + diff --git a/examples/er-rest-example/server-client.csc b/examples/er-rest-example/server-client.csc index b655fcbc9..470a9edfe 100644 --- a/examples/er-rest-example/server-client.csc +++ b/examples/er-rest-example/server-client.csc @@ -8,6 +8,7 @@ [APPS_DIR]/powertracker REST with RPL router + 1.0 123456 1000000 @@ -137,8 +138,8 @@ 259 0 179 - 0 - 0 + 2 + 1 org.contikios.cooja.plugins.Visualizer @@ -161,6 +162,7 @@ org.contikios.cooja.plugins.LogListener + 762 @@ -173,6 +175,9 @@ org.contikios.cooja.plugins.RadioLogger 150 + + false + false 451 @@ -201,7 +206,6 @@ - 125 25.49079397896416 1624 diff --git a/examples/er-rest-example/server-only.csc b/examples/er-rest-example/server-only.csc index 97cb8ffb5..6c0bba3e0 100644 --- a/examples/er-rest-example/server-only.csc +++ b/examples/er-rest-example/server-only.csc @@ -8,6 +8,7 @@ [APPS_DIR]/powertracker REST with RPL router + 1.0 123456 1000000 @@ -100,8 +101,8 @@ 259 0 179 - 0 - 0 + 2 + 1 org.contikios.cooja.plugins.Visualizer @@ -115,19 +116,20 @@ 7.9849281638410705 0.0 0.0 7.9849281638410705 -133.27812697619663 -225.04752569190535 300 - 5 + 1 175 - 263 - 3 + 262 + 2 org.contikios.cooja.plugins.LogListener + 560 - 2 + 3 326 1 293 @@ -136,6 +138,9 @@ org.contikios.cooja.plugins.RadioLogger 150 + + false + false 451 @@ -149,7 +154,7 @@ SerialSocketServer 0 422 - 3 + 4 74 39 199 @@ -163,11 +168,10 @@ - 125 25.49079397896416 1624 - 4 + 5 252 4 622 @@ -180,7 +184,7 @@ 0,0 702 - 1 + 2 646 564 2