00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00149 #include <cfg/arch.h>
00150 #include <cfg/http.h>
00151
00152 #include <string.h>
00153 #include <io.h>
00154 #include <fcntl.h>
00155 #include <ctype.h>
00156 #include <stdlib.h>
00157 #include <sys/stat.h>
00158 #include <memdebug.h>
00159
00160 #include <sys/heap.h>
00161 #include <sys/version.h>
00162
00163 #include <pro/rfctime.h>
00164 #include <pro/httpd.h>
00165
00166 #include "dencode.h"
00167 #include "httpd_p.h"
00168
00169
00171 #ifndef HTTP_MAJOR_VERSION
00172 #define HTTP_MAJOR_VERSION 1
00173 #endif
00174
00176 #ifndef HTTP_MINOR_VERSION
00177 #define HTTP_MINOR_VERSION 1
00178 #endif
00179
00181 #ifndef HTTP_KEEP_ALIVE_REQ
00182 #define HTTP_KEEP_ALIVE_REQ 0
00183 #endif
00184
00186 #ifndef HTTP_MAX_REQUEST_SIZE
00187 #define HTTP_MAX_REQUEST_SIZE 256
00188 #endif
00189
00191 #ifndef HTTP_FILE_CHUNK_SIZE
00192 #define HTTP_FILE_CHUNK_SIZE 512
00193 #endif
00194
00199
00203 MIMETYPES mimeTypes[] = {
00204 {
00205 ".txt", "text/plain", NULL}, {
00206 ".html", "text/html", NULL}, {
00207 ".shtml", "text/html", NULL}, {
00208 ".asp", "text/html", NULL}, {
00209 ".htm", "text/html", NULL}, {
00210 ".gif", "image/gif", NULL}, {
00211 ".jpg", "image/jpeg", NULL}, {
00212 ".png", "image/png", NULL}, {
00213 ".pdf", "application/pdf", NULL}, {
00214 ".js", "application/x-javascript", NULL}, {
00215 ".jar", "application/x-java-archive", NULL}, {
00216 ".css", "text/css", NULL}, {
00217 ".xml", "text/xml", NULL}, {
00218 NULL, NULL, NULL}
00219 };
00220
00221 static uint32_t http_optflags;
00222
00234 void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
00235 {
00236 static prog_char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
00237
00238 fprintf_P(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, NutVersionString());
00239 #if !defined(HTTPD_EXCLUDE_DATE)
00240 if (http_optflags & HTTP_OF_USE_HOST_TIME) {
00241 time_t now = time(NULL);
00242 fprintf(stream, "Date: %s GMT\r\n", Rfc1123TimeString(gmtime(&now)));
00243 }
00244 #endif
00245 }
00246
00263 void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
00264 {
00265 NutHttpSendHeaderBottom( stream, 0, mime_type, bytes );
00266 }
00267
00282 void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
00283 {
00284 static prog_char typ_fmt_P[] = "Content-Type: %s\r\n";
00285 static prog_char len_fmt_P[] = "Content-Length: %ld\r\n";
00286 static prog_char con_str_P[] = "Connection: ";
00287 static prog_char ccl_str_P[] = "close\r\n\r\n";
00288
00289 if (mime_type)
00290 fprintf_P(stream, typ_fmt_P, mime_type);
00291 if (bytes >= 0)
00292 fprintf_P(stream, len_fmt_P, bytes);
00293 fputs_P(con_str_P, stream);
00294 #if HTTP_KEEP_ALIVE_REQ
00295 if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
00296 static prog_char cka_str_P[] = "Keep-Alive\r\n\r\n";
00297 fputs_P(cka_str_P, stream);
00298 }
00299 else {
00300 fputs_P(ccl_str_P, stream);
00301 }
00302 #else
00303 fputs_P(ccl_str_P, stream);
00304 #endif
00305 }
00306
00317 void NutHttpSendError(FILE * stream, REQUEST * req, int status)
00318 {
00319 static prog_char err_fmt_P[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
00320 static prog_char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
00321 char *title;
00322
00323 switch (status) {
00324 case 304:
00325 title = "Not Modified";
00326 break;
00327 case 400:
00328 title = "Bad Request";
00329 break;
00330 case 401:
00331 title = "Unauthorized";
00332 break;
00333 case 404:
00334 title = "Not Found";
00335 break;
00336 case 500:
00337 title = "Internal Error";
00338 break;
00339 case 501:
00340 title = "Not Implemented";
00341 break;
00342 default:
00343 title = "Error";
00344 break;
00345 }
00346 #if HTTP_KEEP_ALIVE_REQ
00347 if (status >= 400) {
00348 req->req_connection = HTTP_CONN_CLOSE;
00349 }
00350 #endif
00351 NutHttpSendHeaderTop(stream, req, status, title);
00352 if (status == 401) {
00353 char *cp = 0;
00354 char *realm = req->req_url;
00355
00356 if ((cp = strrchr(realm, '/')) != NULL)
00357 *cp = 0;
00358 else
00359 realm = ".";
00360 fprintf_P(stream, auth_fmt_P, realm);
00361 if (cp)
00362 *cp = '/';
00363 }
00364 NutHttpSendHeaderBottom(stream, req, "text/html", -1);
00365 fprintf_P(stream, err_fmt_P, status, title, status, title);
00366 }
00367
00368 static MIMETYPES *GetMimeEntry(char *name)
00369 {
00370 MIMETYPES *rc;
00371 int fl;
00372
00373 if (name == NULL || (fl = strlen(name)) == 0) {
00374 return &mimeTypes[1];
00375 }
00376 for (rc = mimeTypes; rc->mtyp_ext; rc++) {
00377 if (strcasecmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
00378 return rc;
00379 }
00380 }
00381 return &mimeTypes[0];
00382 }
00383
00397 char *NutGetMimeType(char *name)
00398 {
00399 return GetMimeEntry(name)->mtyp_type;
00400 }
00401
00417 void *NutGetMimeHandler(char *name)
00418 {
00419 return GetMimeEntry(name)->mtyp_handler;
00420 }
00421
00433 void NutHttpURLDecode(char *str)
00434 {
00435 register char *ptr1, *ptr2, ch;
00436 char hexstr[3] = { 0, 0, 0 };
00437 for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
00438 if (*ptr1 == '+')
00439 *ptr2++ = ' ';
00440 else if (*ptr1 == '%') {
00441 hexstr[0] = ptr1[1];
00442 hexstr[1] = ptr1[2];
00443 ch = strtol(hexstr, 0, 16);
00444 *ptr2++ = ch;
00445 ptr1 += 2;
00446 } else
00447 *ptr2++ = *ptr1;
00448 }
00449 *ptr2 = 0;
00450 }
00451
00462 void NutHttpProcessQueryString(REQUEST * req)
00463 {
00464 register int i;
00465 register char *ptr;
00466
00467 if (!req->req_query)
00468 return;
00469
00470 req->req_numqptrs = 1;
00471 for (ptr = req->req_query; *ptr; ptr++)
00472 if (*ptr == '&')
00473 req->req_numqptrs++;
00474
00475 req->req_qptrs = (char **) malloc(sizeof(char *) * (req->req_numqptrs * 2));
00476 if (req->req_qptrs == NULL) {
00477
00478 req->req_numqptrs = 0;
00479 return;
00480 }
00481 req->req_qptrs[0] = req->req_query;
00482 req->req_qptrs[1] = NULL;
00483 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00484 if (*ptr == '&') {
00485 req->req_qptrs[i] = ptr + 1;
00486 req->req_qptrs[i + 1] = NULL;
00487 *ptr = 0;
00488 i += 2;
00489 }
00490 }
00491
00492 for (i = 0; i < req->req_numqptrs; i++) {
00493 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00494 if (*ptr == '=') {
00495 req->req_qptrs[i * 2 + 1] = ptr + 1;
00496 *ptr = 0;
00497 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00498 break;
00499 }
00500 }
00501 NutHttpURLDecode(req->req_qptrs[i * 2]);
00502 }
00503 }
00504
00505 static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
00506 {
00507 int fd;
00508 int n;
00509 char *data;
00510 long file_len;
00511 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00512 char *mime_type;
00513 char *filename = NULL;
00514 char *modstr = NULL;
00515
00516
00517
00518
00519 if (NutHttpAuthValidate(req)) {
00520 NutHttpSendError(stream, req, 401);
00521 return;
00522 }
00523
00524
00525
00526
00527 if (NutCgiCheckRequest(stream, req)) {
00528 return;
00529 }
00530
00531 for (n = 0, fd = -1; default_files[n]; n++) {
00532 filename = CreateFilePath(req->req_url, default_files[n]);
00533 if (filename == NULL) {
00534 NutHttpSendError(stream, req, 500);
00535 return;
00536 }
00537
00538
00539
00540
00541
00542
00543 if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00544 if (_filelength(fd)) {
00545 break;
00546 }
00547 _close(fd);
00548 }
00549 free(filename);
00550 }
00551 if (fd == -1) {
00552 NutHttpSendError(stream, req, 404);
00553 return;
00554 }
00555
00556
00557 mime_type = NutGetMimeType(filename);
00558 handler = NutGetMimeHandler(filename);
00559
00560 #if !defined(HTTPD_EXCLUDE_DATE)
00561
00562
00563
00564 if (handler == NULL && (http_optflags & HTTP_OF_USE_FILE_TIME)) {
00565 struct stat s;
00566 time_t ftime;
00567 char *time_str;
00568
00569 if (stat(filename, &s) == 0) {
00570 ftime = s.st_mtime;
00571 }
00572 else {
00573
00574 ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
00575 }
00576
00577
00578 if (req->req_ims && s.st_mtime <= req->req_ims) {
00579 _close(fd);
00580 NutHttpSendError(stream, req, 304);
00581 free(filename);
00582 return;
00583 }
00584
00585
00586 time_str = Rfc1123TimeString(gmtime(&ftime));
00587 modstr = strdup(time_str);
00588 }
00589 #endif
00590
00591
00592 free(filename);
00593
00594 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00595 if (modstr) {
00596 fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
00597 free(modstr);
00598 }
00599
00600 file_len = _filelength(fd);
00601
00602 if (handler) {
00603 NutHttpSendHeaderBottom(stream, req, mime_type, -1);
00604 handler(stream, fd, file_len, http_root, req);
00605 }
00606
00607 else {
00608 NutHttpSendHeaderBottom(stream, req, mime_type, file_len);
00609 if (req->req_method != METHOD_HEAD) {
00610 size_t size = HTTP_FILE_CHUNK_SIZE;
00611
00612 if ((data = malloc(size)) != NULL) {
00613 while (file_len) {
00614 if (file_len < HTTP_FILE_CHUNK_SIZE)
00615 size = (size_t) file_len;
00616
00617 n = _read(fd, data, size);
00618 if (n <= 0) {
00619
00620 break;
00621 }
00622 if (fwrite(data, 1, n, stream) == 0) {
00623 break;
00624 }
00625 file_len -= (long) n;
00626 }
00627 free(data);
00628 }
00629 }
00630 }
00631 _close(fd);
00632 }
00633
00637 static char *NextWord(char *str)
00638 {
00639 while (*str && *str != ' ' && *str != '\t')
00640 str++;
00641 if (*str)
00642 *str++ = 0;
00643 while (*str == ' ' || *str == '\t')
00644 str++;
00645 return str;
00646 }
00647
00653 static REQUEST *CreateRequestInfo(void)
00654 {
00655 REQUEST *req;
00656
00657 if ((req = calloc(1, sizeof(REQUEST))) != NULL) {
00658 req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
00659 }
00660 return req;
00661 }
00662
00675 int NutRegisterHttpRoot(char *path)
00676 {
00677 int len;
00678
00679 if (http_root)
00680 free(http_root);
00681 if (path && (len = strlen(path)) != 0) {
00682 if ((http_root = malloc(len + 1)) != NULL)
00683 strcpy(http_root, path);
00684 else
00685 return -1;
00686 } else
00687 http_root = NULL;
00688
00689 return 0;
00690 }
00691
00700 void NutHttpSetOptionFlags(uint32_t flags)
00701 {
00702 http_optflags = flags;
00703 }
00704
00710 uint32_t NutHttpGetOptionFlags(void)
00711 {
00712 return http_optflags;
00713 }
00714
00730 static int HeaderFieldValue(char **hfvp, CONST char *str)
00731 {
00732
00733 if (*hfvp == NULL) {
00734
00735 while (*str == ' ' || *str == '\t')
00736 str++;
00737
00738 if ((*hfvp = strdup(str)) == NULL)
00739 return -1;
00740 }
00741 return 0;
00742 }
00743
00753 void NutHttpProcessRequest(FILE * stream)
00754 {
00755 REQUEST *req = NULL;
00756 char *method = NULL;
00757 char *path;
00758 char *line;
00759 char *protocol;
00760 char *cp;
00761 #if HTTP_KEEP_ALIVE_REQ
00762 int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
00763 #endif
00764
00765 for(;;) {
00766
00767 DestroyRequestInfo(req);
00768 if ((req = CreateRequestInfo()) == NULL)
00769 break;
00770 if (method)
00771 free(method);
00772
00773
00774 if ((method = malloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00775 break;
00776 }
00777 if (fgets(method, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
00778 break;
00779 }
00780 if ((cp = strchr(method, '\r')) != NULL)
00781 *cp = 0;
00782 if ((cp = strchr(method, '\n')) != NULL)
00783 *cp = 0;
00784
00785
00786
00787
00788 if ((line = malloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00789 break;
00790 }
00791 for (;;) {
00792
00793 if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL)
00794 break;
00795 if ((cp = strchr(line, '\r')) != 0)
00796 *cp = 0;
00797 if ((cp = strchr(line, '\n')) != 0)
00798 *cp = 0;
00799 if (*line == 0)
00800
00801 break;
00802 if (strncasecmp(line, "Authorization:", 14) == 0) {
00803 if (HeaderFieldValue(&req->req_auth, line + 14))
00804 break;
00805 } else if (strncasecmp(line, "Content-Length:", 15) == 0) {
00806 req->req_length = atol(line + 15);
00807 } else if (strncasecmp(line, "Content-Type:", 13) == 0) {
00808 if (HeaderFieldValue(&req->req_type, line + 13))
00809 break;
00810 } else if (strncasecmp(line, "Cookie:", 7) == 0) {
00811 if (HeaderFieldValue(&req->req_cookie, line + 7))
00812 break;
00813 } else if (strncasecmp(line, "User-Agent:", 11) == 0) {
00814 if (HeaderFieldValue(&req->req_agent, line + 11))
00815 break;
00816 #if !defined(HTTPD_EXCLUDE_DATE)
00817 } else if (strncasecmp(line, "If-Modified-Since:", 18) == 0) {
00818 req->req_ims = RfcTimeParse(line + 18);
00819 #endif
00820 } else if (strncasecmp(line, "Referer:", 8) == 0) {
00821 if (HeaderFieldValue(&req->req_referer, line + 8))
00822 break;
00823 } else if (strncasecmp(line, "Host:", 5) == 0) {
00824 if (HeaderFieldValue(&req->req_host, line + 5))
00825 break;
00826 }
00827 #if HTTP_KEEP_ALIVE_REQ
00828 else if (strncasecmp(line, "Connection:", 11) == 0) {
00829 if (strncasecmp(line + 12, "close", 5) == 0) {
00830 req->req_connection = HTTP_CONN_CLOSE;
00831 }
00832 else if (strncasecmp(line + 12, "Keep-Alive", 10) == 0) {
00833 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00834 }
00835 }
00836 #endif
00837 }
00838 if (line) {
00839 free(line);
00840 }
00841 path = NextWord(method);
00842 protocol = NextWord(path);
00843 NextWord(protocol);
00844
00845
00846 if (strcasecmp(method, "GET") == 0)
00847 req->req_method = METHOD_GET;
00848 else if (strcasecmp(method, "HEAD") == 0)
00849 req->req_method = METHOD_HEAD;
00850 else if (strcasecmp(method, "POST") == 0)
00851 req->req_method = METHOD_POST;
00852 else {
00853 NutHttpSendError(stream, req, 501);
00854 break;
00855 }
00856 if (*path == 0 || *protocol == 0) {
00857 NutHttpSendError(stream, req, 400);
00858 break;
00859 }
00860
00861
00862 if (strcasecmp(protocol, "HTTP/1.0") == 0) {
00863 req->req_version = 10;
00864 #if HTTP_KEEP_ALIVE_REQ
00865 if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
00866 req->req_connection = HTTP_CONN_CLOSE;
00867 }
00868 #endif
00869 }
00870 #if HTTP_KEEP_ALIVE_REQ
00871 else if (req->req_connection != HTTP_CONN_CLOSE) {
00872 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00873 }
00874
00875
00876 if (keep_alive_max > 0) {
00877 keep_alive_max--;
00878 }
00879 else {
00880 req->req_connection = HTTP_CONN_CLOSE;
00881 }
00882 #else
00883 req->req_connection = HTTP_CONN_CLOSE;
00884 #endif
00885
00886 if ((cp = strchr(path, '?')) != 0) {
00887 *cp++ = 0;
00888 if ((req->req_query = strdup(cp)) == NULL) {
00889 break;
00890 }
00891 NutHttpProcessQueryString(req);
00892 }
00893
00894 if ((req->req_url = strdup(path)) == NULL) {
00895 break;
00896 }
00897
00898 if (NutDecodePath(req->req_url) == 0) {
00899 NutHttpSendError(stream, req, 400);
00900 } else {
00901 NutHttpProcessFileRequest(stream, req);
00902 }
00903 fflush(stream);
00904
00905 if (req->req_connection == HTTP_CONN_CLOSE) {
00906 break;
00907 }
00908 }
00909 DestroyRequestInfo(req);
00910 if (method)
00911 free(method);
00912 }
00913