Initial commit of an experimental COAP/HTTP/REST implementation for Contiki
This commit is contained in:
parent
932fed8cc7
commit
e441bd4321
19 changed files with 2311 additions and 0 deletions
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…
Add table
Add a link
Reference in a new issue