1 #pragma GCC diagnostic ignored "-Wredundant-decls"
2 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
3 #pragma GCC diagnostic ignored "-Wcast-align"
4 #pragma GCC diagnostic ignored "-Wcast-qual"
5 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
13 * \defgroup httpd Web server
15 * The uIP web server is a very simplistic implementation of an HTTP
16 * server. It can serve web pages and files from a read-only ROM
17 * filesystem, and provides a very small scripting language.
25 * Adam Dunkels <adam@sics.se>
30 * Copyright (c) 2004, Adam Dunkels.
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. Neither the name of the Institute nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * This file is part of the uIP TCP/IP stack.
59 * Author: Adam Dunkels <adam@sics.se>
61 * $Id: httpd.c,v 1.2 2006/06/11 21:46:38 adam Exp $
69 #include "http-strings.h"
75 #include "CommandQueue.h"
76 #include "CallbackStream.h"
80 #define STATE_WAITING 0
81 #define STATE_HEADERS 1
83 #define STATE_OUTPUT 3
84 #define STATE_UPLOAD 4
91 #define ISO_space 0x20
93 #define ISO_percent 0x25
94 #define ISO_period 0x2e
95 #define ISO_slash 0x2f
96 #define ISO_colon 0x3a
98 //#define DEBUG_PRINTF printf
99 #define DEBUG_PRINTF(...)
101 extern const char *get_query_string();
103 // this callback gets the results of a command, line by line. need to check if
104 // we need to stall the upstream sender return 0 if stalled 1 if ok to keep
105 // providing more -1 if the connection has closed or is not in output state.
106 // need to see which connection to send to based on state and add result to
107 // that fifo for each connection. NOTE this will not get called if the
108 // connection has been closed and the stream will get deleted when the last
109 // command has been executed
110 static int command_result(const char *str
, void *state
)
112 struct httpd_state
*s
= (struct httpd_state
*)state
;
114 // connection was closed so discard, this should never happen
115 DEBUG_PRINTF("ERROR: command result for closed state %d\n", (int)state
);
120 DEBUG_PRINTF("End of command (%p)\n", state
);
121 fifo_push(s
->fifo
, NULL
);
124 if (fifo_size(s
->fifo
) < 10) {
125 DEBUG_PRINTF("Got command result (%p): %s", state
, str
);
126 fifo_push(s
->fifo
, strdup(str
));
129 DEBUG_PRINTF("command result fifo is full (%p)\n", state
);
136 static void create_callback_stream(struct httpd_state
*s
)
138 // need to create a callback stream here, but do one per connection pass
139 // the state to the callback, also create the fifo for the command results
140 s
->fifo
= new_fifo();
141 s
->pstream
= new_callback_stream(command_result
, s
);
144 // Used to save files to SDCARD during upload
146 static char *output_filename
= NULL
;
147 static int file_cnt
= 0;
148 static int open_file(const char *fn
)
150 if (output_filename
!= NULL
) free(output_filename
);
151 output_filename
= malloc(strlen(fn
) + 5);
152 strcpy(output_filename
, "/sd/");
153 strcat(output_filename
, fn
);
154 fd
= fopen(output_filename
, "w");
156 free(output_filename
);
157 output_filename
= NULL
;
163 static int close_file()
165 free(output_filename
);
166 output_filename
= NULL
;
171 static int save_file(uint8_t *buf
, unsigned int len
)
173 if (fwrite(buf
, 1, len
, fd
) == len
) {
183 static int fs_open(struct httpd_state
*s
)
185 if (strncmp(s
->filename
, "/sd/", 4) == 0) {
186 DEBUG_PRINTF("Opening file %s\n", s
->filename
);
187 s
->fd
= fopen(s
->filename
, "r");
189 DEBUG_PRINTF("Failed to open: %s\n", s
->filename
);
196 return httpd_fs_open(s
->filename
, &s
->file
);
200 /*---------------------------------------------------------------------------*/
201 static PT_THREAD(send_command_response(struct httpd_state
*s
))
203 PSOCK_BEGIN(&s
->sout
);
206 PSOCK_WAIT_UNTIL( &s
->sout
, fifo_size(s
->fifo
) > 0 );
207 s
->strbuf
= fifo_pop(s
->fifo
);
208 if (s
->strbuf
!= NULL
) {
210 DEBUG_PRINTF("Sending response: %s", s
->strbuf
);
211 // TODO send as much as we can in one packet
212 PSOCK_SEND_STR(&s
->sout
, s
->strbuf
);
215 }else if(--s
->command_count
<= 0) {
216 // when all commands have completed exit
224 /*---------------------------------------------------------------------------*/
225 static unsigned short generate_part_of_file(void *state
)
227 struct httpd_state
*s
= (struct httpd_state
*)state
;
229 if (s
->file
.len
> uip_mss()) {
232 s
->len
= s
->file
.len
;
234 memcpy(uip_appdata
, s
->file
.data
, s
->len
);
238 /*---------------------------------------------------------------------------*/
239 static unsigned short generate_part_of_sd_file(void *state
)
241 struct httpd_state
*s
= (struct httpd_state
*)state
;
243 int len
= fread(uip_appdata
, 1, uip_mss(), s
->fd
);
245 // we need to send something
246 strcpy(uip_appdata
, "\r\n");
254 /*---------------------------------------------------------------------------*/
256 PT_THREAD(send_file(struct httpd_state
*s
))
258 PSOCK_BEGIN(&s
->sout
);
261 PSOCK_GENERATOR_SEND(&s
->sout
, generate_part_of_file
, s
);
262 s
->file
.len
-= s
->len
;
263 s
->file
.data
+= s
->len
;
264 } while (s
->file
.len
> 0);
269 /*---------------------------------------------------------------------------*/
270 static PT_THREAD(send_sd_file(struct httpd_state
*s
))
272 PSOCK_BEGIN(&s
->sout
);
275 PSOCK_GENERATOR_SEND(&s
->sout
, generate_part_of_sd_file
, s
);
276 } while (s
->len
> 0);
284 /*---------------------------------------------------------------------------*/
285 static PT_THREAD(send_headers_3(struct httpd_state
*s
, const char *statushdr
, char send_content_type
))
289 PSOCK_BEGIN(&s
->sout
);
291 PSOCK_SEND_STR(&s
->sout
, statushdr
);
292 PSOCK_SEND_STR(&s
->sout
, http_header_all
);
294 if (send_content_type
) {
295 ptr
= strrchr(s
->filename
, ISO_period
);
297 PSOCK_SEND_STR(&s
->sout
, http_content_type_plain
); // http_content_type_binary);
298 } else if (strncmp(http_html
, ptr
, 5) == 0) {
299 PSOCK_SEND_STR(&s
->sout
, http_content_type_html
);
300 } else if (strncmp(http_css
, ptr
, 4) == 0) {
301 PSOCK_SEND_STR(&s
->sout
, http_content_type_css
);
302 } else if (strncmp(http_png
, ptr
, 4) == 0) {
303 PSOCK_SEND_STR(&s
->sout
, http_content_type_png
);
304 } else if (strncmp(http_gif
, ptr
, 4) == 0) {
305 PSOCK_SEND_STR(&s
->sout
, http_content_type_gif
);
306 } else if (strncmp(http_jpg
, ptr
, 4) == 0) {
307 PSOCK_SEND_STR(&s
->sout
, http_content_type_jpg
);
309 PSOCK_SEND_STR(&s
->sout
, http_content_type_plain
);
314 static PT_THREAD(send_headers(struct httpd_state
*s
, const char *statushdr
))
316 return send_headers_3(s
, statushdr
, 1);
318 /*---------------------------------------------------------------------------*/
320 PT_THREAD(handle_output(struct httpd_state
*s
))
323 PT_BEGIN(&s
->outputpt
);
325 if (s
->method
== OPTIONS
) {
326 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_preflight
));
327 PSOCK_SEND_STR(&s
->sout
, "OK\r\n");
329 else if (s
->method
== POST
) {
330 if (strcmp(s
->filename
, "/command") == 0) {
331 DEBUG_PRINTF("Executed command post\n");
332 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_200
));
333 // send response as we get it
334 PT_WAIT_THREAD(&s
->outputpt
, send_command_response(s
));
336 } else if (strcmp(s
->filename
, "/command_silent") == 0) {
337 DEBUG_PRINTF("Executed silent command post\n");
338 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_200
));
340 } else if (strcmp(s
->filename
, "/upload") == 0) {
341 DEBUG_PRINTF("upload output: %d\n", s
->uploadok
);
342 if (s
->uploadok
== 0) {
343 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_503
));
344 PSOCK_SEND_STR(&s
->sout
, "FAILED\r\n");
346 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_200
));
347 PSOCK_SEND_STR(&s
->sout
, "OK\r\n");
351 DEBUG_PRINTF("Unknown POST: %s\n", s
->filename
);
352 httpd_fs_open(http_404_html
, &s
->file
);
353 strcpy(s
->filename
, http_404_html
);
354 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_404
));
355 PT_WAIT_THREAD(&s
->outputpt
, send_file(s
));
359 // Presume method GET
361 if (strcmp(s
->filename
, "/query") == 0) { // query short cut
362 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_200
));
363 strncpy(qstr
, get_query_string(), sizeof(qstr
) - 1);
364 PSOCK_SEND_STR(&s
->sout
, qstr
);
366 } else if (!fs_open(s
)) { // Note this has the side effect of opening the file
367 DEBUG_PRINTF("404 file not found\n");
368 httpd_fs_open(http_404_html
, &s
->file
);
369 strcpy(s
->filename
, http_404_html
);
370 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_404
));
371 PT_WAIT_THREAD(&s
->outputpt
, send_file(s
));
373 } else if (s
->cache_page
) {
375 // if it was an sd file then we need to close it
379 // tell it it has not changed
380 DEBUG_PRINTF("304 Not Modified\n");
381 PT_WAIT_THREAD(&s
->outputpt
, send_headers_3(s
, http_header_304
, 0));
384 DEBUG_PRINTF("sending file %s\n", s
->filename
);
385 PT_WAIT_THREAD(&s
->outputpt
, send_headers(s
, http_header_200
));
388 PT_WAIT_THREAD(&s
->outputpt
, send_sd_file(s
));
392 PT_WAIT_THREAD(&s
->outputpt
, send_file(s
));
397 PSOCK_CLOSE(&s
->sout
);
398 PT_END(&s
->outputpt
);
401 /*---------------------------------------------------------------------------*/
402 // this forces us to yield every other call as we read all data everytime
403 static char has_newdata(struct httpd_state
*s
)
405 if (s
->upload_state
== 1) {
406 /* All data in uip_appdata buffer already consumed. */
409 } else if (uip_newdata()) {
410 /* There is new data that has not been consumed. */
413 /* There is no new data. */
419 * handle the uploaded data, as there may be part of that buffer still in the last packet buffer
420 * write that first from the buf/len parameters
422 static PT_THREAD(handle_uploaded_data(struct httpd_state
*s
, uint8_t *buf
, int len
))
424 PT_BEGIN(&s
->inputpt
);
426 DEBUG_PRINTF("Uploading file: %s, %d\n", s
->upload_name
, s
->content_length
);
428 // The body is the raw data to be stored to the file
429 if (!open_file(s
->upload_name
)) {
430 DEBUG_PRINTF("failed to open file\n");
432 PT_EXIT(&s
->inputpt
);
435 DEBUG_PRINTF("opened file: %s\n", s
->upload_name
);
438 // write the first part of the buffer
439 if (!save_file(buf
, len
)) {
440 DEBUG_PRINTF("initial write failed\n");
442 PT_EXIT(&s
->inputpt
);
444 s
->content_length
-= len
;
447 s
->upload_state
= 1; // first time through we need to yield to get new data
449 // save the entire input buffer
450 while (s
->content_length
> 0) {
451 PT_WAIT_UNTIL(&s
->inputpt
, has_newdata(s
));
454 u8_t
*readptr
= (u8_t
*)uip_appdata
;
455 int readlen
= uip_datalen();
456 //DEBUG_PRINTF("read %d bytes of data\n", readlen);
459 if (!save_file(readptr
, readlen
)) {
460 DEBUG_PRINTF("write failed\n");
462 PT_EXIT(&s
->inputpt
);
464 s
->content_length
-= readlen
;
470 DEBUG_PRINTF("finished upload\n");
474 /*---------------------------------------------------------------------------*/
476 PT_THREAD(handle_input(struct httpd_state
*s
))
478 PSOCK_BEGIN(&s
->sin
);
480 PSOCK_READTO(&s
->sin
, ISO_space
);
482 if (strncmp(s
->inputbuf
, http_get
, 3) == 0) {
484 } else if (strncmp(s
->inputbuf
, http_post
, 4) == 0) {
486 } else if (strncmp(s
->inputbuf
, http_options
, 7) == 0) {
489 DEBUG_PRINTF("Unexpected method: %s\n", s
->inputbuf
);
490 PSOCK_CLOSE_EXIT(&s
->sin
);
493 DEBUG_PRINTF("Method: %s\n", s
->method
== POST
? "POST" : (s
->method
== GET
? "GET" : "OPTIONS"));
495 PSOCK_READTO(&s
->sin
, ISO_space
);
497 if (s
->inputbuf
[0] != ISO_slash
) {
498 PSOCK_CLOSE_EXIT(&s
->sin
);
501 if (s
->inputbuf
[1] == ISO_space
) {
502 strncpy(s
->filename
, http_index_html
, sizeof(s
->filename
));
504 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 1] = 0;
505 strncpy(s
->filename
, &s
->inputbuf
[0], sizeof(s
->filename
));
508 DEBUG_PRINTF("filename: %s\n", s
->filename
);
510 /* httpd_log_file(uip_conn->ripaddr, s->filename);*/
512 s
->state
= STATE_HEADERS
;
513 s
->content_length
= 0;
516 if (s
->state
== STATE_HEADERS
) {
517 // read the headers of the request
518 PSOCK_READTO(&s
->sin
, ISO_nl
);
519 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 1] = 0;
520 if (s
->inputbuf
[0] == '\r') {
521 DEBUG_PRINTF("end of headers\n");
522 if (s
->method
== OPTIONS
) {
523 s
->state
= STATE_OUTPUT
;
525 } else if (s
->method
== GET
) {
526 s
->state
= STATE_OUTPUT
;
528 } else if (s
->method
== POST
) {
529 if (strcmp(s
->filename
, "/upload") == 0) {
530 s
->state
= STATE_UPLOAD
;
532 s
->state
= STATE_BODY
;
536 DEBUG_PRINTF("reading header: %s\n", s
->inputbuf
);
537 // handle headers here
538 if (strncmp(s
->inputbuf
, http_content_length
, sizeof(http_content_length
) - 1) == 0) {
539 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 2] = 0;
540 s
->content_length
= atoi(&s
->inputbuf
[sizeof(http_content_length
) - 1]);
541 DEBUG_PRINTF("Content length= %s, %d\n", &s
->inputbuf
[sizeof(http_content_length
) - 1], s
->content_length
);
543 } else if (strncmp(s
->inputbuf
, "X-Filename: ", 11) == 0) {
544 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 2] = 0;
545 strncpy(s
->upload_name
, &s
->inputbuf
[12], sizeof(s
->upload_name
) - 1);
546 DEBUG_PRINTF("Upload name= %s\n", s
->upload_name
);
548 } else if (strncmp(s
->inputbuf
, http_cache_control
, sizeof(http_cache_control
) - 1) == 0) {
549 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 2] = 0;
550 s
->cache_page
= strncmp(http_no_cache
, &s
->inputbuf
[sizeof(http_cache_control
) - 1], sizeof(http_no_cache
) - 1) != 0;
551 DEBUG_PRINTF("cache page= %d\n", s
->cache_page
);
555 } else if (s
->state
== STATE_BODY
) {
556 if (s
->method
== POST
&& strcmp(s
->filename
, "/command") == 0) {
557 // create a callback stream and fifo for the results as it is a command
558 create_callback_stream(s
);
560 } else if (s
->method
== POST
&& strcmp(s
->filename
, "/command_silent") == 0) {
561 // stick the command on the command queue specifying null output stream
564 } else { // unknown POST
565 DEBUG_PRINTF("Unknown Post URL: %s\n", s
->filename
);
566 s
->state
= STATE_OUTPUT
;
570 // read the Body of the request, each line is a command
571 if (s
->content_length
> 0) {
572 DEBUG_PRINTF("start reading body %d...\n", s
->content_length
);
573 while (s
->content_length
> 2) {
574 PSOCK_READTO(&s
->sin
, ISO_nl
);
575 s
->inputbuf
[PSOCK_DATALEN(&s
->sin
) - 1] = 0;
576 s
->content_length
-= PSOCK_DATALEN(&s
->sin
);
577 // stick the command on the command queue, with this connections stream output
578 DEBUG_PRINTF("Adding command: %s, left: %d\n", s
->inputbuf
, s
->content_length
);
579 network_add_command(s
->inputbuf
, s
->pstream
);
580 s
->command_count
++; // count number of command lines we submit
582 DEBUG_PRINTF("Read body done\n");
583 s
->state
= STATE_OUTPUT
;
586 s
->state
= STATE_OUTPUT
;
590 } else if (s
->state
== STATE_UPLOAD
) {
591 PSOCK_WAIT_THREAD(&s
->sin
, handle_uploaded_data(s
, PSOCK_GET_START_OF_REST_OF_BUFFER(&s
->sin
), PSOCK_GET_LENGTH_OF_REST_OF_BUFFER(&s
->sin
)));
592 PSOCK_MARK_BUFFER_READ(&s
->sin
);
593 s
->state
= STATE_OUTPUT
;
597 DEBUG_PRINTF("WTF State: %d", s
->state
);
604 /*---------------------------------------------------------------------------*/
606 handle_connection(struct httpd_state
*s
)
608 if (s
->state
!= STATE_OUTPUT
) {
611 if (s
->state
== STATE_OUTPUT
) {
615 /*---------------------------------------------------------------------------*/
619 struct httpd_state
*s
= (struct httpd_state
*)(uip_conn
->appstate
);
621 if (uip_connected()) {
622 s
= malloc(sizeof(struct httpd_state
));
624 DEBUG_PRINTF("Connection: Out of memory\n");
628 uip_conn
->appstate
= s
;
629 DEBUG_PRINTF("Connection: %d.%d.%d.%d:%d\n",
630 uip_ipaddr1(uip_conn
->ripaddr
), uip_ipaddr2(uip_conn
->ripaddr
),
631 uip_ipaddr3(uip_conn
->ripaddr
), uip_ipaddr4(uip_conn
->ripaddr
),
632 HTONS(uip_conn
->rport
));
634 PSOCK_INIT(&s
->sin
, s
->inputbuf
, sizeof(s
->inputbuf
) - 1);
635 PSOCK_INIT(&s
->sout
, s
->inputbuf
, sizeof(s
->inputbuf
) - 1);
636 PT_INIT(&s
->outputpt
);
637 PT_INIT(&s
->inputpt
);
638 s
->state
= STATE_WAITING
;
639 /* timer_set(&s->timer, CLOCK_SECOND * 100);*/
648 DEBUG_PRINTF("ERROR no state context: %d\n", uip_flags
);
653 // check for timeout on connection here so we can cleanup if we abort
656 if (s
->timer
>= 20 * 2) { // we have a 0.5 second poll and we want 20 second timeout
657 DEBUG_PRINTF("Timer expired, aborting\n");
664 if (uip_closed() || uip_aborted() || uip_timedout()) {
665 DEBUG_PRINTF("Closing connection: %d\n", HTONS(uip_conn
->rport
));
666 if (s
->fd
!= NULL
) fclose(fd
); // clean up
667 if (s
->strbuf
!= NULL
) free(s
->strbuf
);
668 if (s
->pstream
!= NULL
) {
669 // free these if they were allocated
670 delete_fifo(s
->fifo
);
671 delete_callback_stream(s
->pstream
); // this will mark it as closed and will get deleted when no longer needed
674 uip_conn
->appstate
= NULL
;
677 handle_connection(s
);
681 /*---------------------------------------------------------------------------*/
683 * \brief Initialize the web server
685 * This function initializes the web server and should be
686 * called at system boot-up.
688 void httpd_init(void)
690 uip_listen(HTONS(80));
692 /*---------------------------------------------------------------------------*/