Initial commit of an experimental COAP/HTTP/REST implementation for Contiki
This commit is contained in:
parent
932fed8cc7
commit
e441bd4321
4
apps/rest-coap/Makefile.rest-coap
Executable file
4
apps/rest-coap/Makefile.rest-coap
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
rest-coap_src = coap-common.c coap-server.c
|
||||||
|
|
||||||
|
APPS += rest-common
|
||||||
|
include $(CONTIKI)/apps/rest-common/Makefile.rest-common
|
196
apps/rest-coap/coap-common.c
Normal file
196
apps/rest-coap/coap-common.c
Normal file
|
@ -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 <stdio.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "coap-common.h"
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
#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 ", ((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;
|
||||||
|
}
|
132
apps/rest-coap/coap-common.h
Normal file
132
apps/rest-coap/coap-common.h
Normal file
|
@ -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_ */
|
397
apps/rest-coap/coap-server.c
Normal file
397
apps/rest-coap/coap-server.c
Normal file
|
@ -0,0 +1,397 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h> /*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 <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 ", ((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();
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
39
apps/rest-coap/coap-server.h
Normal file
39
apps/rest-coap/coap-server.h
Normal file
|
@ -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_ */
|
1
apps/rest-common/Makefile.rest-common
Executable file
1
apps/rest-common/Makefile.rest-common
Executable file
|
@ -0,0 +1 @@
|
||||||
|
rest-common_src = rest.c rest-util.c buffer.c static-routing.c
|
75
apps/rest-common/buffer.c
Normal file
75
apps/rest-common/buffer.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* buffer.c
|
||||||
|
*
|
||||||
|
* Created on: Oct 19, 2010
|
||||||
|
* Author: dogan
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
17
apps/rest-common/buffer.h
Normal file
17
apps/rest-common/buffer.h
Normal file
|
@ -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_ */
|
73
apps/rest-common/rest-util.c
Normal file
73
apps/rest-common/rest-util.c
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* rest-util.c
|
||||||
|
*
|
||||||
|
* Created on: Oct 26, 2010
|
||||||
|
* Author: dogan
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h> /*for size_t*/
|
||||||
|
#include <ctype.h> /*for isxdigit*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*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;
|
||||||
|
}
|
14
apps/rest-common/rest-util.h
Normal file
14
apps/rest-common/rest-util.h
Normal file
|
@ -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_ */
|
201
apps/rest-common/rest.c
Executable file
201
apps/rest-common/rest.c
Executable file
|
@ -0,0 +1,201 @@
|
||||||
|
#include "contiki.h"
|
||||||
|
#include <string.h> /*for string operations in match_addresses*/
|
||||||
|
#include "rest.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
#if DEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#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*/
|
||||||
|
}
|
151
apps/rest-common/rest.h
Executable file
151
apps/rest-common/rest.h
Executable file
|
@ -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_*/
|
73
apps/rest-common/static-routing.c
Normal file
73
apps/rest-common/static-routing.c
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* static-routing.c
|
||||||
|
*
|
||||||
|
* Created on: Oct 12, 2010
|
||||||
|
* Author: dogan
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "static-routing.h"
|
||||||
|
|
||||||
|
#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 ", ((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*/
|
56
apps/rest-common/static-routing.h
Normal file
56
apps/rest-common/static-routing.h
Normal file
|
@ -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_ */
|
4
apps/rest-http/Makefile.rest-http
Executable file
4
apps/rest-http/Makefile.rest-http
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
rest-http_src = http-common.c http-server.c
|
||||||
|
|
||||||
|
APPS += rest-common
|
||||||
|
include $(CONTIKI)/apps/rest-common/Makefile.rest-common
|
29
apps/rest-http/http-common.c
Executable file
29
apps/rest-http/http-common.c
Executable file
|
@ -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 = ": ";
|
144
apps/rest-http/http-common.h
Executable file
144
apps/rest-http/http-common.h
Executable file
|
@ -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_*/
|
646
apps/rest-http/http-server.c
Executable file
646
apps/rest-http/http-server.c
Executable file
|
@ -0,0 +1,646 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> /*for atoi*/
|
||||||
|
#include <string.h>
|
||||||
|
#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 <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 ", ((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 <CR> 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();
|
||||||
|
}
|
59
apps/rest-http/http-server.h
Executable file
59
apps/rest-http/http-server.h
Executable file
|
@ -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_*/
|
Loading…
Reference in a new issue