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
00196 #ifndef HTTPD_SUPPORT_GZIP
00197 #define HTTPD_SUPPORT_GZIP 0
00198 #endif
00199
00204
00208 typedef struct _REQUEST_LOOKUP {
00209 uint_fast8_t rlu_len;
00210 char *rlu_str;
00211 } REQUEST_LOOKUP;
00212
00223 static const REQUEST_LOOKUP req_lookup[] = {
00224 { 15, "accept-encoding" },
00225 { 13, "authorization" },
00226 #if HTTP_KEEP_ALIVE_REQ
00227 { 10, "connection" },
00228 #else
00229 { 0, NULL },
00230 #endif
00231 { 14, "content-length" },
00232 { 12, "content-type" },
00233 { 6, "cookie" },
00234 { 4, "host" },
00235 #if defined(HTTPD_EXCLUDE_DATE)
00236 { 0, NULL },
00237 #else
00238 { 17, "if-modified-since" },
00239 #endif
00240 { 7, "referer" },
00241 { 10, "user-agent" }
00242 };
00243
00247 #define NUM_REQUEST_LOOKUP sizeof(req_lookup) / sizeof(REQUEST_LOOKUP)
00248
00252 #if defined(HTTPD_EXCLUDE_DATE)
00253 #define MAX_REQUEST_NAME_SIZE 15
00254 #else
00255 #define MAX_REQUEST_NAME_SIZE 17
00256 #endif
00257
00261 MIMETYPES mimeTypes[] = {
00262 {
00263 ".txt", "text/plain", NULL}, {
00264 ".html", "text/html", NULL}, {
00265 ".shtml", "text/html", NULL}, {
00266 ".asp", "text/html", NULL}, {
00267 ".htm", "text/html", NULL}, {
00268 ".gif", "image/gif", NULL}, {
00269 ".jpg", "image/jpeg", NULL}, {
00270 ".png", "image/png", NULL}, {
00271 ".bmp", "image/bmp", NULL}, {
00272 ".pdf", "application/pdf", NULL}, {
00273 ".js", "application/x-javascript", NULL}, {
00274 ".jar", "application/x-java-archive", NULL}, {
00275 ".css", "text/css", NULL}, {
00276 ".xml", "text/xml", NULL}, {
00277 ".svg", "image/svg+xml", NULL}, {
00278 NULL, NULL, NULL}
00279 };
00280
00281 static uint32_t http_optflags;
00282
00294 void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
00295 {
00296 static prog_char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
00297
00298 fprintf_P(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, NutVersionString());
00299 #if !defined(HTTPD_EXCLUDE_DATE)
00300 if (http_optflags & HTTP_OF_USE_HOST_TIME) {
00301 time_t now = time(NULL);
00302 fprintf(stream, "Date: %s GMT\r\n", Rfc1123TimeString(gmtime(&now)));
00303 }
00304 #endif
00305 }
00306
00323 void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
00324 {
00325 NutHttpSendHeaderBottom( stream, 0, mime_type, bytes);
00326 }
00327
00344 static void NutHttpSendHeaderBottomEx(FILE * stream, REQUEST * req, char *mime_type, long bytes, unsigned short first2bytes)
00345 {
00346 static prog_char typ_fmt_P[] = "Content-Type: %s\r\n";
00347 static prog_char len_fmt_P[] = "Content-Length: %ld\r\n";
00348 static prog_char enc_fmt_P[] = "Content-Encoding: gzip\r\n";
00349 static prog_char con_str_P[] = "Connection: ";
00350 static prog_char ccl_str_P[] = "close\r\n\r\n";
00351
00352 #define GZIP_ID 0x8b1f
00353
00354 if (mime_type)
00355 fprintf_P(stream, typ_fmt_P, mime_type);
00356 if (bytes >= 0)
00357 fprintf_P(stream, len_fmt_P, bytes);
00358 if (first2bytes == GZIP_ID)
00359 fputs_P(enc_fmt_P, stream);
00360 fputs_P(con_str_P, stream);
00361 #if HTTP_KEEP_ALIVE_REQ
00362 if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
00363 static prog_char cka_str_P[] = "Keep-Alive\r\n\r\n";
00364 fputs_P(cka_str_P, stream);
00365 }
00366 else {
00367 fputs_P(ccl_str_P, stream);
00368 }
00369 #else
00370 fputs_P(ccl_str_P, stream);
00371 #endif
00372 }
00373
00388 void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
00389 {
00390 NutHttpSendHeaderBottomEx(stream, req, mime_type, bytes, 0);
00391 }
00392
00403 void NutHttpSendError(FILE * stream, REQUEST * req, int status)
00404 {
00405 static prog_char err_fmt_P[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
00406 static prog_char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
00407 char *title;
00408
00409 switch (status) {
00410 case 304:
00411 title = "Not Modified";
00412 break;
00413 case 400:
00414 title = "Bad Request";
00415 break;
00416 case 401:
00417 title = "Unauthorized";
00418 break;
00419 case 404:
00420 title = "Not Found";
00421 break;
00422 case 500:
00423 title = "Internal Error";
00424 break;
00425 case 501:
00426 title = "Not Implemented";
00427 break;
00428 default:
00429 title = "Error";
00430 break;
00431 }
00432 #if HTTP_KEEP_ALIVE_REQ
00433 if (status >= 400) {
00434 req->req_connection = HTTP_CONN_CLOSE;
00435 }
00436 #endif
00437 NutHttpSendHeaderTop(stream, req, status, title);
00438 if (status == 401) {
00439 char *cp = 0;
00440 char *realm = req->req_url;
00441
00442 if ((cp = strrchr(realm, '/')) != NULL)
00443 *cp = 0;
00444 else
00445 realm = ".";
00446 fprintf_P(stream, auth_fmt_P, realm);
00447 if (cp)
00448 *cp = '/';
00449 }
00450 NutHttpSendHeaderBottom(stream, req, "text/html", -1);
00451 fprintf_P(stream, err_fmt_P, status, title, status, title);
00452 }
00453
00454 static MIMETYPES *GetMimeEntry(char *name)
00455 {
00456 MIMETYPES *rc;
00457 int fl;
00458
00459 if (name == NULL || (fl = strlen(name)) == 0) {
00460 return &mimeTypes[1];
00461 }
00462 for (rc = mimeTypes; rc->mtyp_ext; rc++) {
00463 if (strcasecmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
00464 return rc;
00465 }
00466 }
00467 return &mimeTypes[0];
00468 }
00469
00483 char *NutGetMimeType(char *name)
00484 {
00485 return GetMimeEntry(name)->mtyp_type;
00486 }
00487
00503 void *NutGetMimeHandler(char *name)
00504 {
00505 return GetMimeEntry(name)->mtyp_handler;
00506 }
00507
00519 void NutHttpURLDecode(char *str)
00520 {
00521 register char *ptr1, *ptr2, ch;
00522 char hexstr[3] = { 0, 0, 0 };
00523 for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
00524 if (*ptr1 == '+')
00525 *ptr2++ = ' ';
00526 else if (*ptr1 == '%') {
00527 hexstr[0] = ptr1[1];
00528 hexstr[1] = ptr1[2];
00529 ch = strtol(hexstr, 0, 16);
00530 *ptr2++ = ch;
00531 ptr1 += 2;
00532 } else
00533 *ptr2++ = *ptr1;
00534 }
00535 *ptr2 = 0;
00536 }
00537
00548 void NutHttpProcessQueryString(REQUEST * req)
00549 {
00550 register int i;
00551 register char *ptr;
00552
00553 if (!req->req_query)
00554 return;
00555
00556 req->req_numqptrs = 1;
00557 for (ptr = req->req_query; *ptr; ptr++)
00558 if (*ptr == '&')
00559 req->req_numqptrs++;
00560
00561 req->req_qptrs = (char **) malloc(sizeof(char *) * (req->req_numqptrs * 2));
00562 if (req->req_qptrs == NULL) {
00563
00564 req->req_numqptrs = 0;
00565 return;
00566 }
00567 req->req_qptrs[0] = req->req_query;
00568 req->req_qptrs[1] = NULL;
00569 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00570 if (*ptr == '&') {
00571 req->req_qptrs[i] = ptr + 1;
00572 req->req_qptrs[i + 1] = NULL;
00573 *ptr = 0;
00574 i += 2;
00575 }
00576 }
00577
00578 for (i = 0; i < req->req_numqptrs; i++) {
00579 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00580 if (*ptr == '=') {
00581 req->req_qptrs[i * 2 + 1] = ptr + 1;
00582 *ptr = 0;
00583 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00584 break;
00585 }
00586 }
00587 NutHttpURLDecode(req->req_qptrs[i * 2]);
00588 }
00589 }
00590
00591 static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
00592 {
00593 int fd;
00594 int n;
00595 char *data;
00596 long file_len;
00597 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00598 char *mime_type;
00599 char *filename = NULL;
00600 char *modstr = NULL;
00601 unsigned short first2bytes = 0;
00602
00603
00604
00605
00606 if (NutHttpAuthValidate(req)) {
00607 NutHttpSendError(stream, req, 401);
00608 return;
00609 }
00610
00611
00612
00613
00614 if (NutCgiCheckRequest(stream, req)) {
00615 return;
00616 }
00617
00618 for (n = 0, fd = -1; default_files[n]; n++) {
00619 filename = CreateFilePath(req->req_url, default_files[n]);
00620 if (filename == NULL) {
00621 NutHttpSendError(stream, req, 500);
00622 return;
00623 }
00624
00625
00626
00627
00628
00629
00630 if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00631 if (_filelength(fd)) {
00632 break;
00633 }
00634 _close(fd);
00635 }
00636 free(filename);
00637 }
00638 if (fd == -1) {
00639 NutHttpSendError(stream, req, 404);
00640 return;
00641 }
00642
00643
00644 mime_type = NutGetMimeType(filename);
00645 handler = NutGetMimeHandler(filename);
00646
00647 #if !defined(HTTPD_EXCLUDE_DATE)
00648
00649
00650
00651 if (handler == NULL && (http_optflags & HTTP_OF_USE_FILE_TIME)) {
00652 struct stat s;
00653 time_t ftime;
00654 char *time_str;
00655
00656 if (stat(filename, &s) == 0) {
00657 ftime = s.st_mtime;
00658 }
00659 else {
00660
00661 ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
00662 }
00663
00664
00665 if (req->req_ims && s.st_mtime <= req->req_ims) {
00666 _close(fd);
00667 NutHttpSendError(stream, req, 304);
00668 free(filename);
00669 return;
00670 }
00671
00672
00673 time_str = Rfc1123TimeString(gmtime(&ftime));
00674 modstr = strdup(time_str);
00675 }
00676 #endif
00677
00678
00679 free(filename);
00680
00681 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00682 if (modstr) {
00683 fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
00684 free(modstr);
00685 }
00686
00687 file_len = _filelength(fd);
00688
00689
00690 if (handler) {
00691 NutHttpSendHeaderBottom(stream, req, mime_type, -1);
00692 handler(stream, fd, file_len, http_root, req);
00693 }
00694
00695 else {
00696
00697 #if (HTTPD_SUPPORT_GZIP >= 1)
00698
00699 if (req->req_encoding != NULL) {
00700 if (strstr(req->req_encoding, "gzip") != NULL) {
00701
00702 _read(fd, &first2bytes, 2);
00703 _seek(fd, -2, SEEK_CUR);
00704 }
00705 }
00706 #endif
00707
00708 NutHttpSendHeaderBottomEx(stream, req, mime_type, file_len, first2bytes);
00709 if (req->req_method != METHOD_HEAD) {
00710 size_t size = HTTP_FILE_CHUNK_SIZE;
00711
00712 if ((data = malloc(size)) != NULL) {
00713 while (file_len) {
00714 if (file_len < HTTP_FILE_CHUNK_SIZE)
00715 size = (size_t) file_len;
00716
00717 n = _read(fd, data, size);
00718 if (n <= 0) {
00719
00720 break;
00721 }
00722 if (fwrite(data, 1, n, stream) == 0) {
00723 break;
00724 }
00725 file_len -= (long) n;
00726 }
00727 free(data);
00728 }
00729 }
00730 }
00731 _close(fd);
00732 }
00733
00737 static char *NextWord(char *str)
00738 {
00739 while (*str && *str != ' ' && *str != '\t')
00740 str++;
00741 if (*str)
00742 *str++ = 0;
00743 while (*str == ' ' || *str == '\t')
00744 str++;
00745 return str;
00746 }
00747
00753 static REQUEST *CreateRequestInfo(void)
00754 {
00755 REQUEST *req;
00756
00757 if ((req = calloc(1, sizeof(REQUEST))) != NULL) {
00758 req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
00759 }
00760 return req;
00761 }
00762
00775 int NutRegisterHttpRoot(char *path)
00776 {
00777 int len;
00778
00779 if (http_root)
00780 free(http_root);
00781 if (path && (len = strlen(path)) != 0) {
00782 if ((http_root = malloc(len + 1)) != NULL)
00783 strcpy(http_root, path);
00784 else
00785 return -1;
00786 } else
00787 http_root = NULL;
00788
00789 return 0;
00790 }
00791
00800 void NutHttpSetOptionFlags(uint32_t flags)
00801 {
00802 http_optflags = flags;
00803 }
00804
00810 uint32_t NutHttpGetOptionFlags(void)
00811 {
00812 return http_optflags;
00813 }
00814
00830 static int HeaderFieldValue(char **hfvp, CONST char *str)
00831 {
00832
00833 if (*hfvp == NULL) {
00834
00835 while (*str == ' ' || *str == '\t')
00836 str++;
00837
00838 if ((*hfvp = strdup(str)) == NULL)
00839 return -1;
00840 }
00841 return 0;
00842 }
00843
00861 static int NextHeaderName(FILE * stream, uint_fast8_t *idx)
00862 {
00863 uint_fast8_t i = 0;
00864 int ch = 0;
00865
00866 *idx = 0;
00867
00868
00869 do {
00870 ch = fgetc(stream);
00871 } while (ch == '\r');
00872
00873
00874
00875 if (ch == '\n') {
00876 return 0;
00877 }
00878
00879
00880 while (i < MAX_REQUEST_NAME_SIZE && *idx < NUM_REQUEST_LOOKUP) {
00881
00882
00883 if (ch == EOF || ch == '\n') {
00884 break;
00885 }
00886
00887
00888 if (ch == ':') {
00889 if (i == req_lookup[*idx].rlu_len) {
00890
00891 break;
00892 } else {
00893
00894 *idx = -1;
00895 break;
00896 }
00897 }
00898
00899
00900
00901 for (; *idx < NUM_REQUEST_LOOKUP; (*idx)++) {
00902 if (i < req_lookup[*idx].rlu_len &&
00903 *(req_lookup[*idx].rlu_str + i) == tolower(ch)) {
00904
00905 break;
00906 }
00907 }
00908
00909 i++;
00910 do {
00911 ch = fgetc(stream);
00912 } while (ch == '\r');
00913 }
00914 return ch;
00915 }
00916
00920 static void SkipLine(FILE * stream)
00921 {
00922 int ch;
00923
00924 do {
00925 ch = fgetc(stream);
00926 } while (ch != EOF && ch != '\n');
00927 }
00928
00929 static int ParserHeaderLines(FILE *stream, REQUEST *req)
00930 {
00931 char *cp;
00932 int ch;
00933 uint_fast8_t req_idx;
00934 char **strval;
00935 char *line;
00936
00937 line = malloc(HTTP_MAX_REQUEST_SIZE);
00938 if (line) {
00939
00940 for (;;) {
00941
00942 ch = NextHeaderName(stream, &req_idx);
00943 if (ch == EOF) {
00944
00945 break;
00946 }
00947 if (ch == 0) {
00948
00949 free(line);
00950 return 0;
00951 }
00952 if (ch != ':' || req_idx >= NUM_REQUEST_LOOKUP) {
00953
00954
00955 SkipLine(stream);
00956 } else {
00957
00958
00959 if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
00960
00961 break;
00962 }
00963
00964
00965 cp = strchr(line, '\n');
00966 if (cp == NULL) {
00967
00968 SkipLine(stream);
00969
00970 } else {
00971
00972 *cp = '\0';
00973 if (cp > line && *--cp == '\r') {
00974 *cp = '\0';
00975 }
00976
00977
00978
00979
00980 strval = NULL;
00981 switch (req_idx) {
00982 case 0:
00983
00984 strval = &req->req_encoding;
00985 break;
00986 case 1:
00987
00988 strval = &req->req_auth;
00989 break;
00990 #if HTTP_KEEP_ALIVE_REQ
00991 case 2:
00992
00993 if (strncasecmp(line, "close", 5) == 0) {
00994 req->req_connection = HTTP_CONN_CLOSE;
00995 }
00996 else if (strncasecmp(line, "Keep-Alive", 10) == 0) {
00997 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00998 }
00999 break;
01000 #endif
01001 case 3:
01002
01003 req->req_length = atol(line);
01004 break;
01005 case 4:
01006
01007 strval = &req->req_type;
01008 break;
01009 case 5:
01010
01011 strval = &req->req_cookie;
01012 break;
01013 case 6:
01014
01015 strval = &req->req_host;
01016 break;
01017 #if !defined(HTTPD_EXCLUDE_DATE)
01018 case 7:
01019
01020 req->req_ims = RfcTimeParse(line);
01021 break;
01022 #endif
01023 case 8:
01024
01025 strval = &req->req_referer;
01026 break;
01027 case 9:
01028
01029 strval = &req->req_agent;
01030 break;
01031 }
01032
01033 if (strval && HeaderFieldValue(strval, line)) {
01034 break;
01035 }
01036 }
01037 }
01038 }
01039
01040 free(line);
01041 }
01042 return -1;
01043 }
01044
01054 void NutHttpProcessRequest(FILE * stream)
01055 {
01056 REQUEST *req = NULL;
01057 char *method = NULL;
01058 char *path;
01059 char *protocol;
01060 char *cp;
01061 #if HTTP_KEEP_ALIVE_REQ
01062 int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
01063 #endif
01064
01065 for(;;) {
01066
01067 DestroyRequestInfo(req);
01068 if ((req = CreateRequestInfo()) == NULL)
01069 break;
01070 if (method)
01071 free(method);
01072
01073
01074 if ((method = malloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
01075 break;
01076 }
01077 if (fgets(method, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
01078 break;
01079 }
01080 if ((cp = strchr(method, '\r')) != NULL)
01081 *cp = 0;
01082 if ((cp = strchr(method, '\n')) != NULL)
01083 *cp = 0;
01084
01085
01086 if (ParserHeaderLines(stream, req)) {
01087 break;
01088 }
01089
01090
01091 path = NextWord(method);
01092 protocol = NextWord(path);
01093 NextWord(protocol);
01094
01095
01096 if (strcasecmp(method, "GET") == 0)
01097 req->req_method = METHOD_GET;
01098 else if (strcasecmp(method, "HEAD") == 0)
01099 req->req_method = METHOD_HEAD;
01100 else if (strcasecmp(method, "POST") == 0)
01101 req->req_method = METHOD_POST;
01102 else {
01103 NutHttpSendError(stream, req, 501);
01104 break;
01105 }
01106 if (*path == 0 || *protocol == 0) {
01107 NutHttpSendError(stream, req, 400);
01108 break;
01109 }
01110
01111
01112 if (strcasecmp(protocol, "HTTP/1.0") == 0) {
01113 req->req_version = 10;
01114 #if HTTP_KEEP_ALIVE_REQ
01115 if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
01116 req->req_connection = HTTP_CONN_CLOSE;
01117 }
01118 #endif
01119 }
01120 #if HTTP_KEEP_ALIVE_REQ
01121 else if (req->req_connection != HTTP_CONN_CLOSE) {
01122 req->req_connection = HTTP_CONN_KEEP_ALIVE;
01123 }
01124
01125
01126 if (keep_alive_max > 0) {
01127 keep_alive_max--;
01128 }
01129 else {
01130 req->req_connection = HTTP_CONN_CLOSE;
01131 }
01132 #else
01133 req->req_connection = HTTP_CONN_CLOSE;
01134 #endif
01135
01136 if ((cp = strchr(path, '?')) != 0) {
01137 *cp++ = 0;
01138 if ((req->req_query = strdup(cp)) == NULL) {
01139 break;
01140 }
01141 NutHttpProcessQueryString(req);
01142 }
01143
01144 if ((req->req_url = strdup(path)) == NULL) {
01145 break;
01146 }
01147
01148 if (NutDecodePath(req->req_url) == 0) {
01149 NutHttpSendError(stream, req, 400);
01150 } else {
01151 NutHttpProcessFileRequest(stream, req);
01152 }
01153 fflush(stream);
01154
01155 if (req->req_connection == HTTP_CONN_CLOSE) {
01156 break;
01157 }
01158 }
01159 DestroyRequestInfo(req);
01160 if (method)
01161 free(method);
01162 }
01163