45 #include "contiki-net.h"
50 #define PRINTF(...) printf(__VA_ARGS__)
55 #ifndef WEBSERVER_CONF_CFS_CONNS
56 #define CONNS UIP_CONNS
58 #define CONNS WEBSERVER_CONF_CFS_CONNS
61 #ifndef WEBSERVER_CONF_CFS_URLCONV
64 #define URLCONV WEBSERVER_CONF_CFS_URLCONV
71 static struct httpd_ws_state conns[CONNS];
73 PROCESS(httpd_ws_process,
"Web server (WS)");
76 #define ISO_space 0x20
77 #define ISO_period 0x2e
78 #define ISO_slash 0x2f
80 uint16_t http_connections = 0;
82 static const char http_10[] =
" HTTP/1.0\r\n";
83 static const char http_content_type[] =
"Content-Type:";
84 static const char http_content_type_html[] =
"text/html";
85 static const char http_content_len[] =
"Content-Length:";
86 static const char http_header_404[] =
87 "HTTP/1.0 404 Not found\r\nServer: Contiki\r\nConnection: close\r\n";
88 static const char http_header_200[] =
89 "HTTP/1.0 200 OK\r\nServer: Contiki\r\nConnection: close\r\n";
90 static const char html_not_found[] =
91 "<html><body><h1>Page not found</h1></body></html>";
95 httpd_state_init(
void)
99 for(i = 0; i < CONNS; i++) {
100 conns[i].state = HTTPD_WS_STATE_UNUSED;
104 static struct httpd_ws_state *
105 httpd_state_alloc(
void)
109 for(i = 0; i < CONNS; i++) {
110 if(conns[i].state == HTTPD_WS_STATE_UNUSED) {
111 conns[i].state = HTTPD_WS_STATE_INPUT;
118 #define httpd_state_free(s) (s->state = HTTPD_WS_STATE_UNUSED)
121 PT_THREAD(send_string(
struct httpd_ws_state *s,
const char *str, uint16_t len))
125 SEND_STRING(&s->sout, str, len);
131 PT_THREAD(send_headers(
struct httpd_ws_state *s,
const char *statushdr))
135 SEND_STRING(&s->sout, statushdr, strlen(statushdr));
136 s->outbuf_pos = snprintf(s->outbuf,
sizeof(s->outbuf),
137 "%s %s\r\n\r\n", http_content_type,
138 s->content_type ==
NULL
139 ? http_content_type_html : s->content_type);
140 SEND_STRING(&s->sout, s->outbuf, s->outbuf_pos);
147 PT_THREAD(handle_output(
struct httpd_ws_state *s))
151 s->content_type = http_content_type_html;
152 s->script = httpd_ws_get_script(s);
153 if(s->script ==
NULL) {
156 send_string(s, html_not_found, strlen(html_not_found)));
161 if(s->request_type == HTTPD_WS_POST) {
163 s->state = HTTPD_WS_STATE_INPUT;
164 PT_WAIT_UNTIL(&s->outputpt, s->state == HTTPD_WS_STATE_OUTPUT);
175 PT_THREAD(handle_request(
struct httpd_ws_state *s))
181 send_string(s, s->filename, strlen(s->filename)));
183 if(s->outbuf_pos > 0) {
184 PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos));
187 if(s->content_type !=
NULL) {
188 s->outbuf_pos = snprintf(s->outbuf,
sizeof(s->outbuf),
"%s %s\r\n",
189 http_content_type, s->content_type);
190 PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos));
193 if(s->output_extra_headers !=
NULL) {
194 s->response_index = 0;
195 while((s->outbuf_pos =
196 s->output_extra_headers(s,
197 s->outbuf,
sizeof(s->outbuf),
198 s->response_index)) > 0) {
199 PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos));
205 if(s->content_len > 0) {
206 s->outbuf_pos = snprintf(s->outbuf,
sizeof(s->outbuf),
"%s %u\r\n",
207 http_content_len, s->content_len);
210 if(s->outbuf_pos + 2 <
sizeof(s->outbuf)) {
211 s->outbuf[s->outbuf_pos++] =
'\r';
212 s->outbuf[s->outbuf_pos++] =
'\n';
214 PT_WAIT_THREAD(&s->outputpt, send_string(s, s->outbuf, s->outbuf_pos));
217 if(s->script !=
NULL) {
220 s->state = HTTPD_WS_STATE_REQUEST_INPUT;
227 PT_THREAD(handle_input(
struct httpd_ws_state *s))
232 if(strncmp(s->inputbuf,
"GET ", 4) == 0) {
233 s->request_type = HTTPD_WS_GET;
234 }
else if(strncmp(s->inputbuf,
"POST ", 5) == 0) {
235 s->request_type = HTTPD_WS_POST;
237 }
else if(strncmp(s->inputbuf,
"HTTP ", 5) == 0) {
238 s->request_type = HTTPD_WS_RESPONSE;
246 if(s->inputbuf[0] != ISO_slash) {
252 urlconv_tofilename(s->filename, s->inputbuf,
sizeof(s->filename));
255 snprintf(s->filename,
sizeof(s->filename),
"%s", s->inputbuf);
259 s->state = HTTPD_WS_STATE_OUTPUT;
264 if(s->request_type == HTTPD_WS_POST &&
265 strncmp(s->inputbuf, http_content_len, 15) == 0) {
267 s->content_len = atoi(&s->inputbuf[16]);
274 }
else if(s->request_type == HTTPD_WS_POST) {
278 s->state = HTTPD_WS_STATE_OUTPUT;
285 handle_connection(
struct httpd_ws_state *s)
287 if(s->state == HTTPD_WS_STATE_REQUEST_OUTPUT) {
291 if(s->state == HTTPD_WS_STATE_OUTPUT) {
297 httpd_ws_appcall(
void *state)
299 struct httpd_ws_state *s = (
struct httpd_ws_state *)state;
303 PRINTF(
"HTTPD-WS: closed/aborted (%d)\n", http_connections);
307 PRINTF(
"HTTPD-WS: closed/aborted ** NO HTTPD_WS_STATE!!! ** (%d)\n",
312 s = httpd_state_alloc();
315 PRINTF(
"HTTPD-WS: aborting - no resource (%d)\n", http_connections);
322 s->state = HTTPD_WS_STATE_INPUT;
325 s->state = HTTPD_WS_STATE_REQUEST_OUTPUT;
327 PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf,
sizeof(s->inputbuf) - 1);
328 PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf,
sizeof(s->inputbuf) - 1);
331 handle_connection(s);
332 }
else if(s !=
NULL) {
336 PRINTF(
"HTTPD-WS: aborting - http timeout (%d)\n", http_connections);
341 PRINTF(
"HTTPD-WS: uip-poll (%d)\n", http_connections);
348 handle_connection(s);
350 PRINTF(
"HTTPD-WS: aborting - no state (%d)\n", http_connections);
365 struct httpd_ws_state *
366 httpd_ws_request(
char request_type,
const char *host_ip,
const char *host_hdr,
367 uint16_t port,
const char *file,
368 const char *content_type, uint16_t content_len,
369 httpd_ws_script_t generator)
371 struct httpd_ws_state *s;
373 uip_ipaddr_t *ipaddr;
389 s = httpd_state_alloc();
396 switch(request_type) {
398 request_str =
"POST ";
401 request_str =
"PUT ";
404 request_str =
"GET ";
408 s->request_type = request_type;
409 s->content_len = content_len;
410 s->content_type = content_type;
411 s->script = generator;
412 s->state = HTTPD_WS_STATE_REQUEST_OUTPUT;
416 snprintf(s->filename,
sizeof(s->filename),
"%s%s%s",
417 request_str, file, http_10);
418 s->outbuf_pos = snprintf(s->outbuf,
sizeof(s->outbuf),
"Host:%s\r\n",
419 host_hdr !=
NULL ? host_hdr : host_ip);
425 PRINTF(
"HTTPD-WS: aborting... could not allocate tcp connection (%d)\n",
431 PRINTF(
"HTTPD-WS: created http connection (%d)\n", http_connections);
445 PRINTF(
"Buffer size, input %d, output\n",
446 HTTPD_INBUF_SIZE, HTTPD_OUTBUF_SIZE);
457 httpd_ws_appcall(data);
459 PRINTF(
"HTTPD States: ");
460 for(i = 0; i < CONNS; i++) {
461 PRINTF(
"%d ", conns[i].state);
462 if(conns[i].state != HTTPD_WS_STATE_UNUSED &&
464 conns[i].state = HTTPD_WS_STATE_UNUSED;
465 PRINTF(
"\n*** RELEASED HTTPD Session\n");
A simple webserver for web services
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
#define PT_WAIT_THREAD(pt, thread)
Block and wait until a child protothread completes.
Hostname is fresh and usable.
#define PSOCK_READTO(psock, c)
Read data up to a specified character.
#define PSOCK_CLOSE_EXIT(psock)
Close a protosocket and exit the protosocket's protothread.
void timer_restart(struct timer *t)
Restart the timer from the current point in time.
#define PROCESS_BEGIN()
Define the beginning of a process.
#define PSOCK_BEGIN(psock)
Start the protosocket protothread in a function.
Representation of a uIP TCP connection.
CCIF struct uip_conn * tcp_connect(uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
#define uip_aborted()
Has the connection been aborted by the other end?
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
#define NULL
The null pointer.
#define PT_INIT(pt)
Initialize a protothread.
#define uip_poll()
Is the connection being polled by uIP?
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
#define uiplib_ipaddrconv
Convert a textual representation of an IP address to a numerical representation.
#define PT_THREAD(name_args)
Declaration of a protothread.
#define uip_connected()
Has the connection just been connected?
#define PT_WAIT_UNTIL(pt, condition)
Block and wait until condition is true.
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
#define PROCESS_END()
Define the end of a process.
#define uip_abort()
Abort the current connection.
#define PSOCK_INIT(psock, buffer, buffersize)
Initialize a protosocket.
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
#define PSOCK_CLOSE(psock)
Close a protosocket.
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
#define PSOCK_END(psock)
Declare the end of a protosocket's protothread.
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
#define PSOCK_READBUF_LEN(psock, len)
Read data until at least len bytes have been read.
#define PROCESS(name, strname)
Declare a process.
#define uip_close()
Close the current connection.
void etimer_reset(struct etimer *et)
Reset an event timer with the same interval as was previously set.
process_event_t tcpip_event
The uIP event.
#define uip_timedout()
Has the connection timed out?
#define PT_END(pt)
Declare the end of a protothread.
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
CCIF void tcp_listen(uint16_t port)
Open a TCP port.
#define PSOCK_DATALEN(psock)
The length of the data that was previously read.
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
#define uip_closed()
Has the connection been closed by the other end?
int timer_expired(struct timer *t)
Check if a timer has expired.
#define PT_EXIT(pt)
Exit the protothread.
#define CLOCK_SECOND
A second, measured in system clock time.
#define PROCESS_CONTEXT_END(p)
End a context switch.