Contiki 3.x
http-server.c
1 #include <stdio.h>
2 #include <stdlib.h> /*for atoi*/
3 #include <string.h>
4 #include "contiki.h"
5 
6 #include "http-server.h"
7 #include "buffer.h"
8 #include "rest-util.h"
9 
10 #if !UIP_CONF_IPV6_RPL && !defined (CONTIKI_TARGET_MINIMAL_NET)
11 #include "static-routing.h"
12 #endif
13 
14 #define DEBUG 0
15 #if DEBUG
16 #include <stdio.h>
17 #define PRINTF(...) printf(__VA_ARGS__)
18 #define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
19 #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])
20 #else
21 #define PRINTF(...)
22 #define PRINT6ADDR(addr)
23 #define PRINTLLADDR(addr)
24 #endif
25 
26 static void
27 init_response(http_response_t* response)
28 {
29  response->status_code = OK_200;
30  response->status_string = NULL;
31  response->headers = NULL;
32  response->payload = NULL;
33  response->payload_len = 0;
34 }
35 
36 static void
37 init_request(http_request_t* request)
38 {
39  request->request_type = 0;
40  request->url = NULL;
41  request->url_len = 0;
42  request->query = NULL;
43  request->query_len = 0;
44  request->headers = NULL;
45  request->payload = NULL;
46  request->payload_len = 0;
47 }
48 
49 /**
50  * Initializes the connection state by clearing out the data structures
51  */
52 static void
53 init_connection(connection_state_t* conn_state)
54 {
55  conn_state->state = STATE_WAITING;
56 
57  init_request(&conn_state->request);
58  init_response(&conn_state->response);
59 }
60 
61 void
62 http_set_status(http_response_t* response, status_code_t status)
63 {
64  response->status_code = status;
65 }
66 
67 static http_header_t*
68 allocate_header(uint16_t variable_len)
69 {
70  PRINTF("sizeof http_header_t %u variable size %u\n", sizeof(http_header_t), variable_len);
71  uint8_t* buffer = allocate_buffer(sizeof(http_header_t) + variable_len);
72  if (buffer) {
73  http_header_t* option = (http_header_t*) buffer;
74  option->next = NULL;
75  option->name = NULL;
76  option->value = buffer + sizeof(http_header_t);
77  return option;
78  }
79 
80  return NULL;
81 }
82 
83 int
84 http_set_res_header(http_response_t* response, const char* name, const char* value, int copy)
85 {
86  PRINTF("http_set_res_header (copy:%d) %s:%s\n", copy, name, value);
87  uint16_t size = 0;
88  http_header_t* current_header = NULL;
89  http_header_t* head = NULL;
90 
91  if (copy) {
92  size += strlen(value) + 1;
93  }
94 
95  current_header = allocate_header(size);
96 
97  if (current_header) {
98  current_header->name = (char*)name;
99  if (copy) {
100  strcpy(current_header->value, value);
101  } else {
102  current_header->value = (char*)value;
103  }
104 
105  head = response->headers;
106  response->headers = current_header;
107  if (head) {
108  current_header->next = head;
109  }
110 
111  return 1;
112  }
113 
114  return 0;
115 }
116 
117 static const char* is_request_hdr_needed(const char* header_name)
118 {
119  const char* header = NULL;
120  /*FIXME add needed headers here*/
121  if (strcmp(header_name, HTTP_HEADER_NAME_CONTENT_LENGTH) == 0) {
122  header = HTTP_HEADER_NAME_CONTENT_LENGTH;
123  } else if (strcmp(header_name, HTTP_HEADER_NAME_CONTENT_TYPE) == 0) {
124  header = HTTP_HEADER_NAME_CONTENT_TYPE;
125  }
126 
127  return header;
128 }
129 
130 static service_callback service_cbk = NULL;
131 
132 void
133 http_set_service_callback(service_callback callback)
134 {
135  service_cbk = callback;
136 }
137 
138 const char* content_types[] = {
139  "text/plain",
140  "text/xml",
141  "text/csv",
142  "text/html",
143  "application/xml",
144  "application/exi",
145  "application/json",
146  "application/link-format",
147  "application/x-www-form-urlencoded",
148 };
149 
150 const char*
151 http_get_content_type_string(content_type_t content_type)
152 {
153  return content_types[content_type];
154 }
155 
156 char*
157 get_default_status_string(status_code_t status_code)
158 {
159  char* value = NULL;
160  switch(status_code) {
161  case 200:
162  value = "OK";
163  break;
164  case 201:
165  value = "Created";
166  break;
167  case 202:
168  value = "Accepted";
169  break;
170  case 204:
171  value = "No Content";
172  break;
173  case 304:
174  value = "Not Modified";
175  break;
176  case 400:
177  value = "Bad Request" ;
178  break;
179  case 404:
180  value = "Not Found" ;
181  break;
182  case 405:
183  value = "Method Not Allowed" ;
184  break;
185  case 406:
186  value = "Not Acceptable" ;
187  break;
188  case 414:
189  value = "Request-URI Too Long" ;
190  break;
191  case 415:
192  value = "Unsupported Media Type" ;
193  break;
194  case 500:
195  value = "Internal Server Error" ;
196  break;
197  case 501:
198  value = "Not Implemented" ;
199  break;
200  case 503:
201  value = "Service Unavailable" ;
202  break;
203  /*FIXME : will be removed later, put to catch the unhandled statuses.*/
204  default:
205  value = "$$BUG$$";
206  break;
207  }
208 
209  return value;
210 }
211 
212 int
213 http_get_query_variable(http_request_t* request, const char *name, char* output, uint16_t output_size)
214 {
215  if (request->query) {
216  return get_variable(name, request->query, request->query_len, output, output_size, 0);
217  }
218 
219  return 0;
220 }
221 
222 int
223 http_get_post_variable(http_request_t* request, const char *name, char* output, uint16_t output_size)
224 {
225  if (request->payload) {
226  return get_variable(name, request->payload, request->payload_len, output, output_size, 1);
227  }
228 
229  return 0;
230 }
231 
232 static int
233 is_method_handled(connection_state_t* conn_state, const char* method)
234 {
235  /*other method types can be added here if needed*/
236  if(strncmp(method, http_get_string, 3) == 0) {
237  conn_state->request.request_type = HTTP_METHOD_GET;
238  } else if (strncmp(method, http_post_string, 4) == 0) {
239  conn_state->request.request_type = HTTP_METHOD_POST;
240  } else if (strncmp(method, http_put_string, 3) == 0) {
241  conn_state->request.request_type = HTTP_METHOD_PUT;
242  } else if (strncmp(method, http_delete_string, 3) == 0) {
243  conn_state->request.request_type = HTTP_METHOD_DELETE;
244  } else {
245  PRINTF("No Method supported : %s\nstate : %d\n", conn_state->inputbuf, conn_state->state);
246  return 0;
247  }
248 
249  return 1;
250 }
251 
252 static int
253 parse_url(connection_state_t* conn_state, char* url)
254 {
255  int error = HTTP_NO_ERROR;
256  int full_url_path = 0;
257  /*even for default index.html there is / Ex: GET / HTTP/1.1*/
258  if (url[0] != '/') {
259  /*if url is complete (http://...) rather than relative*/
260  if (strncmp(url, http_string, 4) != 0 ) {
261  PRINTF("Url not valid : %s \n",url);
262  error = HTTP_URL_INVALID;
263  } else {
264  full_url_path = 1;
265  }
266  }
267 
268  if (error == HTTP_NO_ERROR) {
269  char* url_buffer = url;
270  if (full_url_path) {
271  unsigned char num_of_slash = 0;
272  do {
273  url_buffer = strchr( ++url_buffer, '/' );
274 
275  PRINTF("Buffer : %s %d\n", url_buffer, num_of_slash);
276 
277  } while (url_buffer && ++num_of_slash < 3);
278  }
279 
280  PRINTF("Url found :%s\n", url_buffer);
281 
282  /*Get rid of the first slash*/
283  if (url_buffer && ++url_buffer) {
284  conn_state->request.url = (char*) copy_text_to_buffer(url_buffer);
285  conn_state->request.url_len = strlen(url_buffer);
286 
287  if ((conn_state->request.query = strchr(conn_state->request.url, '?'))) {
288  *(conn_state->request.query++) = 0;
289  /*update url len - decrease the size of query*/
290  conn_state->request.url_len = strlen(conn_state->request.url);
291  conn_state->request.query_len = strlen(conn_state->request.query);
292  }
293 
294  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);
295 
296  /*FIXME url is not decoded - should be done here*/
297  } else {
298  error = HTTP_URL_INVALID;
299  }
300  }
301 
302  return error;
303 }
304 
305 static int
306 parse_header(connection_state_t* conn_state, char* inputbuf)
307 {
308  PRINTF("parse_header --->\n");
309  const char* header_name = NULL;
310 
311  char* delimiter = strchr(inputbuf, ':');
312  if (delimiter) {
313  *delimiter++ = 0; /*after increment delimiter will point space char*/
314 
315  header_name = is_request_hdr_needed(inputbuf);
316  if (header_name && delimiter) {
317  char* buffer = delimiter;
318 
319  if (buffer[0] == ' ') {
320  buffer++;
321  }
322 
323  http_header_t* current_header = NULL;
324  http_header_t* head = NULL;
325 
326  current_header = allocate_header(strlen(buffer));
327 
328  if (current_header) {
329  current_header->name = (char*)header_name;
330  strcpy(current_header->value, buffer);
331  }
332 
333  head = conn_state->request.headers;
334  conn_state->request.headers = current_header;
335  if (head) {
336  current_header->next = head;
337  }
338 
339  return 1;
340  }
341  }
342 
343  return 0;
344 }
345 
346 int
347 http_set_res_payload(http_response_t* response, uint8_t* payload, uint16_t size)
348 {
349  response->payload = copy_to_buffer(payload, size);
350  if (response->payload) {
351  response->payload_len = size;
352  return 1;
353  }
354 
355  return 0;
356 }
357 
358 static const char*
359 get_header(http_header_t* headers, const char* hdr_name)
360 {
361  for (;headers; headers = headers->next) {
362  if (strcmp(headers->name, hdr_name) == 0) {
363  return headers->value;
364  }
365  }
366 
367  return NULL;
368 }
369 
370 const char* http_get_req_header(http_request_t* request, const char* name)
371 {
372  return get_header(request->headers, name);
373 }
374 
375 content_type_t http_get_header_content_type(http_request_t* request)
376 {
377  const char* content_type_string = http_get_req_header(request, HTTP_HEADER_NAME_CONTENT_TYPE);
378  if (content_type_string) {
379  int i = 0;
380  for(; i < sizeof(content_types)/sizeof(const char*) ; i++) {
381  if (strcmp(content_types[i], content_type_string)) {
382  return (content_type_t)i;
383  }
384  }
385  }
386 
387  return UNKNOWN_CONTENT_TYPE;
388 }
389 
390 static
391 PT_THREAD(handle_request(connection_state_t* conn_state))
392 {
393  static int error;
394  const char* content_len;
395 
396  PSOCK_BEGIN(&(conn_state->sin));
397 
398  content_len = NULL;
399 
400  error = HTTP_NO_ERROR; /*always reinit static variables due to protothreads*/
401 
402  PRINTF("Request--->\n");
403 
404  //read method
405  PSOCK_READTO(&(conn_state->sin), ' ');
406 
407  if (!is_method_handled(conn_state, conn_state->inputbuf)) {
408  /*method not handled*/
409  http_set_status(&conn_state->response, SERVICE_UNAVAILABLE_503);
410  conn_state->state = STATE_OUTPUT;
411  } else {
412  /*read until the end of url*/
413  PSOCK_READTO(&(conn_state->sin), ' ');
414 
415  /*-1 is needed since it also includes space char*/
416  if (conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] != ' ' ) {
417  error = HTTP_URL_TOO_LONG;
418  }
419 
420  conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0;
421 
422  PRINTF("Read URL:%s\n", conn_state->inputbuf);
423 
424  if (error == HTTP_NO_ERROR) {
425  error = parse_url(conn_state, conn_state->inputbuf);
426  }
427 
428  if (error != HTTP_NO_ERROR) {
429  if (error == HTTP_URL_TOO_LONG) {
430  http_set_status(&conn_state->response, REQUEST_URI_TOO_LONG_414);
431  } else {
432  http_set_status(&conn_state->response, BAD_REQUEST_400);
433  }
434 
435  conn_state->state = STATE_OUTPUT;
436  } else {
437  /*read until the end of HTTP version - not used yet*/
438  PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR);
439 
440  PRINTF("After URL:%s\n", conn_state->inputbuf);
441 
442  /*FIXME : PSOCK_READTO takes just a single delimiter so I read till the end of line
443  but now it may not fit in the buffer. If PSOCK_READTO would take two delimiters,
444  we would have read until : and <CR> so it would not be blocked.*/
445 
446  /*Read the headers and store the necessary ones*/
447  do {
448  /*read the next line*/
449  PSOCK_READTO(&(conn_state->sin), LINE_FEED_CHAR);
450  conn_state->inputbuf[ PSOCK_DATALEN(&(conn_state->sin)) - 1] = 0;
451 
452  /*if headers finished then stop the infinite loop*/
453  if (conn_state->inputbuf[0] == CARRIAGE_RETURN_CHAR || conn_state->inputbuf[0] == 0) {
454  PRINTF("Finished Headers!\n\n");
455  break;
456  }
457 
458  parse_header(conn_state, conn_state->inputbuf);
459  }
460  while(1);
461 
462  content_len = get_header(conn_state->request.headers, HTTP_HEADER_NAME_CONTENT_LENGTH);
463  if (content_len) {
464  conn_state->request.payload_len = atoi(content_len);
465 
466  PRINTF("Post Data Size string: %s int: %d\n", content_len, conn_state->request.payload_len);
467  }
468 
469  if (conn_state->request.payload_len) {
470  static uint16_t read_bytes = 0;
471  /*init the static variable again*/
472  read_bytes = 0;
473 
474  conn_state->request.payload = allocate_buffer(conn_state->request.payload_len + 1);
475 
476  if (conn_state->request.payload) {
477  do {
478  PSOCK_READBUF(&(conn_state->sin));
479  /*null terminate the buffer in case it is a string.*/
480  conn_state->inputbuf[PSOCK_DATALEN(&(conn_state->sin))] = 0;
481 
482  memcpy(conn_state->request.payload + read_bytes, conn_state->inputbuf, PSOCK_DATALEN(&(conn_state->sin)));
483 
484  read_bytes += PSOCK_DATALEN(&(conn_state->sin));
485 
486  } while (read_bytes < conn_state->request.payload_len);
487 
488  conn_state->request.payload[read_bytes++] = 0;
489 
490  PRINTF("PostData => %s \n", conn_state->request.payload);
491  } else {
492  error = HTTP_MEMORY_ALLOC_ERR;
493  }
494  }
495 
496  if (error == HTTP_NO_ERROR) {
497  if (service_cbk) {
498  service_cbk(&conn_state->request, &conn_state->response);
499  }
500  } else {
501  PRINTF("Error:%d\n",error);
502  http_set_status(&conn_state->response, INTERNAL_SERVER_ERROR_500);
503  }
504 
505  conn_state->state = STATE_OUTPUT;
506  }
507  }
508 
509  PSOCK_END(&(conn_state->sin));
510 }
511 
512 static
513 PT_THREAD(send_data(connection_state_t* conn_state))
514 {
515  uint16_t index;
516  http_response_t* response;
517  http_header_t* header;
518  uint8_t* buffer;
519 
520  PSOCK_BEGIN(&(conn_state->sout));
521 
522  PRINTF("send_data -> \n");
523 
524  index = 0;
525  response = &conn_state->response;
526  header = response->headers;
527  buffer = allocate_buffer(200);
528 
529  /*FIXME: what is the best solution here to send the data. Right now, if buffer is not allocated, no data is sent!*/
530  if (buffer) {
531  index += sprintf(buffer + index, "%s %d %s%s", httpv1_1, response->status_code, response->status_string, line_end);
532  for (;header;header = header->next) {
533  PRINTF("header %u \n", (uint16_t)header);
534  index += sprintf(buffer + index, "%s%s%s%s", header->name, header_delimiter, header->value, line_end);
535  }
536  index += sprintf(buffer + index, "%s", line_end);
537 
538  memcpy(buffer + index, response->payload, response->payload_len);
539  index += response->payload_len;
540 
541  PRINTF("Sending Data(size %d): %s \n", index, buffer);
542 
543  PSOCK_SEND(&(conn_state->sout), buffer, index);
544  } else {
545  PRINTF("BUFF ERROR: send_data!\n");
546  }
547 
548  PSOCK_END(&(conn_state->sout));
549 }
550 
551 static
552 PT_THREAD(handle_response(connection_state_t* conn_state))
553 {
554  PT_BEGIN(&(conn_state->outputpt));
555 
556  PRINTF("handle_response ->\n");
557 
558  http_set_res_header(&conn_state->response, HTTP_HEADER_NAME_CONNECTION, close, 0);
559  http_set_res_header(&conn_state->response, HTTP_HEADER_NAME_SERVER, contiki, 0);
560 
561  if (!(conn_state->response.status_string)) {
562  conn_state->response.status_string =
563  get_default_status_string(conn_state->response.status_code);
564  }
565 
566  PT_WAIT_THREAD(&(conn_state->outputpt), send_data(conn_state));
567 
568  PRINTF("<-- handle_response\n\n\n");
569 
570  PSOCK_CLOSE(&(conn_state->sout));
571 
572  PT_END(&(conn_state->outputpt));
573 }
574 
575 static void
576 handle_connection(connection_state_t* conn_state)
577 {
578  if (conn_state->state == STATE_WAITING) {
579  handle_request(conn_state);
580  }
581 
582  if (conn_state->state == STATE_OUTPUT) {
583  handle_response(conn_state);
584  }
585 }
586 
587 PROCESS(http_server, "Httpd Process");
588 
589 PROCESS_THREAD(http_server, ev, data)
590 {
591  connection_state_t *conn_state;
592 
593  PROCESS_BEGIN();
594 
595  /* if static routes are used rather than RPL */
596 #if !UIP_CONF_IPV6_RPL && !defined (CONTIKI_TARGET_MINIMAL_NET)
597  set_global_address();
598  configure_routing();
599 #endif /*!UIP_CONF_IPV6_RPL*/
600 
601  #ifdef CONTIKI_TARGET_SKY
602  PRINTF("##RF CHANNEL : %d##\n",RF_CHANNEL);
603  #endif //CONTIKI_TARGET_SKY
604 
605  tcp_listen(uip_htons(HTTP_PORT));
606 
607  /*
608  * We loop for ever, accepting new connections.
609  */
610  while(1) {
612 
613  conn_state = (connection_state_t *)data;
614 
615  if(uip_connected()) {
616  PRINTF("##Connected##\n");
617 
618  if(init_buffer(HTTP_DATA_BUFF_SIZE)) {
619  conn_state = (connection_state_t*)allocate_buffer(sizeof(connection_state_t));
620 
621  if (conn_state) {
622  tcp_markconn(uip_conn, conn_state);
623 
624  /*initialize connection state*/
625  init_connection(conn_state);
626 
627  /*-1 is needed to be able to null terminate the strings in the buffer, especially good for debugging (to have null terminated strings)*/
628  PSOCK_INIT(&(conn_state->sin), (uint8_t*)conn_state->inputbuf, sizeof(conn_state->inputbuf) - 1);
629  PSOCK_INIT(&(conn_state->sout), (uint8_t*)conn_state->inputbuf, sizeof(conn_state->inputbuf) - 1);
630  PT_INIT(&(conn_state->outputpt));
631 
632  handle_connection(conn_state);
633  } else {
634  PRINTF("Memory Alloc Error. Aborting!\n");
635  uip_abort();
636  }
637  }
638  } else if (uip_aborted() || uip_closed() || uip_timedout()) {
639  if (conn_state) {
640  delete_buffer();
641 
642  /*Following 2 lines are needed since this part of code is somehow executed twice so it tries to free the same region twice.
643  Potential bug in uip*/
644  conn_state = NULL;
645  tcp_markconn(uip_conn, conn_state);
646  }
647  } else {
648  handle_connection(conn_state);
649  }
650  }
651 
652  PROCESS_END();
653 }
#define PT_WAIT_THREAD(pt, thread)
Block and wait until a child protothread completes.
Definition: pt.h:191
#define PSOCK_READTO(psock, c)
Read data up to a specified character.
Definition: psock.h:291
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define PSOCK_BEGIN(psock)
Start the protosocket protothread in a function.
Definition: psock.h:164
Representation of a uIP TCP connection.
Definition: uip.h:1336
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
#define PSOCK_READBUF(psock)
Read data until the buffer is full.
Definition: psock.h:256
#define NULL
The null pointer.
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
#define PSOCK_INIT(psock, buffer, buffersize)
Initialize a protosocket.
Definition: psock.h:150
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2298
#define PSOCK_CLOSE(psock)
Close a protosocket.
Definition: psock.h:241
#define PSOCK_SEND(psock, data, datalen)
Send data.
Definition: psock.h:184
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
#define PSOCK_END(psock)
Declare the end of a protosocket&#39;s protothread.
Definition: psock.h:348
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
CCIF void tcp_listen(uint16_t port)
Open a TCP port.
#define PSOCK_DATALEN(psock)
The length of the data that was previously read.
Definition: psock.h:304
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771