Added Erbium coap-12 and coap-13.

This commit is contained in:
Matthias Kovatsch 2013-02-13 17:43:04 +01:00
parent 19c8f9ffdd
commit a522357d20
33 changed files with 7138 additions and 107 deletions

View file

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

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*----------------------------------------------------------------------------*/
/*- Variables ----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
coap_error_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
/* Static declaration reduces stack peaks and program code size. */
static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n 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->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
{
PRINTF("Block1 NOT IMPLEMENTED\n");
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
}
else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
}
else
{
coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
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_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*----------------------------------------------------------------------------*/
/*- 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
}
};

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#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) */
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
const char *url;
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint16_t last_mid;
struct stimer refresh_timer;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
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);
int coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_observe_handler(resource_t *resource, void *request, void *response);
#endif /* COAP_OBSERVING_H_ */

View file

@ -0,0 +1,117 @@
/*
* 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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#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);
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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 <stdio.h>
#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_counter<COAP_MAX_RETRANSMIT)
{
/* Not timed out yet. */
PRINTF("Keeping transaction %u\n", t->mid);
if (t->retrans_counter==0)
{
t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
else
{
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
/*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);
}
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

1155
apps/er-coap-12/er-coap-12.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_12_H_
#define COAP_12_H_
#include <stddef.h> /* 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; i<coap_pkt->field##_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_ */

View file

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

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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<len; ++i) { \
for (j=7; j>=0; --j) { \
PRINTF("%c", (((char *)buf)[i] & 1<<j) ? '1' : '0'); \
} \
PRINTF(" "); \
} \
}
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#define PRINTBITS(buf,len)
#endif
PROCESS(coap_receiver, "CoAP Receiver");
/*----------------------------------------------------------------------------*/
/*- Variables ----------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static service_callback_t service_cbk = NULL;
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
static
int
coap_receive(void)
{
coap_error_code = NO_ERROR;
PRINTF("handle_incoming_data(): received uip_datalen=%u \n",(uint16_t)uip_datalen());
/* Static declaration reduces stack peaks and program code size. */
static coap_packet_t message[1]; /* This way the packet can be treated as pointer as usual. */
static coap_packet_t response[1];
static coap_transaction_t *transaction = NULL;
if (uip_newdata()) {
PRINTF("receiving UDP datagram from: ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(":%u\n Length: %u\n 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->code<BAD_REQUEST_4_00 && !IS_OPTION(response, COAP_OPTION_BLOCK1) )
{
PRINTF("Block1 NOT IMPLEMENTED\n");
coap_error_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
}
else if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
{
/* unchanged new_offset indicates that resource is unaware of blockwise transfer */
if (new_offset==block_offset)
{
PRINTF("Blockwise: unaware resource with payload length %u/%u\n", response->payload_len, block_size);
if (block_offset >= response->payload_len)
{
PRINTF("handle_incoming_data(): block_offset >= response->payload_len\n");
response->code = BAD_OPTION_4_02;
coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
}
else
{
coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
} /* if (valid offset) */
}
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_error<COAP_MAX_ATTEMPTS);
PT_END(&state->pt);
}
/*----------------------------------------------------------------------------*/
/*- 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
}
};

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#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) */
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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<COAP_MAX_OBSERVERS
#warning "COAP_MAX_OPEN_TRANSACTIONS smaller than COAP_MAX_OBSERVERS: cannot handle CON notifications"
#endif
typedef struct coap_observer {
struct coap_observer *next; /* for LIST */
const char *url;
uip_ipaddr_t addr;
uint16_t port;
uint8_t token_len;
uint8_t token[COAP_TOKEN_LEN];
uint16_t last_mid;
struct stimer refresh_timer;
} coap_observer_t;
list_t coap_get_observers(void);
coap_observer_t *coap_add_observer(uip_ipaddr_t *addr, uint16_t port, const uint8_t *token, size_t token_len, const char *url);
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);
int coap_remove_observer_by_url(uip_ipaddr_t *addr, uint16_t port, const char *url);
int coap_remove_observer_by_mid(uip_ipaddr_t *addr, uint16_t port, uint16_t mid);
void coap_notify_observers(resource_t *resource, int32_t obs_counter, void *notification);
void coap_observe_handler(resource_t *resource, void *request, void *response);
#endif /* COAP_OBSERVING_H_ */

View file

@ -0,0 +1,117 @@
/*
* 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 <kovatsch@inf.ethz.ch>
*/
#include <stdio.h>
#include <string.h>
#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);
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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 <stdio.h>
#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_counter<COAP_MAX_RETRANSMIT)
{
/* Not timed out yet. */
PRINTF("Keeping transaction %u\n", t->mid);
if (t->retrans_counter==0)
{
t->retrans_timer.timer.interval = COAP_RESPONSE_TIMEOUT_TICKS + (random_rand() % (clock_time_t) COAP_RESPONSE_TIMEOUT_BACKOFF_MASK);
PRINTF("Initial interval %f\n", (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
else
{
t->retrans_timer.timer.interval <<= 1; /* double */
PRINTF("Doubled (%u) interval %f\n", t->retrans_counter, (float)t->retrans_timer.timer.interval/CLOCK_SECOND);
}
/*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);
}
}
}

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#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_ */

1131
apps/er-coap-13/er-coap-13.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 <kovatsch@inf.ethz.ch>
*/
#ifndef COAP_12_H_
#define COAP_12_H_
#include <stddef.h> /* 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; i<coap_pkt->field##_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_ */

View file

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