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
00141 #include <cfg/arch.h>
00142 #include <cfg/http.h>
00143
00144 #include <string.h>
00145 #include <io.h>
00146 #include <fcntl.h>
00147 #include <ctype.h>
00148 #include <stdlib.h>
00149 #include <sys/stat.h>
00150
00151 #include <sys/heap.h>
00152 #include <sys/version.h>
00153
00154 #include <pro/rfctime.h>
00155 #include <pro/httpd.h>
00156
00157 #include "dencode.h"
00158 #include "httpd_p.h"
00159
00160
00162 #ifndef HTTP_MAJOR_VERSION
00163 #define HTTP_MAJOR_VERSION 1
00164 #endif
00165
00167 #ifndef HTTP_MINOR_VERSION
00168 #define HTTP_MINOR_VERSION 1
00169 #endif
00170
00172 #ifndef HTTP_KEEP_ALIVE_REQ
00173 #define HTTP_KEEP_ALIVE_REQ 0
00174 #endif
00175
00177 #ifndef HTTP_MAX_REQUEST_SIZE
00178 #define HTTP_MAX_REQUEST_SIZE 256
00179 #endif
00180
00182 #ifndef HTTP_FILE_CHUNK_SIZE
00183 #define HTTP_FILE_CHUNK_SIZE 512
00184 #endif
00185
00190
00194 MIMETYPES mimeTypes[] = {
00195 {
00196 ".txt", "text/plain", NULL}, {
00197 ".html", "text/html", NULL}, {
00198 ".shtml", "text/html", NULL}, {
00199 ".asp", "text/html", NULL}, {
00200 ".htm", "text/html", NULL}, {
00201 ".gif", "image/gif", NULL}, {
00202 ".jpg", "image/jpeg", NULL}, {
00203 ".png", "image/png", NULL}, {
00204 ".pdf", "application/pdf", NULL}, {
00205 ".js", "application/x-javascript", NULL}, {
00206 ".jar", "application/x-java-archive", NULL}, {
00207 ".css", "text/css", NULL}, {
00208 ".xml", "text/xml", NULL}, {
00209 NULL, NULL, NULL}
00210 };
00211
00212 static uint32_t http_optflags;
00213
00225 void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
00226 {
00227 static prog_char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";
00228
00229 fprintf_P(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, NutVersionString());
00230 #if !defined(HTTPD_EXCLUDE_DATE)
00231 if (http_optflags & HTTP_OF_USE_HOST_TIME) {
00232 time_t now = time(NULL);
00233 fprintf(stream, "Date: %s GMT\r\n", Rfc1123TimeString(gmtime(&now)));
00234 }
00235 #endif
00236 }
00237
00254 void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
00255 {
00256 NutHttpSendHeaderBottom( stream, 0, mime_type, bytes );
00257 }
00258
00273 void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
00274 {
00275 static prog_char typ_fmt_P[] = "Content-Type: %s\r\n";
00276 static prog_char len_fmt_P[] = "Content-Length: %ld\r\n";
00277 static prog_char con_str_P[] = "Connection: ";
00278 static prog_char ccl_str_P[] = "close\r\n\r\n";
00279
00280 if (mime_type)
00281 fprintf_P(stream, typ_fmt_P, mime_type);
00282 if (bytes >= 0)
00283 fprintf_P(stream, len_fmt_P, bytes);
00284 fputs_P(con_str_P, stream);
00285 #if HTTP_KEEP_ALIVE_REQ
00286 if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
00287 static prog_char cka_str_P[] = "Keep-Alive\r\n\r\n";
00288 fputs_P(cka_str_P, stream);
00289 }
00290 else {
00291 fputs_P(ccl_str_P, stream);
00292 }
00293 #else
00294 fputs_P(ccl_str_P, stream);
00295 #endif
00296 }
00297
00308 void NutHttpSendError(FILE * stream, REQUEST * req, int status)
00309 {
00310 static prog_char err_fmt_P[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
00311 static prog_char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
00312 char *title;
00313
00314 switch (status) {
00315 case 304:
00316 title = "Not Modified";
00317 break;
00318 case 400:
00319 title = "Bad Request";
00320 break;
00321 case 401:
00322 title = "Unauthorized";
00323 break;
00324 case 404:
00325 title = "Not Found";
00326 break;
00327 case 500:
00328 title = "Internal Error";
00329 break;
00330 case 501:
00331 title = "Not Implemented";
00332 break;
00333 default:
00334 title = "Error";
00335 break;
00336 }
00337 #if HTTP_KEEP_ALIVE_REQ
00338 if (status >= 400) {
00339 req->req_connection = HTTP_CONN_CLOSE;
00340 }
00341 #endif
00342 NutHttpSendHeaderTop(stream, req, status, title);
00343 if (status == 401) {
00344 char *cp = 0;
00345 char *realm = req->req_url;
00346
00347 if ((cp = strrchr(realm, '/')) != NULL)
00348 *cp = 0;
00349 else
00350 realm = ".";
00351 fprintf_P(stream, auth_fmt_P, realm);
00352 if (cp)
00353 *cp = '/';
00354 }
00355 NutHttpSendHeaderBottom(stream, req, "text/html", -1);
00356 fprintf_P(stream, err_fmt_P, status, title, status, title);
00357 }
00358
00359 static MIMETYPES *GetMimeEntry(char *name)
00360 {
00361 MIMETYPES *rc;
00362 int fl;
00363
00364 if (name == NULL || (fl = strlen(name)) == 0) {
00365 return &mimeTypes[1];
00366 }
00367 for (rc = mimeTypes; rc->mtyp_ext; rc++) {
00368 if (strcasecmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
00369 return rc;
00370 }
00371 }
00372 return &mimeTypes[0];
00373 }
00374
00388 char *NutGetMimeType(char *name)
00389 {
00390 return GetMimeEntry(name)->mtyp_type;
00391 }
00392
00408 void *NutGetMimeHandler(char *name)
00409 {
00410 return GetMimeEntry(name)->mtyp_handler;
00411 }
00412
00424 void NutHttpURLDecode(char *str)
00425 {
00426 register char *ptr1, *ptr2, ch;
00427 char hexstr[3] = { 0, 0, 0 };
00428 for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
00429 if (*ptr1 == '+')
00430 *ptr2++ = ' ';
00431 else if (*ptr1 == '%') {
00432 hexstr[0] = ptr1[1];
00433 hexstr[1] = ptr1[2];
00434 ch = strtol(hexstr, 0, 16);
00435 *ptr2++ = ch;
00436 ptr1 += 2;
00437 } else
00438 *ptr2++ = *ptr1;
00439 }
00440 *ptr2 = 0;
00441 }
00442
00453 void NutHttpProcessQueryString(REQUEST * req)
00454 {
00455 register int i;
00456 register char *ptr;
00457
00458 if (!req->req_query)
00459 return;
00460
00461 req->req_numqptrs = 1;
00462 for (ptr = req->req_query; *ptr; ptr++)
00463 if (*ptr == '&')
00464 req->req_numqptrs++;
00465
00466 req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
00467 if (req->req_qptrs == NULL) {
00468
00469 req->req_numqptrs = 0;
00470 return;
00471 }
00472 req->req_qptrs[0] = req->req_query;
00473 req->req_qptrs[1] = NULL;
00474 for (ptr = req->req_query, i = 2; *ptr; ptr++) {
00475 if (*ptr == '&') {
00476 req->req_qptrs[i] = ptr + 1;
00477 req->req_qptrs[i + 1] = NULL;
00478 *ptr = 0;
00479 i += 2;
00480 }
00481 }
00482
00483 for (i = 0; i < req->req_numqptrs; i++) {
00484 for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
00485 if (*ptr == '=') {
00486 req->req_qptrs[i * 2 + 1] = ptr + 1;
00487 *ptr = 0;
00488 NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
00489 break;
00490 }
00491 }
00492 NutHttpURLDecode(req->req_qptrs[i * 2]);
00493 }
00494 }
00495
00496 static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
00497 {
00498 int fd;
00499 int n;
00500 char *data;
00501 long file_len;
00502 void (*handler)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);
00503 char *mime_type;
00504 char *filename = NULL;
00505 char *modstr = NULL;
00506
00507
00508
00509
00510 if (NutHttpAuthValidate(req)) {
00511 NutHttpSendError(stream, req, 401);
00512 return;
00513 }
00514
00515
00516
00517
00518 if (NutCgiCheckRequest(stream, req)) {
00519 return;
00520 }
00521
00522 for (n = 0, fd = -1; default_files[n]; n++) {
00523 filename = CreateFilePath(req->req_url, default_files[n]);
00524 if (filename == NULL) {
00525 NutHttpSendError(stream, req, 500);
00526 return;
00527 }
00528
00529
00530
00531
00532
00533
00534 if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
00535 if (_filelength(fd)) {
00536 break;
00537 }
00538 _close(fd);
00539 }
00540 NutHeapFree(filename);
00541 }
00542 if (fd == -1) {
00543 NutHttpSendError(stream, req, 404);
00544 return;
00545 }
00546
00547
00548 mime_type = NutGetMimeType(filename);
00549 handler = NutGetMimeHandler(filename);
00550
00551 #if !defined(HTTPD_EXCLUDE_DATE)
00552
00553
00554
00555 if (handler == NULL && (http_optflags & HTTP_OF_USE_FILE_TIME)) {
00556 struct stat s;
00557 time_t ftime;
00558 char *time_str;
00559
00560 if (stat(filename, &s) == 0) {
00561 ftime = s.st_mtime;
00562 }
00563 else {
00564
00565 ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
00566 }
00567
00568
00569 if (req->req_ims && s.st_mtime <= req->req_ims) {
00570 _close(fd);
00571 NutHttpSendError(stream, req, 304);
00572 NutHeapFree(filename);
00573 return;
00574 }
00575
00576
00577 time_str = Rfc1123TimeString(gmtime(&ftime));
00578 if ((modstr = NutHeapAlloc(strlen(time_str) + 1)) != NULL) {
00579 strcpy(modstr, time_str);
00580 }
00581 }
00582 #endif
00583
00584
00585 NutHeapFree(filename);
00586
00587 NutHttpSendHeaderTop(stream, req, 200, "Ok");
00588 if (modstr) {
00589 fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
00590 NutHeapFree(modstr);
00591 }
00592
00593 file_len = _filelength(fd);
00594
00595 if (handler) {
00596 NutHttpSendHeaderBottom(stream, req, mime_type, -1);
00597 handler(stream, fd, file_len, http_root, req);
00598 }
00599
00600 else {
00601 NutHttpSendHeaderBottom(stream, req, mime_type, file_len);
00602 if (req->req_method != METHOD_HEAD) {
00603 size_t size = HTTP_FILE_CHUNK_SIZE;
00604
00605 if ((data = NutHeapAlloc(size)) != NULL) {
00606 while (file_len) {
00607 if (file_len < HTTP_FILE_CHUNK_SIZE)
00608 size = (size_t) file_len;
00609
00610 n = _read(fd, data, size);
00611 if (n <= 0) {
00612
00613 break;
00614 }
00615 if (fwrite(data, 1, n, stream) == 0) {
00616 break;
00617 }
00618 file_len -= (long) n;
00619 }
00620 NutHeapFree(data);
00621 }
00622 }
00623 }
00624 _close(fd);
00625 }
00626
00630 static char *NextWord(char *str)
00631 {
00632 while (*str && *str != ' ' && *str != '\t')
00633 str++;
00634 if (*str)
00635 *str++ = 0;
00636 while (*str == ' ' || *str == '\t')
00637 str++;
00638 return str;
00639 }
00640
00646 static REQUEST *CreateRequestInfo(void)
00647 {
00648 REQUEST *req;
00649
00650 if ((req = NutHeapAllocClear(sizeof(REQUEST))) != NULL) {
00651 req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
00652 }
00653 return req;
00654 }
00655
00668 int NutRegisterHttpRoot(char *path)
00669 {
00670 int len;
00671
00672 if (http_root)
00673 NutHeapFree(http_root);
00674 if (path && (len = strlen(path)) != 0) {
00675 if ((http_root = NutHeapAlloc(len + 1)) != NULL)
00676 strcpy(http_root, path);
00677 else
00678 return -1;
00679 } else
00680 http_root = NULL;
00681
00682 return 0;
00683 }
00684
00693 void NutHttpSetOptionFlags(uint32_t flags)
00694 {
00695 http_optflags = flags;
00696 }
00697
00703 uint32_t NutHttpGetOptionFlags(void)
00704 {
00705 return http_optflags;
00706 }
00707
00723 static int HeaderFieldValue(char **hfvp, CONST char *str)
00724 {
00725
00726 if (*hfvp == NULL) {
00727
00728 while (*str == ' ' || *str == '\t')
00729 str++;
00730
00731 if ((*hfvp = NutHeapAlloc(strlen(str) + 1)) == NULL)
00732 return -1;
00733 strcpy(*hfvp, str);
00734 }
00735 return 0;
00736 }
00737
00747 void NutHttpProcessRequest(FILE * stream)
00748 {
00749 REQUEST *req = NULL;
00750 char *method = NULL;
00751 char *path;
00752 char *line;
00753 char *protocol;
00754 char *cp;
00755 #if HTTP_KEEP_ALIVE_REQ
00756 int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
00757 #endif
00758
00759 for(;;) {
00760
00761 DestroyRequestInfo(req);
00762 if ((req = CreateRequestInfo()) == NULL)
00763 break;
00764 if (method)
00765 NutHeapFree(method);
00766
00767
00768 if ((method = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00769 break;
00770 }
00771 if (fgets(method, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
00772 break;
00773 }
00774 if ((cp = strchr(method, '\r')) != NULL)
00775 *cp = 0;
00776 if ((cp = strchr(method, '\n')) != NULL)
00777 *cp = 0;
00778
00779
00780
00781
00782 if ((line = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
00783 break;
00784 }
00785 for (;;) {
00786
00787 if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL)
00788 break;
00789 if ((cp = strchr(line, '\r')) != 0)
00790 *cp = 0;
00791 if ((cp = strchr(line, '\n')) != 0)
00792 *cp = 0;
00793 if (*line == 0)
00794
00795 break;
00796 if (strncasecmp(line, "Authorization:", 14) == 0) {
00797 if (HeaderFieldValue(&req->req_auth, line + 14))
00798 break;
00799 } else if (strncasecmp(line, "Content-Length:", 15) == 0) {
00800 req->req_length = atol(line + 15);
00801 } else if (strncasecmp(line, "Content-Type:", 13) == 0) {
00802 if (HeaderFieldValue(&req->req_type, line + 13))
00803 break;
00804 } else if (strncasecmp(line, "Cookie:", 7) == 0) {
00805 if (HeaderFieldValue(&req->req_cookie, line + 7))
00806 break;
00807 } else if (strncasecmp(line, "User-Agent:", 11) == 0) {
00808 if (HeaderFieldValue(&req->req_agent, line + 11))
00809 break;
00810 #if !defined(HTTPD_EXCLUDE_DATE)
00811 } else if (strncasecmp(line, "If-Modified-Since:", 18) == 0) {
00812 req->req_ims = RfcTimeParse(line + 18);
00813 #endif
00814 } else if (strncasecmp(line, "Referer:", 8) == 0) {
00815 if (HeaderFieldValue(&req->req_referer, line + 8))
00816 break;
00817 } else if (strncasecmp(line, "Host:", 5) == 0) {
00818 if (HeaderFieldValue(&req->req_host, line + 5))
00819 break;
00820 }
00821 #if HTTP_KEEP_ALIVE_REQ
00822 else if (strncasecmp(line, "Connection:", 11) == 0) {
00823 if (strncasecmp(line + 12, "close", 5) == 0) {
00824 req->req_connection = HTTP_CONN_CLOSE;
00825 }
00826 else if (strncasecmp(line + 12, "Keep-Alive", 10) == 0) {
00827 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00828 }
00829 }
00830 #endif
00831 }
00832 if (line) {
00833 NutHeapFree(line);
00834 }
00835 path = NextWord(method);
00836 protocol = NextWord(path);
00837 NextWord(protocol);
00838
00839
00840 if (strcasecmp(method, "GET") == 0)
00841 req->req_method = METHOD_GET;
00842 else if (strcasecmp(method, "HEAD") == 0)
00843 req->req_method = METHOD_HEAD;
00844 else if (strcasecmp(method, "POST") == 0)
00845 req->req_method = METHOD_POST;
00846 else {
00847 NutHttpSendError(stream, req, 501);
00848 break;
00849 }
00850 if (*path == 0 || *protocol == 0) {
00851 NutHttpSendError(stream, req, 400);
00852 break;
00853 }
00854
00855
00856 if (strcasecmp(protocol, "HTTP/1.0") == 0) {
00857 req->req_version = 10;
00858 #if HTTP_KEEP_ALIVE_REQ
00859 if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
00860 req->req_connection = HTTP_CONN_CLOSE;
00861 }
00862 #endif
00863 }
00864 #if HTTP_KEEP_ALIVE_REQ
00865 else if (req->req_connection != HTTP_CONN_CLOSE) {
00866 req->req_connection = HTTP_CONN_KEEP_ALIVE;
00867 }
00868
00869
00870 if (keep_alive_max > 0) {
00871 keep_alive_max--;
00872 }
00873 else {
00874 req->req_connection = HTTP_CONN_CLOSE;
00875 }
00876 #else
00877 req->req_connection = HTTP_CONN_CLOSE;
00878 #endif
00879
00880 if ((cp = strchr(path, '?')) != 0) {
00881 *cp++ = 0;
00882 if ((req->req_query = NutHeapAlloc(strlen(cp) + 1)) == NULL) {
00883 break;
00884 }
00885 strcpy(req->req_query, cp);
00886
00887 NutHttpProcessQueryString(req);
00888 }
00889
00890 if ((req->req_url = NutHeapAlloc(strlen(path) + 1)) == NULL) {
00891 break;
00892 }
00893 strcpy(req->req_url, path);
00894
00895 if (NutDecodePath(req->req_url) == 0) {
00896 NutHttpSendError(stream, req, 400);
00897 } else {
00898 NutHttpProcessFileRequest(stream, req);
00899 }
00900 fflush(stream);
00901
00902 if (req->req_connection == HTTP_CONN_CLOSE) {
00903 break;
00904 }
00905 }
00906 DestroyRequestInfo(req);
00907 if (method)
00908 NutHeapFree(method);
00909 }
00910