From a522357d20611ba90676b6213a19d59a65105fa8 Mon Sep 17 00:00:00 2001 From: Matthias Kovatsch Date: Wed, 13 Feb 2013 17:43:04 +0100 Subject: [PATCH 1/2] Added Erbium coap-12 and coap-13. --- apps/er-coap-12/Makefile.er-coap-12 | 1 + apps/er-coap-12/er-coap-12-engine.c | 669 ++++++++++ apps/er-coap-12/er-coap-12-engine.h | 94 ++ apps/er-coap-12/er-coap-12-observing.c | 255 ++++ apps/er-coap-12/er-coap-12-observing.h | 82 ++ apps/er-coap-12/er-coap-12-separate.c | 117 ++ apps/er-coap-12/er-coap-12-separate.h | 66 + apps/er-coap-12/er-coap-12-transactions.c | 199 +++ apps/er-coap-12/er-coap-12-transactions.h | 78 ++ apps/er-coap-12/er-coap-12.c | 1155 +++++++++++++++++ apps/er-coap-12/er-coap-12.h | 390 ++++++ apps/er-coap-13/Makefile.er-coap-13 | 1 + apps/er-coap-13/er-coap-13-engine.c | 669 ++++++++++ apps/er-coap-13/er-coap-13-engine.h | 94 ++ apps/er-coap-13/er-coap-13-observing.c | 255 ++++ apps/er-coap-13/er-coap-13-observing.h | 82 ++ apps/er-coap-13/er-coap-13-separate.c | 117 ++ apps/er-coap-13/er-coap-13-separate.h | 66 + apps/er-coap-13/er-coap-13-transactions.c | 199 +++ apps/er-coap-13/er-coap-13-transactions.h | 78 ++ apps/er-coap-13/er-coap-13.c | 1131 ++++++++++++++++ apps/er-coap-13/er-coap-13.h | 382 ++++++ apps/erbium/erbium.h | 16 +- examples/er-rest-example/Makefile | 37 +- examples/er-rest-example/README | 36 +- examples/er-rest-example/er-example-client.c | 8 +- examples/er-rest-example/er-example-server.c | 55 +- examples/er-rest-example/er-plugtest-server.c | 792 ++++++++++- examples/er-rest-example/in6addr.patch | 10 + examples/er-rest-example/project-conf.h | 63 +- examples/er-rest-example/server-client.csc | 18 +- examples/er-rest-example/server-only.csc | 28 +- examples/er-rest-example/static-routing.c | 2 +- 33 files changed, 7138 insertions(+), 107 deletions(-) create mode 100644 apps/er-coap-12/Makefile.er-coap-12 create mode 100644 apps/er-coap-12/er-coap-12-engine.c create mode 100644 apps/er-coap-12/er-coap-12-engine.h create mode 100644 apps/er-coap-12/er-coap-12-observing.c create mode 100644 apps/er-coap-12/er-coap-12-observing.h create mode 100644 apps/er-coap-12/er-coap-12-separate.c create mode 100644 apps/er-coap-12/er-coap-12-separate.h create mode 100644 apps/er-coap-12/er-coap-12-transactions.c create mode 100644 apps/er-coap-12/er-coap-12-transactions.h create mode 100644 apps/er-coap-12/er-coap-12.c create mode 100644 apps/er-coap-12/er-coap-12.h create mode 100644 apps/er-coap-13/Makefile.er-coap-13 create mode 100644 apps/er-coap-13/er-coap-13-engine.c create mode 100644 apps/er-coap-13/er-coap-13-engine.h create mode 100644 apps/er-coap-13/er-coap-13-observing.c create mode 100644 apps/er-coap-13/er-coap-13-observing.h create mode 100644 apps/er-coap-13/er-coap-13-separate.c create mode 100644 apps/er-coap-13/er-coap-13-separate.h create mode 100644 apps/er-coap-13/er-coap-13-transactions.c create mode 100644 apps/er-coap-13/er-coap-13-transactions.h create mode 100644 apps/er-coap-13/er-coap-13.c create mode 100644 apps/er-coap-13/er-coap-13.h create mode 100644 examples/er-rest-example/in6addr.patch diff --git a/apps/er-coap-12/Makefile.er-coap-12 b/apps/er-coap-12/Makefile.er-coap-12 new file mode 100644 index 000000000..73e7b74c2 --- /dev/null +++ b/apps/er-coap-12/Makefile.er-coap-12 @@ -0,0 +1 @@ +er-coap-12_src = er-coap-12-engine.c er-coap-12.c er-coap-12-transactions.c er-coap-12-observing.c er-coap-12-separate.c diff --git a/apps/er-coap-12/er-coap-12-engine.c b/apps/er-coap-12/er-coap-12-engine.c new file mode 100644 index 000000000..5630c6db4 --- /dev/null +++ b/apps/er-coap-12/er-coap-12-engine.c @@ -0,0 +1,669 @@ +/* + * 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 + */ + +#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 new file mode 100644 index 000000000..d3e5c8bad --- /dev/null +++ b/apps/er-coap-12/er-coap-12-engine.h @@ -0,0 +1,94 @@ +/* + * 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 new file mode 100644 index 000000000..323a90fe3 --- /dev/null +++ b/apps/er-coap-12/er-coap-12-observing.c @@ -0,0 +1,255 @@ +/* + * 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 new file mode 100644 index 000000000..349a0fc4f --- /dev/null +++ b/apps/er-coap-12/er-coap-12-observing.h @@ -0,0 +1,82 @@ +/* + * 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 "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-separate.h b/apps/er-coap-12/er-coap-12-separate.h new file mode 100644 index 000000000..7277cb0f0 --- /dev/null +++ b/apps/er-coap-12/er-coap-12-separate.h @@ -0,0 +1,66 @@ +/* + * 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 separate responses + * \author + * Matthias Kovatsch + */ + +#ifndef COAP_SEPARATE_H_ +#define COAP_SEPARATE_H_ + +#include "er-coap-12.h" + +typedef struct coap_separate { + + uip_ipaddr_t addr; + uint16_t port; + + coap_message_type_t type; + uint16_t mid; + + uint8_t token_len; + uint8_t token[COAP_TOKEN_LEN]; + + /* 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_ */ diff --git a/apps/er-coap-12/er-coap-12-transactions.c b/apps/er-coap-12/er-coap-12-transactions.c new file mode 100644 index 000000000..3cd496e65 --- /dev/null +++ b/apps/er-coap-12/er-coap-12-transactions.c @@ -0,0 +1,199 @@ +/* + * 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-transactions.h b/apps/er-coap-12/er-coap-12-transactions.h new file mode 100644 index 000000000..b2a748cda --- /dev/null +++ b/apps/er-coap-12/er-coap-12-transactions.h @@ -0,0 +1,78 @@ +/* + * 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 + */ + +#ifndef COAP_TRANSACTIONS_H_ +#define COAP_TRANSACTIONS_H_ + +#include "er-coap-12.h" + +/* + * 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 */ + +/* 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_ */ diff --git a/apps/er-coap-12/er-coap-12.c b/apps/er-coap-12/er-coap-12.c new file mode 100644 index 000000000..0e68bfa34 --- /dev/null +++ b/apps/er-coap-12/er-coap-12.c @@ -0,0 +1,1155 @@ +/* + * 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 new file mode 100644 index 000000000..03efd0c21 --- /dev/null +++ b/apps/er-coap-12/er-coap-12.h @@ -0,0 +1,390 @@ +/* + * 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_l2_l3_hdr_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 new file mode 100644 index 000000000..b3c3ba169 --- /dev/null +++ b/apps/er-coap-13/Makefile.er-coap-13 @@ -0,0 +1 @@ +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 new file mode 100644 index 000000000..fed8fa889 --- /dev/null +++ b/apps/er-coap-13/er-coap-13-engine.c @@ -0,0 +1,669 @@ +/* + * 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 + */ + +#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_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 = '\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 new file mode 100644 index 000000000..6df5f4048 --- /dev/null +++ b/apps/er-coap-13/er-coap-13-engine.h @@ -0,0 +1,94 @@ +/* + * 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-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 new file mode 100644 index 000000000..8fe62135a --- /dev/null +++ b/apps/er-coap-13/er-coap-13-observing.c @@ -0,0 +1,255 @@ +/* + * 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-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-observing.h b/apps/er-coap-13/er-coap-13-observing.h new file mode 100644 index 000000000..fa2c7ec9b --- /dev/null +++ b/apps/er-coap-13/er-coap-13-observing.h @@ -0,0 +1,82 @@ +/* + * 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 "er-coap-13.h" +#include "er-coap-13-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-13-separate.h" +#include "er-coap-13-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-13/er-coap-13-separate.h b/apps/er-coap-13/er-coap-13-separate.h new file mode 100644 index 000000000..850b117d2 --- /dev/null +++ b/apps/er-coap-13/er-coap-13-separate.h @@ -0,0 +1,66 @@ +/* + * 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 separate responses + * \author + * Matthias Kovatsch + */ + +#ifndef COAP_SEPARATE_H_ +#define COAP_SEPARATE_H_ + +#include "er-coap-13.h" + +typedef struct coap_separate { + + uip_ipaddr_t addr; + uint16_t port; + + coap_message_type_t type; + uint16_t mid; + + uint8_t token_len; + uint8_t token[COAP_TOKEN_LEN]; + + /* 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_ */ diff --git a/apps/er-coap-13/er-coap-13-transactions.c b/apps/er-coap-13/er-coap-13-transactions.c new file mode 100644 index 000000000..650fead1e --- /dev/null +++ b/apps/er-coap-13/er-coap-13-transactions.c @@ -0,0 +1,199 @@ +/* + * 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-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) + +#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-13/er-coap-13-transactions.h b/apps/er-coap-13/er-coap-13-transactions.h new file mode 100644 index 000000000..22948a782 --- /dev/null +++ b/apps/er-coap-13/er-coap-13-transactions.h @@ -0,0 +1,78 @@ +/* + * 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 + */ + +#ifndef COAP_TRANSACTIONS_H_ +#define COAP_TRANSACTIONS_H_ + +#include "er-coap-13.h" + +/* + * 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 */ + +/* 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_ */ diff --git a/apps/er-coap-13/er-coap-13.c b/apps/er-coap-13/er-coap-13.c new file mode 100644 index 000000000..5a9d99498 --- /dev/null +++ b/apps/er-coap-13/er-coap-13.c @@ -0,0 +1,1131 @@ +/* + * 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-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 (delta>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) 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); + + PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, i); + + i += coap_set_option_header(number - current_number, i, buffer); + + 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] = 0xFF & (coap_pkt->mid)>>8; + coap_pkt->buffer[3] = 0xFF & 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; + + 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-13/er-coap-13.h b/apps/er-coap-13/er-coap-13.h new file mode 100644 index 000000000..0d32256a1 --- /dev/null +++ b/apps/er-coap-13/er-coap-13.h @@ -0,0 +1,382 @@ +/* + * 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 /* | 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_l2_l3_hdr_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 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_12_H_ */ diff --git a/apps/erbium/erbium.h b/apps/erbium/erbium.h index bf0cb8eee..0fecd960d 100644 --- a/apps/erbium/erbium.h +++ b/apps/erbium/erbium.h @@ -98,8 +98,9 @@ struct rest_implementation_status 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_MADIA_TYPE; /* UNSUPPORTED_MADIA_TYPE_4_15, UNSUPPORTED_MADIA_TYPE_415 */ + 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 */ @@ -182,11 +183,18 @@ struct rest_implementation { /** Get the content-type of a request. */ unsigned int (* get_header_content_type)(void *request); - /** Set the content-type of a response. */ + /** 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); @@ -209,7 +217,7 @@ struct rest_implementation { int (* set_header_location)(void *response, const char *location); /** Get the payload option of a request. */ - int (* get_request_payload)(void *request, uint8_t **payload); + 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); @@ -224,7 +232,7 @@ struct rest_implementation { 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, uint16_t counter, void *notification); + void (* notify_subscribers)(resource_t *resource, int32_t counter, void *notification); /** The handler for resource subscriptions. */ restful_post_handler subscription_handler; diff --git a/examples/er-rest-example/Makefile b/examples/er-rest-example/Makefile index fb21adaa8..004f0df26 100644 --- a/examples/er-rest-example/Makefile +++ b/examples/er-rest-example/Makefile @@ -5,24 +5,22 @@ CONTIKI=../.. CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" # for static routing, if enabled -ifneq ($(TARGET), minimal-net) -ifneq ($(TARGET), native) -ifneq ($(TARGET), econotag) -ifneq ($(findstring avr,$(TARGET)), avr) +ifeq ($(TARGET),$(filter $(TARGET),sky cooja)) PROJECT_SOURCEFILES += static-routing.c endif -endif -endif -endif # variable for root Makefile.include WITH_UIP6=1 # for some platforms UIP_CONF_IPV6=1 +# radio configuration +#CFLAGS += -DRF_CHANNEL=20 +#CFLAGS += -DIEEE802154_CONF_PANID=0xCAFE + # variable for this Makefile -# configure CoAP implementation (3|7) (er-coap-07 also supports CoAP draft 08) -WITH_COAP=7 +# configure CoAP implementation (3|7|12|13) (er-coap-07 also supports CoAP draft 08) +WITH_COAP=13 # new variable since slip-radio ifneq ($(TARGET), minimal-net) @@ -33,14 +31,29 @@ ${info INFO: compiling without RPL} UIP_CONF_RPL=0 CFLAGS += -DUIP_CONF_ND6_DEF_MAXDADNS=0 CFLAGS += -DHARD_CODED_ADDRESS=\"fdfd::10\" -CFLAGS += -DUIP_CONF_BUFFER_SIZE=1280 +${info INFO: compiling with large buffers} +CFLAGS += -DUIP_CONF_BUFFER_SIZE=2048 +CFLAGS += -DREST_MAX_CHUNK_SIZE=1024 +CFLAGS += -DCOAP_MAX_HEADER_SIZE=640 endif # linker optimizations SMALL=1 # REST framework, requires WITH_COAP -ifeq ($(WITH_COAP), 7) +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 @@ -86,5 +99,5 @@ connect-router: $(CONTIKI)/tools/tunslip6 connect-router-cooja: $(CONTIKI)/tools/tunslip6 sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 aaaa::1/64 -tap0up: +connect-minimal: sudo ip address add fdfd::1/64 dev tap0 diff --git a/examples/er-rest-example/README b/examples/er-rest-example/README index 2d2f3ccfe..b5267fdca 100644 --- a/examples/er-rest-example/README +++ b/examples/er-rest-example/README @@ -57,12 +57,36 @@ Add a client: 2) Connect a third Tmote Sky 3) $ make TARGET=sky er-example-client.upload MOTE=3 +MINIMAL-NET HOWTO +----------------- +With the target minimal-net you can test your CoAP applications without constraints, i.e., with large buffers, debug output, memory protection, etc. +The er-plugtest-server is thought for the minimal-net platform, as it requires an 1280-byte IP buffer and 1024-byte blocks. +1) $ make TARGET=minimal-net er-plugtest-server +2) $ sudo ./er-plugtest-server.minimal-net +3) Open new terminal +4) $ make connect-minimal +5) Start Copper and discover resources at coap://[fdfd::ff:fe00:10]:5683/ +- You can enable the ETSI Plugtest menu in Copper's preferences + +Under Windows/Cygwin, WPCAP might need a patch in \usr\include\w32api\in6addr.h: + +21,23c21 +< #ifdef __INSIDE_CYGWIN__ +< uint32_t __s6_addr32[4]; +< #endif +--- +> u_int __s6_addr32[4]; +36d33 +< #ifdef __INSIDE_CYGWIN__ +39d35 +< #endif + DETAILS ------- -Erbium currently implements draft 08 (name "er-coap-07" stems from last technical draft changes). +Erbium currently implements draft 13. Central features are commented in er-example-server.c. -In general, apps/er-coap-07 supports: -* All draft 08 header options +In general, apps/er-coap-13 supports: +* All draft 13 header options * CON Retransmissions (note COAP_MAX_OPEN_TRANSACTIONS) * Blockwise Transfers (note REST_MAX_CHUNK_SIZE, see er-plugtest-server.c for Block1 uploads) * Separate Responses (no rest_set_pre_handler() required anymore, note coap_separate_accept(), _reject(), and _resume()) @@ -72,6 +96,10 @@ In general, apps/er-coap-07 supports: 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/. @@ -81,6 +109,8 @@ The Makefile uses WITH_COAP to configure different implementations for the Erbiu TODOs ----- +* Dedicated Observe buffers +* Optimize message struct variable access (directly access struct without copying) * Observe client * Multiple If-Match ETags * (Message deduplication) diff --git a/examples/er-rest-example/er-example-client.c b/examples/er-rest-example/er-example-client.c index d1d543cf0..e8d623687 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) 2011, Matthias Kovatsch and other contributors. + * Copyright (c) 2012, Matthias Kovatsch and other contributors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,6 +56,10 @@ #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 @@ -99,7 +103,7 @@ static int uri_switch = 0; void client_chunk_handler(void *response) { - uint8_t *chunk; + const uint8_t *chunk; int len = coap_get_payload(response, &chunk); printf("|%.*s", len, (char *)chunk); diff --git a/examples/er-rest-example/er-example-server.c b/examples/er-rest-example/er-example-server.c index aff33e10b..d9835b1d0 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) 2011, Matthias Kovatsch and other contributors. + * Copyright (c) 2012, Matthias Kovatsch and other contributors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,18 +44,18 @@ /* Define which resources to include to meet memory constraints. */ -#define REST_RES_HELLO 1 +#define REST_RES_HELLO 0 #define REST_RES_MIRROR 0 /* causes largest code size */ -#define REST_RES_CHUNKS 1 +#define REST_RES_CHUNKS 0 #define REST_RES_SEPARATE 1 #define REST_RES_PUSHING 1 #define REST_RES_EVENT 1 #define REST_RES_SUB 1 -#define REST_RES_LEDS 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 1 +#define REST_RES_RADIO 0 @@ -92,6 +92,10 @@ #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 */ @@ -163,7 +167,7 @@ mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferre 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 = 0; + uint32_t max_age_and_size = 0; const char *str = NULL; uint32_t observe = 0; const uint8_t *bytes = NULL; @@ -178,14 +182,22 @@ mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferre 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. */ - strpos += snprintf((char *)buffer, REST_MAX_CHUNK_SIZE+1, "CT %u\n", content_type); - + * 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)) + 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); + 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))) @@ -275,9 +287,10 @@ mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferre /* 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, 10); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */ + 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 @@ -359,8 +372,16 @@ chunks_handler(void* request, void* response, uint8_t *buffer, uint16_t preferre /******************************************************************************/ #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. @@ -557,7 +578,7 @@ sub_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_s } else { - snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, ".%s", uri_path+base_len); + snprintf((char *)buffer, REST_MAX_CHUNK_SIZE, ".%.*s", len-base_len, uri_path+base_len); } REST.set_response_payload(response, buffer, strlen((char *)buffer)); @@ -619,7 +640,7 @@ leds_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_ /******************************************************************************/ #if REST_RES_TOGGLE /* A simple actuator example. Toggles the red led */ -RESOURCE(toggle, METHOD_GET | METHOD_PUT | METHOD_POST, "actuators/toggle", "title=\"Red LED\";rt=\"Control\""); +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) { @@ -664,7 +685,7 @@ light_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred } else { - REST.set_response_status(response, REST.status.UNSUPPORTED_MADIA_TYPE); + 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)); } @@ -699,7 +720,7 @@ battery_handler(void* request, void* response, uint8_t *buffer, uint16_t preferr } else { - REST.set_response_status(response, REST.status.UNSUPPORTED_MADIA_TYPE); + 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)); } @@ -757,7 +778,7 @@ radio_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred } else { - REST.set_response_status(response, REST.status.UNSUPPORTED_MADIA_TYPE); + 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)); } diff --git a/examples/er-rest-example/er-plugtest-server.c b/examples/er-rest-example/er-plugtest-server.c index 051b44be0..ac91f4cf5 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) 2011, Matthias Kovatsch and other contributors. + * Copyright (c) 2012, Matthias Kovatsch and other contributors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,17 +43,26 @@ #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!" @@ -70,6 +79,10 @@ /* 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 */ @@ -92,31 +105,136 @@ */ 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; itype, coap_req->code, 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 ### 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, "/nirvana"); + REST.set_header_location(response, "/location1/location2/location3"); } else if (method & METHOD_PUT) { PRINTF("PUT "); - REST.set_response_status(response, REST.status.CHANGED); + + 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) { @@ -126,6 +244,233 @@ test_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_ 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 @@ -179,10 +524,116 @@ query_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred } #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. */ -#include "er-coap-07-separate.h" -#include "er-coap-07-transactions.h" +#if 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 */ @@ -264,12 +715,15 @@ separate_periodic_handler(resource_t *resource) #endif #if REST_RES_LARGE + +/* double expansion */ +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) + /* * Large resource */ -RESOURCE(large, METHOD_GET, "large", "title=\"Large resource\";rt=\"block\""); - -#define CHUNKS_TOTAL 1280 +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) @@ -323,10 +777,10 @@ large_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred /* * 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\""); +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 = 1280; -static uint8_t large_update_store[2048] = {0}; +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 @@ -348,7 +802,7 @@ large_update_handler(void* request, void* response, uint8_t *buffer, uint16_t pr return; } - REST.set_response_payload(response, large_update_store+*offset, preferred_size); + 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. */ @@ -372,7 +826,7 @@ large_update_handler(void* request, void* response, uint8_t *buffer, uint16_t pr return; } - if ((len = REST.get_request_payload(request, &incoming))) + 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)) { @@ -424,7 +878,7 @@ large_create_handler(void* request, void* response, uint8_t *buffer, uint16_t pr return; } - if ((len = REST.get_request_payload(request, &incoming))) + if ((len = REST.get_request_payload(request, (const uint8_t **) &incoming))) { if (coap_req->block1_num*coap_req->block1_size+len <= 2048) { @@ -451,23 +905,102 @@ large_create_handler(void* request, void* response, uint8_t *buffer, uint16_t pr #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, "obs", "title=\"Observable resource which changes every 5 seconds\";obs;rt=\"observe\"", 5*CLOCK_SECOND); +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[16]; +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) { - REST.set_header_content_type(response, REST.type.TEXT_PLAIN); - REST.set_header_max_age(response, 5); + uint8_t method = request==NULL ? METHOD_GET : REST.get_method_type(request); - REST.set_response_payload(response, obs_content, snprintf(obs_content, MAX_PLUGFEST_PAYLOAD, "TICK %lu", obs_counter)); + /* 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; - /* A post_handler that handles subscriptions will be called for periodic resources by the REST framework. */ + 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"); + } } /* @@ -479,8 +1012,37 @@ obs_periodic_handler(resource_t *r) { ++obs_counter; - PRINTF("TICK %u for /%s\n", obs_counter, r->url); + //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. */ @@ -491,9 +1053,169 @@ obs_periodic_handler(resource_t *r) /* 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 */ + + + + + PROCESS(plugtest_server, "PlugtestServer"); AUTOSTART_PROCESSES(&plugtest_server); @@ -527,6 +1249,10 @@ PROCESS_THREAD(plugtest_server, ev, data) /* 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); @@ -534,6 +1260,20 @@ PROCESS_THREAD(plugtest_server, ev, data) #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 @@ -551,6 +1291,10 @@ PROCESS_THREAD(plugtest_server, ev, data) rest_activate_periodic_resource(&periodic_resource_obs); #endif +#if REST_RES_MIRROR + rest_activate_resource(&resource_mirror); +#endif + /* Define application-specific events here. */ while(1) { PROCESS_WAIT_EVENT(); diff --git a/examples/er-rest-example/in6addr.patch b/examples/er-rest-example/in6addr.patch new file mode 100644 index 000000000..92ca106cf --- /dev/null +++ b/examples/er-rest-example/in6addr.patch @@ -0,0 +1,10 @@ +21,23c21 +< #ifdef __INSIDE_CYGWIN__ +< uint32_t __s6_addr32[4]; +< #endif +--- +> u_int __s6_addr32[4]; +36d33 +< #ifdef __INSIDE_CYGWIN__ +39d35 +< #endif diff --git a/examples/er-rest-example/project-conf.h b/examples/er-rest-example/project-conf.h index 751503f13..3e47076c3 100644 --- a/examples/er-rest-example/project-conf.h +++ b/examples/er-rest-example/project-conf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Swedish Institute of Computer Science. + * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,39 +29,60 @@ * */ -#ifndef __PROJECT_RPL_WEB_CONF_H__ -#define __PROJECT_RPL_WEB_CONF_H__ +#ifndef __PROJECT_ERBIUM_CONF_H__ +#define __PROJECT_ERBIUM_CONF_H__ -#define SICSLOWPAN_CONF_FRAG 1 +/* Some platforms have weird includes. */ +#undef IEEE802154_CONF_PANID /* 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 +/* Increase rpl-border-router IP-buffer when using more than 64. */ +#undef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 64 + +/* 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 +*/ + +/* Multiplies with chunk size, be aware of memory constraints. */ +#undef COAP_MAX_OPEN_TRANSACTIONS +#define COAP_MAX_OPEN_TRANSACTIONS 4 + +/* Must be <= open transaction number, default is COAP_MAX_OPEN_TRANSACTIONS-1. */ +/* +#undef COAP_MAX_OBSERVERS +#define COAP_MAX_OBSERVERS 2 +*/ + +/* Filtering can be disabled to save space. */ +/* +#undef COAP_LINK_FORMAT_FILTERING +#define COAP_LINK_FORMAT_FILTERING 0 +*/ + /* Save some memory for the sky platform. */ #undef UIP_CONF_DS6_NBR_NBU #define UIP_CONF_DS6_NBR_NBU 10 #undef UIP_CONF_DS6_ROUTE_NBU #define UIP_CONF_DS6_ROUTE_NBU 10 -/* Increase rpl-border-router IP-buffer when using 128. */ -#ifndef REST_MAX_CHUNK_SIZE -#define REST_MAX_CHUNK_SIZE 64 -#endif - -/* Multiplies with chunk size, be aware of memory constraints. */ -#ifndef COAP_MAX_OPEN_TRANSACTIONS -#define COAP_MAX_OPEN_TRANSACTIONS 2 -#endif - -/* Must be <= open transaction number. */ -#ifndef COAP_MAX_OBSERVERS -#define COAP_MAX_OBSERVERS COAP_MAX_OPEN_TRANSACTIONS-1 -#endif - /* Reduce 802.15.4 frame queue to save RAM. */ #undef QUEUEBUF_CONF_NUM -#define QUEUEBUF_CONF_NUM 4 +#define QUEUEBUF_CONF_NUM 4 -#endif /* __PROJECT_RPL_WEB_CONF_H__ */ +#undef SICSLOWPAN_CONF_FRAG +#define SICSLOWPAN_CONF_FRAG 1 + +#endif /* __PROJECT_ERBIUM_CONF_H__ */ diff --git a/examples/er-rest-example/server-client.csc b/examples/er-rest-example/server-client.csc index 8c45fdf02..0c09f41b5 100644 --- a/examples/er-rest-example/server-client.csc +++ b/examples/er-rest-example/server-client.csc @@ -1,13 +1,13 @@ - [CONTIKI_DIR]/tools/cooja/apps/mrm - [CONTIKI_DIR]/tools/cooja/apps/mspsim - [CONTIKI_DIR]/tools/cooja/apps/avrora - [CONTIKI_DIR]/tools/cooja/apps/serial_socket - [CONTIKI_DIR]/tools/cooja/apps/collect-view + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker REST with RPL router - -2147483648 123456 1000000 @@ -37,7 +37,7 @@ se.sics.cooja.mspmote.interfaces.SkyButton se.sics.cooja.mspmote.interfaces.SkyFlash se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem - se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.Msp802154Radio se.sics.cooja.mspmote.interfaces.MspSerial se.sics.cooja.mspmote.interfaces.SkyLED se.sics.cooja.mspmote.interfaces.MspDebugOutput @@ -60,7 +60,7 @@ se.sics.cooja.mspmote.interfaces.SkyButton se.sics.cooja.mspmote.interfaces.SkyFlash se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem - se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.Msp802154Radio se.sics.cooja.mspmote.interfaces.MspSerial se.sics.cooja.mspmote.interfaces.SkyLED se.sics.cooja.mspmote.interfaces.MspDebugOutput @@ -83,7 +83,7 @@ se.sics.cooja.mspmote.interfaces.SkyButton se.sics.cooja.mspmote.interfaces.SkyFlash se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem - se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.Msp802154Radio se.sics.cooja.mspmote.interfaces.MspSerial se.sics.cooja.mspmote.interfaces.SkyLED se.sics.cooja.mspmote.interfaces.MspDebugOutput diff --git a/examples/er-rest-example/server-only.csc b/examples/er-rest-example/server-only.csc index d5eee34d6..935bd6e79 100644 --- a/examples/er-rest-example/server-only.csc +++ b/examples/er-rest-example/server-only.csc @@ -1,13 +1,13 @@ - [CONTIKI_DIR]/tools/cooja/apps/mrm - [CONTIKI_DIR]/tools/cooja/apps/mspsim - [CONTIKI_DIR]/tools/cooja/apps/avrora - [CONTIKI_DIR]/tools/cooja/apps/serial_socket - [CONTIKI_DIR]/tools/cooja/apps/collect-view + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker REST with RPL router - -2147483648 123456 1000000 @@ -37,7 +37,7 @@ se.sics.cooja.mspmote.interfaces.SkyButton se.sics.cooja.mspmote.interfaces.SkyFlash se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem - se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.Msp802154Radio se.sics.cooja.mspmote.interfaces.MspSerial se.sics.cooja.mspmote.interfaces.SkyLED se.sics.cooja.mspmote.interfaces.MspDebugOutput @@ -60,7 +60,7 @@ se.sics.cooja.mspmote.interfaces.SkyButton se.sics.cooja.mspmote.interfaces.SkyFlash se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem - se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.Msp802154Radio se.sics.cooja.mspmote.interfaces.MspSerial se.sics.cooja.mspmote.interfaces.SkyLED se.sics.cooja.mspmote.interfaces.MspDebugOutput @@ -98,7 +98,7 @@ se.sics.cooja.plugins.SimControl 259 - 5 + 0 179 0 0 @@ -115,7 +115,7 @@ 7.9849281638410705 0.0 0.0 7.9849281638410705 -133.27812697619663 -225.04752569190535 300 - 4 + 5 175 263 3 @@ -127,7 +127,7 @@ 560 - 1 + 2 326 1 293 @@ -149,7 +149,7 @@ SerialSocketServer 0 422 - 2 + 3 74 39 199 @@ -167,7 +167,7 @@ 25.49079397896416 1624 - 3 + 4 252 4 622 @@ -180,7 +180,7 @@ 0,0 702 - 0 + 1 646 564 2 diff --git a/examples/er-rest-example/static-routing.c b/examples/er-rest-example/static-routing.c index 00b6c68be..628594892 100644 --- a/examples/er-rest-example/static-routing.c +++ b/examples/er-rest-example/static-routing.c @@ -21,7 +21,7 @@ #endif #include "contiki-net.h" -#include "sys/node-id.h" +#include "node-id.h" int node_rank; From 82884ab63f701fe096dd3b93cb07c85e3cd406a0 Mon Sep 17 00:00:00 2001 From: Matthias Kovatsch Date: Wed, 13 Feb 2013 19:22:21 +0100 Subject: [PATCH 2/2] Fixed legacy CoAP versions. --- apps/er-coap-03/er-coap-03-engine.c | 5 ++- apps/er-coap-03/er-coap-03-observing.c | 42 +++++++++++-------- apps/er-coap-03/er-coap-03-observing.h | 2 +- apps/er-coap-03/er-coap-03.c | 2 +- apps/er-coap-03/er-coap-03.h | 4 +- apps/er-coap-07/er-coap-07-engine.c | 7 +++- apps/er-coap-07/er-coap-07.c | 2 +- apps/er-coap-07/er-coap-07.h | 5 ++- examples/er-rest-example/er-example-server.c | 6 +-- examples/er-rest-example/er-plugtest-server.c | 5 ++- 10 files changed, 50 insertions(+), 30 deletions(-) diff --git a/apps/er-coap-03/er-coap-03-engine.c b/apps/er-coap-03/er-coap-03-engine.c index 048261349..8e21d30d3 100644 --- a/apps/er-coap-03/er-coap-03-engine.c +++ b/apps/er-coap-03/er-coap-03-engine.c @@ -484,6 +484,8 @@ const struct rest_implementation coap_rest_implementation = { 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, @@ -518,8 +520,9 @@ const struct rest_implementation coap_rest_implementation = { METHOD_NOT_ALLOWED_405, NOT_FOUND_404, METHOD_NOT_ALLOWED_405, + UNSUPPORTED_MEDIA_TYPE_415, BAD_REQUEST_400, - UNSUPPORTED_MADIA_TYPE_415, + UNSUPPORTED_MEDIA_TYPE_415, INTERNAL_SERVER_ERROR_500, CRITICAL_OPTION_NOT_SUPPORTED, diff --git a/apps/er-coap-03/er-coap-03-observing.c b/apps/er-coap-03/er-coap-03-observing.c index dc8fbc048..a55c31691 100644 --- a/apps/er-coap-03/er-coap-03-observing.c +++ b/apps/er-coap-03/er-coap-03-observing.c @@ -122,39 +122,47 @@ coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, } /*-----------------------------------------------------------------------------------*/ void -coap_notify_observers(const char *url, int type, uint32_t observe, uint8_t *payload, size_t payload_len) +coap_notify_observers(resource_t *resource, uint16_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==url) /* using RESOURCE url string as handle */ + 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 */ + /*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("Observing: Refresh client with CON\n"); - type = COAP_TYPE_CON; + PRINTF(" Refreshing with CON\n"); + coap_res->type = COAP_TYPE_CON; stimer_restart(&obs->refresh_timer); } + else + { + coap_res->type = preferred_type; + } - /* prepare response */ - coap_packet_t push[1]; /* This way the packet can be treated as pointer as usual. */ - coap_init_message(push, (coap_message_type_t)type, OK_200, transaction->tid ); - coap_set_header_observe(push, observe); - coap_set_header_token(push, obs->token, obs->token_len); - coap_set_payload(push, payload, payload_len); - transaction->packet_len = coap_serialize_message(push, transaction->packet); - - PRINTF("Observing: Notify from /%s for ", url); - PRINT6ADDR(&obs->addr); - PRINTF(":%u\n", obs->port); - PRINTF(" %.*s\n", payload_len, payload); + transaction->packet_len = coap_serialize_message(coap_res, transaction->packet); coap_send_transaction(transaction); } diff --git a/apps/er-coap-03/er-coap-03-observing.h b/apps/er-coap-03/er-coap-03-observing.h index 287c11e9f..48cae5f92 100644 --- a/apps/er-coap-03/er-coap-03-observing.h +++ b/apps/er-coap-03/er-coap-03-observing.h @@ -69,7 +69,7 @@ coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint void coap_remove_observer(coap_observer_t *o); int coap_remove_observer_by_client(uip_ipaddr_t *addr, uint16_t port); int coap_remove_observer_by_token(uip_ipaddr_t *addr, uint16_t port, uint8_t *token, size_t token_len); -void coap_notify_observers(const char *url, int type, uint32_t observe, uint8_t *payload, size_t payload_len); +void coap_notify_observers(resource_t *resource, uint16_t obs_counter, void *notification); void coap_observe_handler(resource_t *resource, void *request, void *response); diff --git a/apps/er-coap-03/er-coap-03.c b/apps/er-coap-03/er-coap-03.c index 4f7a5b81c..8a770282b 100644 --- a/apps/er-coap-03/er-coap-03.c +++ b/apps/er-coap-03/er-coap-03.c @@ -745,7 +745,7 @@ coap_set_header_uri_query(void *packet, const char *query) /*- PAYLOAD -------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/ int -coap_get_payload(void *packet, uint8_t **payload) +coap_get_payload(void *packet, const uint8_t **payload) { if (((coap_packet_t *)packet)->payload) { *payload = ((coap_packet_t *)packet)->payload; diff --git a/apps/er-coap-03/er-coap-03.h b/apps/er-coap-03/er-coap-03.h index ebd2f164f..00a292bcb 100644 --- a/apps/er-coap-03/er-coap-03.h +++ b/apps/er-coap-03/er-coap-03.h @@ -120,7 +120,7 @@ typedef enum { BAD_REQUEST_400 = 160, NOT_FOUND_404 = 164, METHOD_NOT_ALLOWED_405 = 165, - UNSUPPORTED_MADIA_TYPE_415 = 175, + UNSUPPORTED_MEDIA_TYPE_415 = 175, INTERNAL_SERVER_ERROR_500 = 200, BAD_GATEWAY_502 = 202, SERVICE_UNAVAILABLE_503 = 203, @@ -278,7 +278,7 @@ int coap_set_header_block(void *packet, uint32_t num, uint8_t more, uint16_t siz int coap_get_header_uri_query(void *packet, const char **query); /*CAUTION in-place string might not be 0-terminated */ int coap_set_header_uri_query(void *packet, const char *query); -int coap_get_payload(void *packet, uint8_t **payload); +int coap_get_payload(void *packet, const uint8_t **payload); int coap_set_payload(void *packet, const void *payload, size_t length); #endif /* COAP_03_H_ */ diff --git a/apps/er-coap-07/er-coap-07-engine.c b/apps/er-coap-07/er-coap-07-engine.c index 483f0309d..28f7f459c 100644 --- a/apps/er-coap-07/er-coap-07-engine.c +++ b/apps/er-coap-07/er-coap-07-engine.c @@ -537,6 +537,8 @@ const struct rest_implementation coap_rest_implementation = { 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, @@ -564,14 +566,17 @@ const struct rest_implementation coap_rest_implementation = { 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_MADIA_TYPE_4_15, + UNSUPPORTED_MEDIA_TYPE_4_15, + INTERNAL_SERVER_ERROR_5_00, NOT_IMPLEMENTED_5_01, BAD_GATEWAY_5_02, diff --git a/apps/er-coap-07/er-coap-07.c b/apps/er-coap-07/er-coap-07.c index 66bf40b8c..4bf995c97 100644 --- a/apps/er-coap-07/er-coap-07.c +++ b/apps/er-coap-07/er-coap-07.c @@ -1177,7 +1177,7 @@ coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size) /*- PAYLOAD -------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/ int -coap_get_payload(void *packet, uint8_t **payload) +coap_get_payload(void *packet, const uint8_t **payload) { coap_packet_t *const coap_pkt = (coap_packet_t *) packet; diff --git a/apps/er-coap-07/er-coap-07.h b/apps/er-coap-07/er-coap-07.h index 2284f0a00..4749a5f50 100644 --- a/apps/er-coap-07/er-coap-07.h +++ b/apps/er-coap-07/er-coap-07.h @@ -129,9 +129,10 @@ typedef enum { 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_MADIA_TYPE_4_15 = 143, /* UNSUPPORTED_MADIA_TYPE */ + 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 */ @@ -315,7 +316,7 @@ int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t si 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_payload(void *packet, uint8_t **payload); +int coap_get_payload(void *packet, const uint8_t **payload); int coap_set_payload(void *packet, const void *payload, size_t length); #endif /* COAP_07_H_ */ diff --git a/examples/er-rest-example/er-example-server.c b/examples/er-rest-example/er-example-server.c index d9835b1d0..4bdfb7ba2 100644 --- a/examples/er-rest-example/er-example-server.c +++ b/examples/er-rest-example/er-example-server.c @@ -440,7 +440,7 @@ separate_finalize_handler() 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); + coap_separate_resume(response, &separate_store->request_metadata, REST.status.OK); coap_set_payload(response, separate_store->buffer, strlen(separate_store->buffer)); @@ -505,7 +505,7 @@ pushing_periodic_handler(resource_t *r) /* 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_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. */ @@ -547,7 +547,7 @@ event_event_handler(resource_t *r) /* 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, CONTENT_2_05, 0 ); + 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. */ diff --git a/examples/er-rest-example/er-plugtest-server.c b/examples/er-rest-example/er-plugtest-server.c index ac91f4cf5..55fb7fd95 100644 --- a/examples/er-rest-example/er-plugtest-server.c +++ b/examples/er-rest-example/er-plugtest-server.c @@ -627,7 +627,10 @@ path_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_ #if REST_RES_SEPARATE /* Required to manually (=not by the engine) handle the response transaction. */ -#if WITH_COAP == 12 +#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