From e441bd432183afbf1124ee4d0e3d491f51039454 Mon Sep 17 00:00:00 2001 From: doganyazar Date: Thu, 28 Oct 2010 12:37:42 +0000 Subject: [PATCH] Initial commit of an experimental COAP/HTTP/REST implementation for Contiki --- apps/rest-coap/Makefile.rest-coap | 4 + apps/rest-coap/coap-common.c | 196 ++++++++ apps/rest-coap/coap-common.h | 132 ++++++ apps/rest-coap/coap-server.c | 397 ++++++++++++++++ apps/rest-coap/coap-server.h | 39 ++ apps/rest-common/Makefile.rest-common | 1 + apps/rest-common/buffer.c | 75 +++ apps/rest-common/buffer.h | 17 + apps/rest-common/rest-util.c | 73 +++ apps/rest-common/rest-util.h | 14 + apps/rest-common/rest.c | 201 ++++++++ apps/rest-common/rest.h | 151 ++++++ apps/rest-common/static-routing.c | 73 +++ apps/rest-common/static-routing.h | 56 +++ apps/rest-http/Makefile.rest-http | 4 + apps/rest-http/http-common.c | 29 ++ apps/rest-http/http-common.h | 144 ++++++ apps/rest-http/http-server.c | 646 ++++++++++++++++++++++++++ apps/rest-http/http-server.h | 59 +++ 19 files changed, 2311 insertions(+) create mode 100755 apps/rest-coap/Makefile.rest-coap create mode 100644 apps/rest-coap/coap-common.c create mode 100644 apps/rest-coap/coap-common.h create mode 100644 apps/rest-coap/coap-server.c create mode 100644 apps/rest-coap/coap-server.h create mode 100755 apps/rest-common/Makefile.rest-common create mode 100644 apps/rest-common/buffer.c create mode 100644 apps/rest-common/buffer.h create mode 100644 apps/rest-common/rest-util.c create mode 100644 apps/rest-common/rest-util.h create mode 100755 apps/rest-common/rest.c create mode 100755 apps/rest-common/rest.h create mode 100644 apps/rest-common/static-routing.c create mode 100644 apps/rest-common/static-routing.h create mode 100755 apps/rest-http/Makefile.rest-http create mode 100755 apps/rest-http/http-common.c create mode 100755 apps/rest-http/http-common.h create mode 100755 apps/rest-http/http-server.c create mode 100755 apps/rest-http/http-server.h diff --git a/apps/rest-coap/Makefile.rest-coap b/apps/rest-coap/Makefile.rest-coap new file mode 100755 index 000000000..cd6e332cd --- /dev/null +++ b/apps/rest-coap/Makefile.rest-coap @@ -0,0 +1,4 @@ +rest-coap_src = coap-common.c coap-server.c + +APPS += rest-common +include $(CONTIKI)/apps/rest-common/Makefile.rest-common diff --git a/apps/rest-coap/coap-common.c b/apps/rest-coap/coap-common.c new file mode 100644 index 000000000..88e6e400c --- /dev/null +++ b/apps/rest-coap/coap-common.c @@ -0,0 +1,196 @@ +/* + * coap-common.c + * + * Created on: Aug 30, 2010 + * Author: dogan + */ + +#ifdef CONTIKI_TARGET_SKY + #include "contiki.h" + #include "contiki-net.h" +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#include "coap-common.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_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 initialize_packet(coap_packet_t* packet) +{ + packet->ver = 1; + packet->type = 0; + packet->option_count = 0; + packet->code = 0; + packet->tid = 0; + packet->options = NULL; + packet->url = NULL; + packet->url_len = 0; + packet->query = NULL; + packet->query_len = 0; + packet->payload = NULL; +} + +//void parse_message(coap_packet_t* packet, uint8_t* buf, uint16_t size) +//{ +// int processed=0; +// int i=0; +// PRINTF("parse_message size %d-->\n",size); +// +// initialize_packet(packet); +// +// packet->ver = (buf[0]&COAP_HEADER_VERSION_MASK)>>COAP_HEADER_VERSION_POSITION; +// packet->type = (buf[0]&COAP_HEADER_TYPE_MASK)>>COAP_HEADER_TYPE_POSITION; +// packet->option_count = buf[0]&COAP_HEADER_OPTION_COUNT_MASK; +// packet->code = buf[1]; +// packet->tid = (buf[2] << 8) + buf[3]; +// +// processed += 4; +// +// header_option_t options[5]; +// +// if(packet->option_count){ +// int option_index=0; +// uint8_t option_delta; +// uint16_t option_len; +//// uint8_t option_value[100]; +// uint8_t* option_buf = buf+processed; +// +// while(option_index < packet->option_count){ +// //DY FIX_ME : put boundary controls +//// int j=0; +// option_delta=(option_buf[i] & COAP_HEADER_OPTION_DELTA_MASK) >> COAP_HEADER_OPTION_DELTA_POSITION; +// option_len=(option_buf[i] & COAP_HEADER_OPTION_SHORT_LENGTH_MASK); +// i++; +// if(option_len==0xf){ +// option_len+=option_buf[i]; +// i++; +// } +// +// options[option_index].option=option_delta; +// options[option_index].len=option_len; +// options[option_index].value=option_buf+i; +// if (option_index){ +// options[option_index-1].next=&options[option_index]; +// /*This field defines the difference between the option Type of +// * this option and the previous option (or zero for the first option)*/ +// options[option_index].option+=options[option_index-1].option; +// } +// +// if (options[option_index].option==Option_Type_Uri_Path){ +// packet->url = (char*)options[option_index].value; +// packet->url_len = options[option_index].len; +// } +// +// PRINTF("OPTION %d %u %s \n", options[option_index].option, options[option_index].len, options[option_index].value); +// +// i += option_len; +// option_index++; +// } +// } +// processed += i; +// +// /**/ +// if (processed < size) { +// packet->payload = &buf[processed]; +// } +// +// PRINTF("PACKET ver:%d type:%d oc:%d \ncode:%d tid:%u url:%s len:%u payload:%s\n", (int)packet->ver, (int)packet->type, (int)packet->option_count, (int)packet->code, packet->tid, packet->url, packet->url_len, packet->payload); +//} + +int serialize_packet(coap_packet_t* packet, uint8_t* buffer) +{ + int index = 0; + header_option_t* option = NULL; + uint16_t option_delta = 0; + + buffer[0] = (packet->ver) << COAP_HEADER_VERSION_POSITION; + buffer[0] |= (packet->type) << COAP_HEADER_TYPE_POSITION; + buffer[0] |= packet->option_count; + buffer[1] = packet->code; + uint16_t temp = htons(packet->tid); + memcpy( + (void*)&buffer[2], + (void*)(&temp), + sizeof(packet->tid)); + + index += 4; + + PRINTF("serialize option_count %u\n", packet->option_count); + + /*Options should be sorted beforehand*/ + for (option = packet->options ; option ; option = option->next){ + uint16_t delta = option->option - option_delta; + if ( !delta ){ + PRINTF("WARNING: Delta==Zero\n"); + } + buffer[index] = (delta) << COAP_HEADER_OPTION_DELTA_POSITION; + + PRINTF("option %u len %u option diff %u option_value addr %x option addr %x next option addr %x", option->option, option->len, option->option - option_delta, (uint16_t) option->value, (uint16_t)option, (uint16_t)option->next); + int i = 0; + for ( ; i < option->len ; i++ ){ + PRINTF(" (%u)", option->value[i]); + } + PRINTF("\n"); + + if (option->len < 0xF){ + buffer[index] |= option->len; + index++; + } else{ + buffer[index] |= (0xF); //1111 + buffer[index + 1] = option->len - (0xF); + index += 2; + } + + memcpy((char*)&buffer[index], option->value, option->len); + index += option->len; + option_delta += option->option; + } + +// //QUICK HACK TO SEND URL +// if(packet->url){ +// buffer[index] = (Option_Type_Uri_Path) << COAP_HEADER_OPTION_DELTA_POSITION; +// int uri_len = strlen(packet->url); +// if(uri_len < 0xF) +// { +// buffer[index] |= uri_len; +// strcpy((char*)&buffer[index + 1], packet->url); +// index += 1 + uri_len; +// } +// else +// { +// buffer[index] |= (0xF); //1111 +// buffer[index + 1] = uri_len - (0xF); +// strcpy((char*)&buffer[index + 2],packet->url); +// index += 2 + uri_len; +// } +// } + + if(packet->payload){ + memcpy(&buffer[index], packet->payload, packet->payload_len); + index += packet->payload_len; + } + + return index; +} diff --git a/apps/rest-coap/coap-common.h b/apps/rest-coap/coap-common.h new file mode 100644 index 000000000..6adb8bb95 --- /dev/null +++ b/apps/rest-coap/coap-common.h @@ -0,0 +1,132 @@ +/* + * coap.h + * + * Created on: Aug 25, 2010 + * Author: dogan + */ + +#ifndef COAP_COMMON_H_ +#define COAP_COMMON_H_ + +#define PORT 61616 + +/*COAP method types*/ +typedef enum { + COAP_GET = 1, + COAP_POST, + COAP_PUT, + COAP_DELETE +} coap_method_t; + +typedef enum { + MESSAGE_TYPE_CON, + MESSAGE_TYPE_NON, + MESSAGE_TYPE_ACK, + MESSAGE_TYPE_RST +} message_type; + +typedef enum { + OK_200 = 80, + CREATED_201 = 81, + NOT_MODIFIED_304 = 124, + BAD_REQUEST_400 = 160, + NOT_FOUND_404 = 164, + METHOD_NOT_ALLOWED_405 = 165, + UNSUPPORTED_MADIA_TYPE_415 = 175, + INTERNAL_SERVER_ERROR_500 = 200, + BAD_GATEWAY_502 = 202, + GATEWAY_TIMEOUT_504 = 204 +} status_code_t; + +typedef enum { + Option_Type_Content_Type = 1, + Option_Type_Max_Age = 2, + Option_Type_Etag = 4, + Option_Type_Uri_Authority = 5, + Option_Type_Location = 6, + Option_Type_Uri_Path = 9 +} option_type; + +typedef enum { + TEXT_PLAIN = 0, + TEXT_XML = 1, + TEXT_CSV = 2, + TEXT_HTML = 3, + IMAGE_GIF = 21, + IMAGE_JPEG = 22, + IMAGE_PNG = 23, + IMAGE_TIFF = 24, + AUDIO_RAW = 25, + VIDEO_RAW = 26, + APPLICATION_LINK_FORMAT = 40, + APPLICATION_XML = 41, + APPLICATION_OCTET_STREAM = 42, + APPLICATION_RDF_XML = 43, + APPLICATION_SOAP_XML = 44, + APPLICATION_ATOM_XML = 45, + APPLICATION_XMPP_XML = 46, + APPLICATION_EXI = 47, + APPLICATION_X_BXML = 48, + APPLICATION_FASTINFOSET = 49, + APPLICATION_SOAP_FASTINFOSET = 50, + APPLICATION_JSON = 51 +} content_type_t; + +#define COAP_HEADER_VERSION_MASK 0xC0 +#define COAP_HEADER_TYPE_MASK 0x30 +#define COAP_HEADER_OPTION_COUNT_MASK 0x0F +#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 +#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F + +#define COAP_HEADER_VERSION_POSITION 6 +#define COAP_HEADER_TYPE_POSITION 4 +#define COAP_HEADER_OPTION_DELTA_POSITION 4 + +#define REQUEST_BUFFER_SIZE 200 + +#define DEFAULT_CONTENT_TYPE 0 +#define DEFAULT_MAX_AGE 60 +#define DEFAULT_URI_AUTHORITY "" +#define DEFAULT_URI_PATH "" + +//keep open requests and their xactid + +struct header_option_t +{ + struct header_option_t* next; + uint16_t option; + uint16_t len; + uint8_t* value; +}; +typedef struct header_option_t header_option_t; + +typedef struct +{ + uint8_t ver; //2-bits currently set to 1. + uint8_t type; //2-bits Confirmable (0), Non-Confirmable (1), Acknowledgment (2) or Reset (3) + uint8_t option_count; //4-bits + uint8_t code; //8-bits Method or response code + uint16_t tid; //16-bit unsigned integer + header_option_t* options; + char* url; //put it just as a shortcut or else need to parse options everytime to access it. + uint16_t url_len; + char* query; + uint16_t query_len; + uint16_t payload_len; + uint8_t* payload; +} coap_packet_t; + +/*error definitions*/ +typedef enum +{ + NO_ERROR, + + /*Memory errors*/ + MEMORY_ALLOC_ERR, + MEMORY_BOUNDARY_EXCEEDED +} error_t; + +int serialize_packet(coap_packet_t* request, uint8_t* buffer); +void initialize_packet(coap_packet_t* packet); + +#endif /* COAP_COMMON_H_ */ diff --git a/apps/rest-coap/coap-server.c b/apps/rest-coap/coap-server.c new file mode 100644 index 000000000..9c204b056 --- /dev/null +++ b/apps/rest-coap/coap-server.c @@ -0,0 +1,397 @@ +#include +#include +#include /*for isxdigit*/ +#include "contiki.h" +#include "contiki-net.h" + +#include "rest.h" +#include "buffer.h" +#include "rest-util.h" + +#include "dev/leds.h" + +#if !UIP_CONF_IPV6_RPL +#include "static-routing.h" +#endif + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +#define MAX_PAYLOAD_LEN 120 +#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]) +static struct uip_udp_conn *server_conn; + +static service_callback service_cbk = NULL; + +void +coap_set_service_callback(service_callback callback) +{ + service_cbk = callback; +} + +static void +parse_message(coap_packet_t* packet, uint8_t* buf, uint16_t size) +{ + int processed=0; + int i=0; + PRINTF("parse_message size %d-->\n",size); + + initialize_packet(packet); + + packet->ver = (buf[0]&COAP_HEADER_VERSION_MASK)>>COAP_HEADER_VERSION_POSITION; + packet->type = (buf[0]&COAP_HEADER_TYPE_MASK)>>COAP_HEADER_TYPE_POSITION; + packet->option_count = buf[0]&COAP_HEADER_OPTION_COUNT_MASK; + packet->code = buf[1]; + packet->tid = (buf[2] << 8) + buf[3]; + + processed += 4; + + if (packet->option_count) { + int option_index = 0; + uint8_t option_delta; + uint16_t option_len; + uint8_t* option_buf = buf + processed; + packet->options = (header_option_t*)allocate_buffer(sizeof(header_option_t) * packet->option_count); + + if (packet->options) { + header_option_t* current_option = packet->options; + header_option_t* prev_option = NULL; + while(option_index < packet->option_count){ + /*FIXME : put boundary controls*/ + option_delta = (option_buf[i] & COAP_HEADER_OPTION_DELTA_MASK) >> COAP_HEADER_OPTION_DELTA_POSITION; + option_len = (option_buf[i] & COAP_HEADER_OPTION_SHORT_LENGTH_MASK); + i++; + if(option_len == 0xf){ + option_len += option_buf[i]; + i++; + } + + current_option->option = option_delta; + current_option->len = option_len; + current_option->value = option_buf + i; + if (option_index){ + prev_option->next = current_option; + /*This field defines the difference between the option Type of + * this option and the previous option (or zero for the first option)*/ + current_option->option += prev_option->option; + } + + if (current_option->option == Option_Type_Uri_Path){ + packet->url = (char*)current_option->value; + packet->url_len = current_option->len; + } + + PRINTF("OPTION %d %u %s \n", current_option->option, current_option->len, current_option->value); + + i += option_len; + option_index++; + prev_option = current_option++; + } + current_option->next = NULL; + } else { + PRINTF("MEMORY ERROR\n"); /*FIXME : add control here*/ + return; + } + } + processed += i; + + /**/ + if (processed < size) { + packet->payload = &buf[processed]; + packet->payload_len = size - processed; + } + + /*FIXME url is not decoded - is necessary?*/ + if (packet->url) { + if ((packet->query = strchr(packet->url, '?'))) { + uint16_t total_url_len = packet->url_len; + /*set query len and update url len so that it does not include query part now*/ + packet->url_len = packet->query - packet->url; + packet->query++; + packet->query_len = packet->url + total_url_len - packet->query; + + PRINTF("url %s, url_len %u, query %s, query_len %u\n", packet->url, packet->url_len, packet->query, packet->query_len); + } + } + + PRINTF("PACKET ver:%d type:%d oc:%d \ncode:%d tid:%u url:%s len:%u payload:%s pay_len %u\n", (int)packet->ver, (int)packet->type, (int)packet->option_count, (int)packet->code, packet->tid, packet->url, packet->url_len, packet->payload, packet->payload_len); +} + +int +coap_get_query_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size) +{ + if (packet->query) { + return get_variable(name, packet->query, packet->query_len, output, output_size, 0); + } + + return 0; +} + +int +coap_get_post_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size) +{ + if (packet->payload) { + return get_variable(name, packet->payload, packet->payload_len, output, output_size, 1); + } + + return 0; +} + +static header_option_t* +allocate_header_option(uint16_t variable_len) +{ + PRINTF("sizeof header_option_t %u variable size %u\n", sizeof(header_option_t), variable_len); + uint8_t* buffer = allocate_buffer(sizeof(header_option_t) + variable_len); + if (buffer){ + header_option_t* option = (header_option_t*) buffer; + option->next = NULL; + option->len = 0; + option->value = buffer + sizeof(header_option_t); + return option; + } + + return NULL; +} + +/*FIXME : does not overwrite the same option yet.*/ +static int +set_option(coap_packet_t* packet, option_type option_type, uint16_t len, uint8_t* value) +{ + PRINTF("set_option len %u\n", len); + header_option_t* option = allocate_header_option(len); + if (option){ + option->next = NULL; + option->len = len; + option->option = option_type; + memcpy(option->value, value, len); + header_option_t* option_current = packet->options; + header_option_t* prev = NULL; + while (option_current){ + if (option_current->option > option->option){ + break; + } + prev = option_current; + option_current = option_current->next; + } + + if (!prev){ + if (option_current){ + option->next = option_current; + } + packet->options = option; + } else{ + option->next = option_current; + prev->next = option; + } + + packet->option_count++; + + PRINTF("option->len %u option->option %u option->value %x next %x\n", option->len, option->option, (uint16_t) option->value, (uint16_t)option->next); + int i = 0; + for ( ; i < option->len ; i++ ){ + PRINTF(" (%u)", option->value[i]); + } + PRINTF("\n"); + + return 1; + } + + return 0; +} + +static uint8_t* +get_option(coap_packet_t* packet, option_type option_type) +{ + uint8_t* value = NULL; + int i=0; + + header_option_t* current_option = packet->options; + for (; packet->option_count; i++){ + if (current_option->option >= option_type){ + if (current_option->option == option_type){ + value = current_option->value; + } + break; + } + } + + return value; +} + +static void +fill_error_packet(coap_packet_t* packet, int error, uint16_t tid) +{ + packet->ver=1; + packet->option_count=0; + packet->url=NULL; + packet->options=NULL; + switch (error){ + case MEMORY_ALLOC_ERR: + packet->code=INTERNAL_SERVER_ERROR_500; + packet->tid=tid; + packet->type=MESSAGE_TYPE_ACK; + break; + default: + break; + } +} + +static void +init_response(coap_packet_t* request, coap_packet_t* response) +{ + initialize_packet(response); + if(request->type == MESSAGE_TYPE_CON) + { + response->code = OK_200; + response->tid = request->tid; + response->type = MESSAGE_TYPE_ACK; + } +} + +int +coap_set_payload(coap_packet_t* packet, uint8_t* payload, uint16_t size) +{ + packet->payload = copy_to_buffer(payload, size); + if (packet->payload) { + packet->payload_len = size; + return 1; + } + + return 0; +} + +int +coap_set_header_content_type(coap_packet_t* packet, content_type_t content_type) +{ + uint16_t len = 1; + + return set_option(packet, Option_Type_Content_Type, len, (uint8_t*) &content_type); +} + +content_type_t +coap_get_header_content_type(coap_packet_t* packet) +{ + uint8_t* value = get_option(packet, Option_Type_Content_Type); + if(value){ + return (uint8_t)*value; + } + + return DEFAULT_CONTENT_TYPE; +} + +int +coap_set_header_uri(coap_packet_t* packet, char* uri) +{ + return set_option(packet, Option_Type_Uri_Path, strlen(uri), (uint8_t*) uri); +} + +int +coap_set_header_etag(coap_packet_t* packet, uint8_t* etag, uint8_t size) +{ + return set_option(packet, Option_Type_Etag, size, etag); +} + +void +coap_set_code(coap_packet_t* packet, uint8_t code) +{ + packet->code = code; +} + +static int +handle_data(void) +{ + int error=NO_ERROR; + char buf[MAX_PAYLOAD_LEN]; + + printf("uip_datalen received %u \n",(u16_t)uip_datalen()); + + char* data = uip_appdata + uip_ext_len; + u16_t datalen = uip_datalen() - uip_ext_len; + + int data_size = 0; + + if(uip_newdata()) { + ((char *)data)[datalen] = 0; + PRINTF("Server received: '%s' (port:%u) from ", (char *)data, HTONS(UIP_UDP_BUF->srcport)); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + + if(init_buffer(COAP_DATA_BUFF_SIZE)) { + /*FIXME need to get rid of Request here since now COAP depends on REST layer.*/ + coap_packet_t* request = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); + parse_message(request, (uint8_t*)data, datalen); + + if(request->type != MESSAGE_TYPE_ACK) { + coap_packet_t* response = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); + init_response(request, response); + + if (service_cbk) { + service_cbk(request, response); + } + + data_size = serialize_packet(response, buf); + } + delete_buffer(); + } else { + PRINTF("Memory Alloc Error\n"); + error = MEMORY_ALLOC_ERR; + /*FIXME : Crappy way of accessing TID of the incoming packet, fix it!*/ + coap_packet_t error_packet; + fill_error_packet(&error_packet,error, (data[2] << 8) + data[3]); + data_size = serialize_packet(&error_packet, buf); + } + + uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); + server_conn->rport = UIP_UDP_BUF->srcport; + + PRINTF("Responding with message size: %d",data_size); + uip_udp_packet_send(server_conn, buf, data_size); + /* Restore server connection to allow data from any node */ + memset(&server_conn->ripaddr, 0, sizeof(server_conn->ripaddr)); + server_conn->rport = 0; + } + + return error; +} + +/*---------------------------------------------------------------------------*/ + +PROCESS(coap_server, "Coap Server"); +PROCESS_THREAD(coap_server, ev, data) +{ + PROCESS_BEGIN(); + PRINTF("COAP SERVER\n"); + +/* if static routes are used rather than RPL */ +#if !UIP_CONF_IPV6_RPL + set_global_address(); + configure_routing(); +#endif /*!UIP_CONF_IPV6_RPL*/ + + /* new connection with remote host */ + server_conn = udp_new(NULL, HTONS(0), NULL); + udp_bind(server_conn, HTONS(MOTE_PORT)); + PRINTF("Local/remote port %u/%u\n", + HTONS(server_conn->lport), HTONS(server_conn->rport)); + + while(1) { + PROCESS_YIELD(); + + if(ev == tcpip_event) { + handle_data(); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/rest-coap/coap-server.h b/apps/rest-coap/coap-server.h new file mode 100644 index 000000000..79279a452 --- /dev/null +++ b/apps/rest-coap/coap-server.h @@ -0,0 +1,39 @@ +/* + * coap-server.h + * + * Created on: Oct 4, 2010 + * Author: dogan + */ + +#ifndef COAPSERVER_H_ +#define COAPSERVER_H_ + +#define COAP_DATA_BUFF_SIZE 300 + +#include "contiki.h" + +/*Declare process*/ +PROCESS_NAME(coap_server); + +#define MOTE_PORT 61616 + +int coap_set_payload(coap_packet_t* packet, uint8_t* payload, uint16_t size); + +content_type_t coap_get_header_content_type(coap_packet_t* packet); +int coap_set_header_content_type(coap_packet_t* packet, content_type_t content_type); + +int coap_set_header_uri(coap_packet_t* packet, char* uri); +int coap_set_header_etag(coap_packet_t* packet, uint8_t* etag, uint8_t size); +void coap_set_code(coap_packet_t* packet, uint8_t code); +int coap_get_query_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size); +int coap_get_post_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size); + +/*Type definition of the service callback*/ +typedef int (*service_callback) (coap_packet_t* request, coap_packet_t* response); + +/* + *Setter of the service callback, this callback will be called in case of HTTP request. + */ +void coap_set_service_callback(service_callback callback); + +#endif /* COAPSERVER_H_ */ diff --git a/apps/rest-common/Makefile.rest-common b/apps/rest-common/Makefile.rest-common new file mode 100755 index 000000000..4ce0e5ac3 --- /dev/null +++ b/apps/rest-common/Makefile.rest-common @@ -0,0 +1 @@ +rest-common_src = rest.c rest-util.c buffer.c static-routing.c diff --git a/apps/rest-common/buffer.c b/apps/rest-common/buffer.c new file mode 100644 index 000000000..736e7cd86 --- /dev/null +++ b/apps/rest-common/buffer.c @@ -0,0 +1,75 @@ +/* + * buffer.c + * + * Created on: Oct 19, 2010 + * Author: dogan + */ + +#include +#include +#include "buffer.h" + +uint8_t* data_buffer; +uint16_t buffer_size; +uint16_t buffer_index; + +void +delete_buffer(void) +{ + if (data_buffer) { + free(data_buffer); + buffer_index = 0; + buffer_size = 0; + } +} + +uint8_t* +init_buffer(uint16_t size) +{ + delete_buffer(); + data_buffer = (uint8_t*)malloc(size); + if (data_buffer) { + buffer_size = size; + } + buffer_index = 0; + + return data_buffer; +} + +uint8_t* +allocate_buffer(uint16_t size) +{ + uint8_t* buffer = NULL; + /*To get rid of alignment problems, always allocate even size*/ + if (size % 2) { + size++; + } + if (buffer_index + size < buffer_size) { + buffer = data_buffer + buffer_index; + buffer_index += size; + } + + return buffer; +} + +uint8_t* +copy_to_buffer(void* data, uint16_t len) +{ + uint8_t* buffer = allocate_buffer(len); + if (buffer) { + memcpy(buffer, data, len); + } + + return buffer; +} + +uint8_t* +copy_text_to_buffer(char* text) +{ + uint8_t* buffer = allocate_buffer(strlen(text) + 1); + if (buffer) { + strcpy(buffer, text); + } + + return buffer; +} diff --git a/apps/rest-common/buffer.h b/apps/rest-common/buffer.h new file mode 100644 index 000000000..dd72ac2cf --- /dev/null +++ b/apps/rest-common/buffer.h @@ -0,0 +1,17 @@ +/* + * buffer.h + * + * Created on: Oct 19, 2010 + * Author: dogan + */ + +#ifndef BUFFER_H_ +#define BUFFER_H_ + +void delete_buffer(void); +uint8_t* init_buffer(uint16_t size); +uint8_t* allocate_buffer(uint16_t size); +uint8_t* copy_to_buffer(void* data, uint16_t len); +uint8_t* copy_text_to_buffer(char* text); + +#endif /* BUFFER_H_ */ diff --git a/apps/rest-common/rest-util.c b/apps/rest-common/rest-util.c new file mode 100644 index 000000000..71e03d1ea --- /dev/null +++ b/apps/rest-common/rest-util.c @@ -0,0 +1,73 @@ +/* + * rest-util.c + * + * Created on: Oct 26, 2010 + * Author: dogan + */ + +#include /*for size_t*/ +#include /*for isxdigit*/ +#include + +/*Copied from mangoose http server*/ +size_t +decode(const char *src, size_t srclen, char *dst, size_t dstlen, int is_form) +{ + size_t i, j; + int a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < srclen && j < dstlen - 1; i++, j++) { + if (src[i] == '%' && + isxdigit(* (unsigned char *) (src + i + 1)) && + isxdigit(* (unsigned char *) (src + i + 2))) { + a = tolower(* (unsigned char *) (src + i + 1)); + b = tolower(* (unsigned char *) (src + i + 2)); + dst[j] = ((HEXTOI(a) << 4) | HEXTOI(b)) & 0xff; + i += 2; + } else if (is_form && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return ( i == srclen ); +} + +/*Copied from mangoose http server*/ +int +get_variable(const char *name, const char *buffer, size_t buflen, char* output, size_t output_len, int decode_type) +{ + const char *start = NULL, *end = NULL, *end_of_value; + size_t var_len = 0; + + /*initialize the output buffer first*/ + *output = 0; + + var_len = strlen(name); + end = buffer + buflen; + + for (start = buffer; start + var_len < end; start++) + { + if ((start == buffer || start[-1] == '&') && start[var_len] == '=' && + ! strncmp(name, start, var_len)) + { + /* Point p to variable value */ + start += var_len + 1; + + /* Point s to the end of the value */ + end_of_value = (const char *) memchr(start, '&', end - start); + if (end_of_value == NULL) + { + end_of_value = end; + } + + return decode(start, end_of_value - start, output, output_len, decode_type); + } + } + + return 0; +} diff --git a/apps/rest-common/rest-util.h b/apps/rest-common/rest-util.h new file mode 100644 index 000000000..277468f98 --- /dev/null +++ b/apps/rest-common/rest-util.h @@ -0,0 +1,14 @@ +/* + * rest-util.h + * + * Created on: Oct 26, 2010 + * Author: dogan + */ + +#ifndef RESTUTIL_H_ +#define RESTUTIL_H_ + +size_t decode(const char *src, size_t srclen, char *dst, size_t dstlen, int is_form); +int get_variable(const char *name, const char *buffer, size_t buflen, char* output, size_t output_len, int decode_type); + +#endif /* RESTUTIL_H_ */ diff --git a/apps/rest-common/rest.c b/apps/rest-common/rest.c new file mode 100755 index 000000000..dec10f8bd --- /dev/null +++ b/apps/rest-common/rest.c @@ -0,0 +1,201 @@ +#include "contiki.h" +#include /*for string operations in match_addresses*/ +#include "rest.h" +#include "buffer.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*FIXME it is possible to define some of the rest functions as MACROs rather than functions full of ifdefs.*/ + +LIST(restful_services); + +void +rest_init(void) +{ + list_init(restful_services); + +#ifdef WITH_COAP + coap_set_service_callback(rest_invoke_restful_service); +#else /*WITH_COAP*/ + http_set_service_callback(rest_invoke_restful_service); +#endif /*WITH_COAP*/ + + //start the coap server + process_start(SERVER_PROCESS, NULL); +} + +void +rest_activate_resource(Resource_t* resource) +{ + /*add it to the restful web service link list*/ + list_add(restful_services, resource); +} + +int +rest_invoke_restful_service(REQUEST* request, RESPONSE* response) +{ + int found = 0; + const char* url = request->url; + uint16_t url_len = request->url_len; + + PRINTF("rest_invoke_restful_service url %s url_len %d -->\n", url, url_len); + + Resource_t* resource = NULL; + + for ( resource = (Resource_t*)list_head(restful_services); resource ; resource = resource->next ){ + /*if the web service handles that kind of requests and urls matches*/ + /*FIXME Need to make case insensitive?*/ + if (url && strlen(resource->url) == url_len && strncmp(resource->url, url, url_len) == 0){ + found = 1; + method_t method = rest_get_method_type(request); + + PRINTF("method %u, resource->methods_to_handle %u\n", (uint16_t)method, resource->methods_to_handle); + + //DY FIX_ME resources can only handle 1 method currently anyway, fix this + if ( resource->methods_to_handle & method ) { + + /*call pre handler if it exists*/ + if (!resource->pre_handler || resource->pre_handler(request, response)) { + /* call handler function*/ + resource->handler(request, response); + + /*call post handler if it exists*/ + if (resource->post_handler) { + resource->post_handler(request, response); + } + } + } else { + rest_set_response_status(response, METHOD_NOT_ALLOWED_405); + } + break; + } + } + + if (!found) { + rest_set_response_status(response, NOT_FOUND_404); + } + + return found; +} + +void +rest_set_user_data(Resource_t* resource, void* user_data) +{ + resource->user_data = user_data; +} + +void* +rest_get_user_data(Resource_t* resource) +{ + return resource->user_data; +} + +void +rest_set_pre_handler(Resource_t* resource, restful_pre_handler pre_handler) +{ + resource->pre_handler = pre_handler; +} + +void +rest_set_post_handler(Resource_t* resource, restful_post_handler post_handler) +{ + resource->post_handler = post_handler; +} + +list_t +rest_get_resources(void) +{ + return restful_services; +} + +void +rest_set_response_status(RESPONSE* response, status_code_t status) +{ +#ifdef WITH_COAP + coap_set_code(response, (uint8_t)status); +#else /*WITH_COAP*/ + http_set_status(response, status); +#endif /*WITH_COAP*/ +} + + +method_t +rest_get_method_type(REQUEST* request) +{ +#ifdef WITH_COAP + return (method_t)((request->code) << (request->code - 1)); +#else + return (method_t)(request->request_type); +#endif +} + +void +rest_set_payload(RESPONSE* response, uint8_t* payload, uint16_t size) +{ +#ifdef WITH_COAP + coap_set_payload(response, payload, size); +#else + http_set_payload(response, payload, size); +#endif /*WITH_COAP*/ +} + +int +rest_get_query_variable(REQUEST* request, const char *name, char* output, uint16_t output_size) +{ +#ifdef WITH_COAP + return coap_get_query_variable(request, name, output, output_size); +#else + return http_get_query_variable(request, name, output, output_size); +#endif /*WITH_COAP*/ +} + +int +rest_get_post_variable(REQUEST* request, const char *name, char* output, uint16_t output_size) +{ +#ifdef WITH_COAP + return coap_get_post_variable(request, name, output, output_size); +#else + return http_get_post_variable(request, name, output, output_size); +#endif /*WITH_COAP*/ +} + +content_type_t +rest_get_header_content_type(REQUEST* request) +{ +#ifdef WITH_COAP + return coap_get_header_content_type(request); +#else + return http_get_header_content_type(request); +#endif /*WITH_COAP*/ +} + +int +rest_set_header_content_type(RESPONSE* response, content_type_t content_type) +{ +#ifdef WITH_COAP + return coap_set_header_content_type(response, content_type); +#else + return http_set_res_header(response, HTTP_HEADER_NAME_CONTENT_TYPE, http_get_content_type_string(content_type), 1); +#endif /*WITH_COAP*/ + +} + +int +rest_set_header_etag(RESPONSE* response, uint8_t* etag, uint8_t size) +{ +#ifdef WITH_COAP + return coap_set_header_etag(response, etag, size); +#else + /*FIXME for now etag should be a "/0" ending string for http part*/ + char temp_etag[10]; + memcpy(temp_etag, etag, size); + temp_etag[size] = 0; + return http_set_res_header(response, HTTP_HEADER_NAME_ETAG, temp_etag, 1); +#endif /*WITH_COAP*/ +} diff --git a/apps/rest-common/rest.h b/apps/rest-common/rest.h new file mode 100755 index 000000000..f596383ef --- /dev/null +++ b/apps/rest-common/rest.h @@ -0,0 +1,151 @@ +#ifndef REST_H_ +#define REST_H_ + +/*includes*/ +#include "contiki.h" +#include "contiki-lib.h" + +#ifdef WITH_COAP + #include "coap-common.h" +#include "coap-server.h" + #define REQUEST coap_packet_t + #define RESPONSE coap_packet_t + #define SERVER_PROCESS (&coap_server) +#else /*WITH_COAP*/ + /*WITH_HTTP*/ + #include "http-common.h" + #include "http-server.h" + #define REQUEST http_request_t + #define RESPONSE http_response_t + #define SERVER_PROCESS (&http_server) +#endif /*WITH_COAP*/ + +struct Resource_t; + +/*REST method types*/ +typedef enum { + METHOD_GET = (1 << 0), + METHOD_HEAD = (1 << 1), + METHOD_POST = (1 << 2), + METHOD_PUT = (1 << 3), + METHOD_DELETE = (1 << 4) +} method_t; + +/*Signature of handler functions*/ +typedef void (*restful_handler) (REQUEST* request, RESPONSE* response); +typedef int (*restful_pre_handler) (REQUEST* request, RESPONSE* response); +typedef void (*restful_post_handler) (REQUEST* request, RESPONSE* response); + +typedef void (*restful_periodic_handler) (struct Resource_t* resource); + +/* + * Data structure representing a resource in REST. + */ +struct Resource_t { + struct Resource_t *next; /*points to next resource defined*/ + method_t methods_to_handle; /*handled HTTP methods*/ + const char* url; /*handled URL*/ + restful_handler handler; /*handler function*/ + restful_pre_handler pre_handler; /*to be called before handler, may perform initializations*/ + restful_post_handler post_handler; /*to be called after handler, may perform finalizations (cleanup, etc)*/ + void* user_data; /*pointer to user specific data*/ +}; +typedef struct Resource_t Resource_t; + + +/* + * Macro to define a Resource + * Resources are statically defined for the sake of efficiency and better memory management. + */ +#define RESOURCE(name, methods_to_handle,url) \ +void name##_handler(REQUEST* request, RESPONSE* response); \ +struct etimer timer_##name; \ +Resource_t resource_##name = {NULL, methods_to_handle, url, name##_handler, NULL, NULL, NULL} + +/* + * Initializes REST framework and starts HTTP or COAP process + */ +void rest_init(void); + +/* + * Resources wanted to be accessible should be activated with the following code. + */ +void rest_activate_resource(Resource_t* resource); + +/* + * To be called by HTTP/COAP server as a callback function when a new service request appears. + * This function dispatches the corresponding RESTful service. + */ +int rest_invoke_restful_service(REQUEST* request, RESPONSE* response); + +/* + * Returns the resource list + */ +list_t rest_get_resources(void); + +/* + * Returns query variable in the URL. + * Returns true if the variable found, false otherwise. + * Variable is put in the buffer provided. + */ +int rest_get_query_variable(REQUEST* request, const char *name, char* output, uint16_t output_size); + +/* + * Returns variable in the Post Data/Payload. + * Returns true if the variable found, false otherwise. + * Variable is put in the buffer provided. + */ +int rest_get_post_variable(REQUEST* request, const char *name, char* output, uint16_t output_size); + +method_t rest_get_method_type(REQUEST* request); + +/* + * Getter for the request content type + */ +content_type_t rest_get_header_content_type(REQUEST* request); + +/* + * Setter for the response content type + */ +int rest_set_header_content_type(RESPONSE* response, content_type_t content_type); + +/* + * Setter for the response etag header + */ +int rest_set_header_etag(RESPONSE* response, uint8_t* etag, uint8_t size); + +/* + * Setter for the status code (200, 201, etc) of the response. + */ +void rest_set_response_status(RESPONSE* response, status_code_t status); + +/* + * Setter for the payload of the response + */ +void rest_set_payload(RESPONSE* response, uint8_t* payload, uint16_t size); + +/* + * Getter method for user specific data. + */ +void* rest_get_user_data(Resource_t* resource); + +/* + * Setter method for user specific data. + */ +void rest_set_user_data(Resource_t* resource, void* user_data); + +/* + * Sets the pre handler function of the Resource. + * If set, this function will be called just before the original handler function. + * Can be used to setup work before resource handling. + */ +void rest_set_pre_handler(Resource_t* resource, restful_pre_handler pre_handler); + +/* + * Sets the post handler function of the Resource. + * If set, this function will be called just after the original handler function. + * Can be used to do cleanup (deallocate memory, etc) after resource handling. + */ +void rest_set_post_handler(Resource_t* resource, restful_post_handler post_handler); + +#endif /*REST_H_*/ diff --git a/apps/rest-common/static-routing.c b/apps/rest-common/static-routing.c new file mode 100644 index 000000000..241ff3027 --- /dev/null +++ b/apps/rest-common/static-routing.c @@ -0,0 +1,73 @@ +/* + * static-routing.c + * + * Created on: Oct 12, 2010 + * Author: dogan + */ + +#include "static-routing.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + + +#if !UIP_CONF_IPV6_RPL +#include "contiki-net.h" +#include "node-id.h" + +void set_global_address(void) +{ + uip_ipaddr_t ipaddr; + + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); +} + +void configure_routing(void) +{ + PRINTF("configure_routing\n"); + + if(node_id < 10) //COOJA + { + //Go to desktop machine over border router + ADD_ROUTE(DESKTOP_MACHINE_ID,COOJA_BORDER_ROUTER_ID); + } + else //SKY + { + if(node_id < 20) //First hops (ids between 10-20) + { + //Go to desktop machine over border router + ADD_ROUTE(DESKTOP_MACHINE_ID, BORDER_ROUTER_ID); + } + + switch(node_id) + { + case 12: + ADD_ROUTE(22, 22); //Go to next hop over the local address of next hop + break; + case 13: + ADD_ROUTE(23, 23); //Go to next hop over the local address of next hop + break; + + case 22: + ADD_ROUTE(0, 12); //Go to desktop machine over the corresponding first hop + break; + case 23: + ADD_ROUTE(0, 13); //Go to desktop machine over the corresponding first hop + break; + default: + break; + } + } +} +#endif /*!UIP_CONF_IPV6_RPL*/ diff --git a/apps/rest-common/static-routing.h b/apps/rest-common/static-routing.h new file mode 100644 index 000000000..d08304545 --- /dev/null +++ b/apps/rest-common/static-routing.h @@ -0,0 +1,56 @@ +/* + * static-routing.h + * + * Created on: Oct 12, 2010 + * Author: dogan + */ + +#ifndef STATICROUTING_H_ +#define STATICROUTING_H_ + +#define NODE_IP(nodeid,type,ipaddr) NODE_##nodeid##_##type(ipaddr) + +//desktop machine +#define DESKTOP_MACHINE_ID 0 +#define NODE_0_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0x0001) + +//Cooja Nodes +#define COOJA_BORDER_ROUTER_ID 1 +#define NODE_1_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7401, 0x0001, 0x0101) +#define NODE_1_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7401, 0x0001, 0x0101) + +#define NODE_2_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7402, 0x0002, 0x0202) +#define NODE_2_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7402, 0x0002, 0x0202) + +#define NODE_6_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7406, 0x0006, 0x0606) +#define NODE_6_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7406, 0x0006, 0x0606) + +//nodes +#define BORDER_ROUTER_ID 11 +#define NODE_11_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7400, 0x116e, 0xd5f1) +#define NODE_11_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7400, 0x116e, 0xd5f1) + +#define NODE_12_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7400, 0x116e, 0xc0f6) +#define NODE_12_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7400, 0x116e, 0xc0f6) + +#define NODE_13_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7400, 0x117d, 0x3575) +#define NODE_13_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7400, 0x117d, 0x3575) + +#define NODE_22_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7400, 0x1160, 0xf95a) +#define NODE_22_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7400, 0x1160, 0xf95a) + +#define NODE_23_GLOBAL(ipaddr) uip_ip6addr(ipaddr, 0xaaaa, 0, 0, 0, 0x0212, 0x7400, 0x117d, 0x0d5a) +#define NODE_23_LOCAL(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7400, 0x117d, 0x0d5a) + +#define ADD_ROUTE(node_global,node_local)\ +do{\ + uip_ipaddr_t ipaddr_local, ipaddr_global;\ + NODE_IP(node_global, GLOBAL, &ipaddr_global);\ + NODE_IP(node_local, LOCAL, &ipaddr_local);\ + uip_ds6_route_add(&ipaddr_global, 128, &ipaddr_local, 0);\ +}while(0) + +void set_global_address(void); +void configure_routing(void); + +#endif /* STATICROUTING_H_ */ diff --git a/apps/rest-http/Makefile.rest-http b/apps/rest-http/Makefile.rest-http new file mode 100755 index 000000000..3d946d2d9 --- /dev/null +++ b/apps/rest-http/Makefile.rest-http @@ -0,0 +1,4 @@ +rest-http_src = http-common.c http-server.c + +APPS += rest-common +include $(CONTIKI)/apps/rest-common/Makefile.rest-common diff --git a/apps/rest-http/http-common.c b/apps/rest-http/http-common.c new file mode 100755 index 000000000..0d3deefa3 --- /dev/null +++ b/apps/rest-http/http-common.c @@ -0,0 +1,29 @@ +#include "http-common.h" + +/*needed for web services giving all path (http://172.16.79.0/services/light1) + * instead relative (/services/light1) in HTTP request. Ex: Restlet lib.*/ +const char* http_string = "http"; + +/*HTTP method strings*/ +const char* http_get_string = "GET"; +const char* http_head_string = "HEAD"; +const char* http_post_string = "POST"; +const char* http_put_string = "PUT"; +const char* http_delete_string = "DELETE"; + +const char* httpv1_1 = "HTTP/1.1"; +const char* line_end = "\r\n"; +const char* contiki = "Contiki"; +const char* close = "close"; + +/*header names*/ +const char* HTTP_HEADER_NAME_CONTENT_TYPE = "Content-Type"; +const char* HTTP_HEADER_NAME_CONTENT_LENGTH = "Content-Length"; +const char* HTTP_HEADER_NAME_LOCATION = "Location"; +const char* HTTP_HEADER_NAME_CONNECTION = "Connection"; +const char* HTTP_HEADER_NAME_SERVER = "Server"; +const char* HTTP_HEADER_NAME_HOST = "Host"; +const char* HTTP_HEADER_NAME_IF_NONE_MATCH = "If-None-Match"; +const char* HTTP_HEADER_NAME_ETAG = "ETag"; + +const char* header_delimiter = ": "; diff --git a/apps/rest-http/http-common.h b/apps/rest-http/http-common.h new file mode 100755 index 000000000..4bb346248 --- /dev/null +++ b/apps/rest-http/http-common.h @@ -0,0 +1,144 @@ +#ifndef HTTPCOMMON_H_ +#define HTTPCOMMON_H_ + +/*includes*/ +#include "contiki.h" +#include "contiki-net.h" + +/*current state of the request, waiting: handling request, output: sending response*/ +#define STATE_WAITING 0 +#define STATE_OUTPUT 1 + +/*definitions of the line ending characters*/ +#define LINE_FEED_CHAR '\n' +#define CARRIAGE_RETURN_CHAR '\r' + +/*needed for web services giving all path (http://172.16.79.0/services/light1) + * instead relative (/services/light1) in HTTP request. Ex: Restlet lib. does it*/ +extern const char* http_string; + +/*HTTP method strings*/ +extern const char* http_get_string; +extern const char* http_head_string; +extern const char* http_post_string; +extern const char* http_put_string; +extern const char* http_delete_string; + +extern const char* httpv1_1; +extern const char* line_end; +extern const char* contiki; +extern const char* close; + +/*header names*/ +extern const char* HTTP_HEADER_NAME_CONTENT_TYPE; +extern const char* HTTP_HEADER_NAME_CONTENT_LENGTH; +extern const char* HTTP_HEADER_NAME_LOCATION; +extern const char* HTTP_HEADER_NAME_CONNECTION; +extern const char* HTTP_HEADER_NAME_SERVER; +extern const char* HTTP_HEADER_NAME_HOST; +extern const char* HTTP_HEADER_NAME_IF_NONE_MATCH; +extern const char* HTTP_HEADER_NAME_ETAG; + +extern const char* header_delimiter; + + +/*Configuration parameters*/ +#define HTTP_PORT 8080 +#define HTTP_DATA_BUFF_SIZE 600 +#define INCOMING_DATA_BUFF_SIZE 102 /*100+2, 100 = max url len, 2 = space char+'\0'*/ + +/*HTTP method types*/ +typedef enum { + HTTP_METHOD_GET = (1 << 0), + HTTP_METHOD_HEAD = (1 << 1), + HTTP_METHOD_POST = (1 << 2), + HTTP_METHOD_PUT = (1 << 3), + HTTP_METHOD_DELETE = (1 << 4) +} http_method_t; + +//DY : FIXME right now same enum names with COAP with different values. Will this work fine? +typedef enum { + OK_200 = 200, + CREATED_201 = 201, + NOT_MODIFIED_304 = 304, + BAD_REQUEST_400 = 400, + NOT_FOUND_404 = 404, + METHOD_NOT_ALLOWED_405 = 405, + REQUEST_URI_TOO_LONG_414 = 414, + UNSUPPORTED_MADIA_TYPE_415 = 415, + INTERNAL_SERVER_ERROR_500 = 500, + BAD_GATEWAY_502 = 502, + SERVICE_UNAVAILABLE_503 = 503, + GATEWAY_TIMEOUT_504 = 504 +} status_code_t; + +typedef enum { + TEXT_PLAIN, + TEXT_XML, + TEXT_CSV, + TEXT_HTML, + APPLICATION_XML, + APPLICATION_EXI, + APPLICATION_JSON, + APPLICATION_LINK_FORMAT, + APPLICATION_WWW_FORM, + UNKNOWN_CONTENT_TYPE +} content_type_t; + +/*Header type*/ +struct http_header_t { + struct http_header_t* next; + char* name; + char* value; +}; +typedef struct http_header_t http_header_t; + +/*This structure contains information about the HTTP request.*/ +struct http_request_t { + char* url; + uint16_t url_len; + http_method_t request_type; /* GET, POST, etc */ + char* query; + uint16_t query_len; + http_header_t* headers; + uint16_t payload_len; + uint8_t* payload; +}; +typedef struct http_request_t http_request_t; + +/*This structure contains information about the HTTP response.*/ +struct http_response_t { + status_code_t status_code; + char* status_string; + http_header_t* headers; + uint16_t payload_len; + uint8_t* payload; +}; +typedef struct http_response_t http_response_t; + +/*This structure contains information about the TCP Connection.*/ +typedef struct { + struct psock sin, sout; /*Protosockets for incoming and outgoing communication*/ + struct pt outputpt; + char inputbuf[INCOMING_DATA_BUFF_SIZE]; /*to put incoming data in*/ + uint8_t state; + http_request_t request; + http_response_t response; +} connection_state_t; + +/*error definitions*/ +typedef enum { + HTTP_NO_ERROR, + + /*Memory errors*/ + HTTP_MEMORY_ALLOC_ERR, + HTTP_MEMORY_BOUNDARY_EXCEEDED, + + /*specific errors*/ + HTTP_XML_NOT_VALID, + HTTP_SOAP_MESSAGE_NOT_VALID, + HTTP_URL_TOO_LONG, + HTTP_URL_INVALID +} http_error_t; + +#endif /*HTTPCOMMON_H_*/ diff --git a/apps/rest-http/http-server.c b/apps/rest-http/http-server.c new file mode 100755 index 000000000..ef0ede80c --- /dev/null +++ b/apps/rest-http/http-server.c @@ -0,0 +1,646 @@ +#include +#include /*for atoi*/ +#include +#include "contiki.h" + +#include "http-server.h" +#include "buffer.h" +#include "rest-util.h" + +#if !UIP_CONF_IPV6_RPL +#include "static-routing.h" +#endif + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +static void +init_response(http_response_t* response) +{ + response->status_code = OK_200; + response->status_string = NULL; + response->headers = NULL; + response->payload = NULL; + response->payload_len = 0; +} + +static void +init_request(http_request_t* request) +{ + request->request_type = 0; + request->url = NULL; + request->url_len = 0; + request->query = NULL; + request->query_len = 0; + request->headers = NULL; + request->payload = NULL; + request->payload_len = 0; +} + +/** + * Initializes the connection state by clearing out the data structures + */ +static void +init_connection(connection_state_t* conn_state) +{ + conn_state->state = STATE_WAITING; + + init_request(&conn_state->request); + init_response(&conn_state->response); +} + +void +http_set_status(http_response_t* response, status_code_t status) +{ + response->status_code = status; +} + +static http_header_t* +allocate_header(uint16_t variable_len) +{ + PRINTF("sizeof http_header_t %u variable size %u\n", sizeof(http_header_t), variable_len); + uint8_t* buffer = allocate_buffer(sizeof(http_header_t) + variable_len); + if (buffer) { + http_header_t* option = (http_header_t*) buffer; + option->next = NULL; + option->name = NULL; + option->value = buffer + sizeof(http_header_t); + return option; + } + + return NULL; +} + +int +http_set_res_header(http_response_t* response, const char* name, const char* value, int copy) +{ + PRINTF("http_set_res_header (copy:%d) %s:%s\n", copy, name, value); + uint16_t size = 0; + http_header_t* current_header = NULL; + http_header_t* head = NULL; + + if (copy) { + size += strlen(value) + 1; + } + + current_header = allocate_header(size); + + if (current_header) { + current_header->name = (char*)name; + if (copy) { + strcpy(current_header->value, value); + } else { + current_header->value = (char*)value; + } + + head = response->headers; + response->headers = current_header; + if (head) { + current_header->next = head; + } + + return 1; + } + + return 0; +} + +static const char* is_request_hdr_needed(const char* header_name) +{ + const char* header = NULL; + /*FIXME add needed headers here*/ + if (strcmp(header_name, HTTP_HEADER_NAME_CONTENT_LENGTH) == 0) { + header = HTTP_HEADER_NAME_CONTENT_LENGTH; + } else if (strcmp(header_name, HTTP_HEADER_NAME_CONTENT_TYPE) == 0) { + header = HTTP_HEADER_NAME_CONTENT_TYPE; + } + + return header; +} + +static service_callback service_cbk = NULL; + +void +http_set_service_callback(service_callback callback) +{ + service_cbk = callback; +} + +const char* content_types[] = { + "text/plain", + "text/xml", + "text/csv", + "text/html", + "application/xml", + "application/exi", + "application/json", + "application/link-format", + "application/x-www-form-urlencoded", +}; + +const char* +http_get_content_type_string(content_type_t content_type) +{ + return content_types[content_type]; +} + +char* +get_default_status_string(status_code_t status_code) +{ + char* value = NULL; + switch(status_code) { + case 200: + value = "OK"; + break; + case 201: + value = "Created"; + break; + case 202: + value = "Accepted"; + break; + case 204: + value = "No Content"; + break; + case 304: + value = "Not Modified"; + break; + case 400: + value = "Bad Request" ; + break; + case 404: + value = "Not Found" ; + break; + case 405: + value = "Method Not Allowed" ; + break; + case 406: + value = "Not Acceptable" ; + break; + case 414: + value = "Request-URI Too Long" ; + break; + case 415: + value = "Unsupported Media Type" ; + break; + case 500: + value = "Internal Server Error" ; + break; + case 501: + value = "Not Implemented" ; + break; + case 503: + value = "Service Unavailable" ; + break; + /*FIXME : will be removed later, put to catch the unhandled statuses.*/ + default: + value = "$$BUG$$"; + break; + } + + return value; +} + +int +http_get_query_variable(http_request_t* request, const char *name, char* output, uint16_t output_size) +{ + if (request->query) { + return get_variable(name, request->query, request->query_len, output, output_size, 0); + } + + return 0; +} + +int +http_get_post_variable(http_request_t* request, const char *name, char* output, uint16_t output_size) +{ + if (request->payload) { + return get_variable(name, request->payload, request->payload_len, output, output_size, 1); + } + + return 0; +} + +static int +is_method_handled(connection_state_t* conn_state, const char* method) +{ + /*other method types can be added here if needed*/ + if(strncmp(method, http_get_string, 3) == 0) { + conn_state->request.request_type = HTTP_METHOD_GET; + } else if(strncmp(method, http_head_string, 4) == 0) { + conn_state->request.request_type = HTTP_METHOD_HEAD; + } else if (strncmp(method, http_post_string, 4) == 0) { + conn_state->request.request_type = HTTP_METHOD_POST; + } else if (strncmp(method, http_put_string, 3) == 0) { + conn_state->request.request_type = HTTP_METHOD_PUT; + } else if (strncmp(method, http_delete_string, 3) == 0) { + conn_state->request.request_type = HTTP_METHOD_DELETE; + } else { + PRINTF("No Method supported : %s\nstate : %d\n", conn_state->inputbuf, conn_state->state); + return 0; + } + + return 1; +} + +static int +parse_url(connection_state_t* conn_state, char* url) +{ + int error = HTTP_NO_ERROR; + int full_url_path = 0; + /*even for default index.html there is / Ex: GET / HTTP/1.1*/ + if (url[0] != '/') { + /*if url is complete (http://...) rather than relative*/ + if (strncmp(url, http_string, 4) != 0 ) { + PRINTF("Url not valid : %s \n",url); + error = HTTP_URL_INVALID; + } else { + full_url_path = 1; + } + } + + if (error == HTTP_NO_ERROR) { + char* url_buffer = url; + if (full_url_path) { + unsigned char num_of_slash = 0; + do { + url_buffer = strchr( ++url_buffer, '/' ); + + PRINTF("Buffer : %s %d\n", url_buffer, num_of_slash); + + } while (url_buffer && ++num_of_slash < 3); + } + + PRINTF("Url found :%s\n", url_buffer); + + /*Get rid of the first slash*/ + if (url_buffer && ++url_buffer) { + conn_state->request.url = (char*) copy_text_to_buffer(url_buffer); + conn_state->request.url_len = strlen(url_buffer); + + if ((conn_state->request.query = strchr(conn_state->request.url, '?'))) { + *(conn_state->request.query++) = 0; + /*update url len - decrease the size of query*/ + conn_state->request.url_len = strlen(conn_state->request.url); + conn_state->request.query_len = strlen(conn_state->request.query); + } + + PRINTF("url %s, url_len %u, query %s, query_len %u\n", conn_state->request.url, conn_state->request.url_len, conn_state->request.query, conn_state->request.query_len); + + /*FIXME url is not decoded - should be done here*/ + } else { + error = HTTP_URL_INVALID; + } + } + + return error; +} + +static int +parse_header(connection_state_t* conn_state, char* inputbuf) +{ + PRINTF("parse_header --->\n"); + const char* header_name = NULL; + + char* delimiter = strchr(inputbuf, ':'); + if (delimiter) { + *delimiter++ = 0; /*after increment delimiter will point space char*/ + + header_name = is_request_hdr_needed(inputbuf); + if (header_name && delimiter) { + char* buffer = delimiter; + + if (buffer[0] == ' ') { + buffer++; + } + + http_header_t* current_header = NULL; + http_header_t* head = NULL; + + current_header = allocate_header(strlen(buffer)); + + if (current_header) { + current_header->name = (char*)header_name; + strcpy(current_header->value, buffer); + } + + head = conn_state->request.headers; + conn_state->request.headers = current_header; + if (head) { + current_header->next = head; + } + + return 1; + } + } + + return 0; +} + +int +http_set_payload(http_response_t* response, uint8_t* payload, uint16_t size) +{ + response->payload = copy_to_buffer(payload, size); + if (response->payload) { + response->payload_len = size; + return 1; + } + + return 0; +} + +static const char* +get_header(http_header_t* headers, const char* hdr_name) +{ + for (;headers; headers = headers->next) { + if (strcmp(headers->name, hdr_name) == 0) { + return headers->value; + } + } + + return NULL; +} + +const char* http_get_req_header(http_request_t* request, const char* name) +{ + return get_header(request->headers, name); +} + +content_type_t http_get_header_content_type(http_request_t* request) +{ + const char* content_type_string = http_get_req_header(request, HTTP_HEADER_NAME_CONTENT_TYPE); + if (content_type_string) { + int i = 0; + for(; i < sizeof(content_types)/sizeof(const char*) ; i++) { + if (strcmp(content_types[i], content_type_string)) { + return (content_type_t)i; + } + } + } + + return UNKNOWN_CONTENT_TYPE; +} + +static +PT_THREAD(handle_request(connection_state_t* conn_state)) +{ + PSOCK_BEGIN(&(conn_state->sin)); + + static int error; + const char* content_len = NULL; + + error = HTTP_NO_ERROR; /*always reinit static variables due to protothreads*/ + + PRINTF("Request--->\n"); + + //read method + PSOCK_READTO(&(conn_state->sin), ' '); + + if (!is_method_handled(conn_state, conn_state->inputbuf)) { + /*method not handled*/ + http_set_status(&conn_state->response, SERVICE_UNAVAILABLE_503); + conn_state->state = STATE_OUTPUT; + } else { + /*read until the end of url*/ + PSOCK_READTO(&(conn_state->sin), ' '); + + /*-1 is needed since it also includes space char*/ + if (conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] != ' ' ) { + error = HTTP_URL_TOO_LONG; + } + + conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0; + + PRINTF("Read URL:%s\n", conn_state->inputbuf); + + if (error == HTTP_NO_ERROR) { + error = parse_url(conn_state, conn_state->inputbuf); + } + + if (error != HTTP_NO_ERROR) { + if (error == HTTP_URL_TOO_LONG) { + http_set_status(&conn_state->response, REQUEST_URI_TOO_LONG_414); + } else { + http_set_status(&conn_state->response, BAD_REQUEST_400); + } + + conn_state->state = STATE_OUTPUT; + } else { + /*read until the end of HTTP version - not used yet*/ + PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR); + + PRINTF("After URL:%s\n", conn_state->inputbuf); + + /*FIXME : PSOCK_READTO takes just a single delimiter so I read till the end of line + but now it may not fit in the buffer. If PSOCK_READTO would take two delimiters, + we would have read until : and so it would not be blocked.*/ + + /*Read the headers and store the necessary ones*/ + do { + /*read the next line*/ + PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR); + conn_state->inputbuf[ PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0; + + /*if headers finished then stop the infinite loop*/ + if (conn_state->inputbuf[0] == CARRIAGE_RETURN_CHAR || conn_state->inputbuf[0] == 0) { + PRINTF("Finished Headers!\n\n"); + break; + } + + parse_header(conn_state, conn_state->inputbuf); + } + while(1); + + content_len = get_header(conn_state->request.headers, HTTP_HEADER_NAME_CONTENT_LENGTH); + if (content_len) { + conn_state->request.payload_len = atoi(content_len); + + PRINTF("Post Data Size string: %s int: %d\n", content_len, conn_state->request.payload_len); + } + + if (conn_state->request.payload_len) { + static uint16_t read_bytes = 0; + /*init the static variable again*/ + read_bytes = 0; + + conn_state->request.payload = allocate_buffer(conn_state->request.payload_len + 1); + + if (conn_state->request.payload) { + do { + PSOCK_READBUF(&(conn_state->sin)); + /*null terminate the buffer in case it is a string.*/ + conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin))] = 0; + + memcpy(conn_state->request.payload + read_bytes, conn_state->inputbuf, PSOCK_DATALEN(&(conn_state->sin))); + + read_bytes += PSOCK_DATALEN(&(conn_state->sin)); + + } while (read_bytes < conn_state->request.payload_len); + + conn_state->request.payload[read_bytes++] = 0; + + PRINTF("PostData => %s \n", conn_state->request.payload); + } else { + error = HTTP_MEMORY_ALLOC_ERR; + } + } + + if (error == HTTP_NO_ERROR) { + if (service_cbk) { + service_cbk(&conn_state->request, &conn_state->response); + } + } else { + PRINTF("Error:%d\n",error); + http_set_status(&conn_state->response, INTERNAL_SERVER_ERROR_500); + } + + conn_state->state = STATE_OUTPUT; + } + } + + PSOCK_END(&(conn_state->sin)); +} + +static +PT_THREAD(send_data(connection_state_t* conn_state)) +{ + PSOCK_BEGIN(&(conn_state->sout)); + + PRINTF("send_data -> \n"); + + uint16_t index = 0; + http_response_t* response = &conn_state->response; + http_header_t* header = response->headers; + uint8_t* buffer = allocate_buffer(200); + + /*FIXME: what is the best solution here to send the data. Right now, if buffer is not allocated, no data is sent!*/ + if (buffer) { + index += sprintf(buffer + index, "%s %d %s%s", httpv1_1, response->status_code, response->status_string, line_end); + for (;header;header = header->next) { + PRINTF("header %u \n", (uint16_t)header); + index += sprintf(buffer + index, "%s%s%s%s", header->name, header_delimiter, header->value, line_end); + } + index += sprintf(buffer + index, "%s", line_end); + + memcpy(buffer + index, response->payload, response->payload_len); + index += response->payload_len; + + PRINTF("Sending Data(size %d): %s \n", index, buffer); + + PSOCK_SEND(&(conn_state->sout), buffer, index); + } else { + PRINTF("BUFF ERROR: send_data!\n"); + } + + PSOCK_END(&(conn_state->sout)); +} + +static +PT_THREAD(handle_response(connection_state_t* conn_state)) +{ + PT_BEGIN(&(conn_state->outputpt)); + + PRINTF("handle_response ->\n"); + + http_set_res_header(&conn_state->response, HTTP_HEADER_NAME_CONNECTION, close, 0); + http_set_res_header(&conn_state->response, HTTP_HEADER_NAME_SERVER, contiki, 0); + + if (!(conn_state->response.status_string)) { + conn_state->response.status_string = + get_default_status_string(conn_state->response.status_code); + } + + PT_WAIT_THREAD(&(conn_state->outputpt), send_data(conn_state)); + + PRINTF("<-- handle_response\n\n\n"); + + PSOCK_CLOSE(&(conn_state->sout)); + + PT_END(&(conn_state->outputpt)); +} + +static void +handle_connection(connection_state_t* conn_state) +{ + if (conn_state->state == STATE_WAITING) { + handle_request(conn_state); + } + + if (conn_state->state == STATE_OUTPUT) { + handle_response(conn_state); + } +} + +PROCESS(http_server, "Httpd Process"); + +PROCESS_THREAD(http_server, ev, data) +{ + PROCESS_BEGIN(); + + /* if static routes are used rather than RPL */ +#if !UIP_CONF_IPV6_RPL + set_global_address(); + configure_routing(); +#endif /*!UIP_CONF_IPV6_RPL*/ + + #ifdef CONTIKI_TARGET_SKY + PRINTF("##RF CHANNEL : %d##\n",RF_CHANNEL); + #endif //CONTIKI_TARGET_SKY + + tcp_listen(HTONS(HTTP_PORT)); + + /* + * We loop for ever, accepting new connections. + */ + while(1) { + PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); + + connection_state_t *conn_state = (connection_state_t *)data; + + if(uip_connected()) { + PRINTF("##Connected##\n"); + + if(init_buffer(HTTP_DATA_BUFF_SIZE)) { + conn_state = (connection_state_t*)allocate_buffer(sizeof(connection_state_t)); + + if (conn_state) { + tcp_markconn(uip_conn, conn_state); + + /*initialize connection state*/ + init_connection(conn_state); + + /*-1 is needed to be able to null terminate the strings in the buffer, especially good for debugging (to have null terminated strings)*/ + PSOCK_INIT(&(conn_state->sin), (uint8_t*)conn_state->inputbuf, sizeof(conn_state->inputbuf) - 1); + PSOCK_INIT(&(conn_state->sout), (uint8_t*)conn_state->inputbuf, sizeof(conn_state->inputbuf) - 1); + PT_INIT(&(conn_state->outputpt)); + + handle_connection(conn_state); + } else { + PRINTF("Memory Alloc Error. Aborting!\n"); + uip_abort(); + } + } + } else if (uip_aborted() || uip_closed() || uip_timedout()) { + if (conn_state) { + delete_buffer(); + + /*Following 2 lines are needed since this part of code is somehow executed twice so it tries to free the same region twice. + Potential bug in uip*/ + conn_state = NULL; + tcp_markconn(uip_conn, conn_state); + } + } else { + handle_connection(conn_state); + } + } + + PROCESS_END(); +} diff --git a/apps/rest-http/http-server.h b/apps/rest-http/http-server.h new file mode 100755 index 000000000..de6029639 --- /dev/null +++ b/apps/rest-http/http-server.h @@ -0,0 +1,59 @@ +#ifndef HTTPSERVER_H_ +#define HTTPSERVER_H_ + +#include "http-common.h" +#include "rest.h" + +/*Declare process*/ +PROCESS_NAME(http_server); + +/*Type definition of the service callback*/ +typedef int (*service_callback) (http_request_t* request, http_response_t* response); + +/* + *Setter of the service callback, this callback will be called in case of HTTP request. + */ +void http_set_service_callback(service_callback callback); + +/* + * Setter for the status code (200, 201, etc) of the response. + */ +void http_set_status(http_response_t* response, status_code_t status); + +/* + * Adds the header name and value provided to the response. + * Name of the header should be hardcoded since it is accessed from code segment + * (not copied to buffer) whereas value of the header can be copied + * depending on the relevant parameter. This is needed since some values may be + * generated dynamically (ex: e-tag value) + */ +int http_set_res_header(http_response_t* response, const char* name, const char* value, int copy); + +/* + * Returns the value of the header name provided. Return NULL if header does not exist. + */ +const char* http_get_req_header(http_request_t* request, const char* name); + +int http_set_payload(http_response_t* response, uint8_t* payload, uint16_t size); + +/* + * Returns query variable in the URL. + * Returns true if the variable found, false otherwise. + * Variable is put in the buffer provided. + */ +int http_get_query_variable(http_request_t* request, const char *name, char* output, uint16_t output_size); + +/* + * Returns variable in the Post Data. + * Returns true if the variable found, false otherwise. + * Variable is put in the buffer provided. + */ +int http_get_post_variable(http_request_t* request, const char *name, char* output, uint16_t output_size); + +/* + * Get the header "Content-Type". + */ +const char* http_get_content_type_string(content_type_t content_type); +content_type_t http_get_header_content_type(http_request_t* request); + +#endif /*HTTPSERVER_H_*/