Nut/OS  4.10.3
API Reference
httpd.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 by egnite GmbH. All rights reserved.
00003  * Copyright (C) 2001-2006 by egnite Software GmbH. All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the copyright holders nor the names of
00015  *    contributors may be used to endorse or promote products derived
00016  *    from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00025  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00026  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00028  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  *
00031  * For additional information see http://www.ethernut.de/
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         /* Out of memory */
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      * Validate authorization.
00605      */
00606     if (NutHttpAuthValidate(req)) {
00607         NutHttpSendError(stream, req, 401);
00608         return;
00609     }
00610 
00611     /*
00612      * Process CGI.
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          * Note, that simple file systems may not provide stat() or access(),
00626          * thus trying to open the file is the only way to check for existence.
00627          * Another problem is, that PHAT allows to open directories. We use
00628          * the file length to ensure, that we got a normal file.
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     /* Check for mime type and handler. */
00644     mime_type = NutGetMimeType(filename);
00645     handler = NutGetMimeHandler(filename);
00646 
00647 #if !defined(HTTPD_EXCLUDE_DATE)
00648     /*
00649      * Optionally process modification time.
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             /* Use compile time if stat not available. */
00661             ftime = RfcTimeParse("Fri " __DATE__ " " __TIME__);
00662         }
00663 
00664         /* Check if-modified-since condition. */
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         /* Save static buffer contents. */
00673         time_str = Rfc1123TimeString(gmtime(&ftime));
00674         modstr = strdup(time_str);
00675     }
00676 #endif /* HTTPD_EXCLUDE_DATE */
00677 
00678     /* Filename no longer used. */
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     /* Use mime handler, if one has been registered. */
00690     if (handler) {
00691         NutHttpSendHeaderBottom(stream, req, mime_type, -1);
00692         handler(stream, fd, file_len, http_root, req);
00693     }
00694     /* Use default transfer, if no registered mime handler is available. */
00695     else {
00696     
00697 #if (HTTPD_SUPPORT_GZIP >= 1)    
00698         /* Check for Accept-Encoding: gzip support */
00699         if (req->req_encoding != NULL) {
00700             if (strstr(req->req_encoding, "gzip") != NULL) {
00701                 /* Read first two bytes, needed for gzip header check */
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                         /* We can't do much here, the header is out already. */
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     /* Do not override existing values. */
00833     if (*hfvp == NULL) {
00834         /* Skip spaces. */
00835         while (*str == ' ' || *str == '\t')
00836             str++;
00837         /* Allocate a string copy. */
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     /* Read the first character, which is not a carriage returns. */
00869     do {
00870         ch = fgetc(stream);
00871     } while (ch == '\r');
00872     /* Return 0, if we got the linefeed. This is an empty line,
00873        which should be interpreted by the caller as the end of
00874        the HTTP header. */
00875     if (ch == '\n') {
00876         return 0;
00877     }
00878 
00879     /* Lookup the header line name. */
00880     while (i < MAX_REQUEST_NAME_SIZE && *idx < NUM_REQUEST_LOOKUP) {
00881         /* Stop, if the last read failed or if we found the end of the
00882            line or the name field. */
00883         if (ch == EOF || ch == '\n') {
00884             break;
00885         }
00886         
00887         /* Check if the length is correct */
00888         if (ch == ':') {
00889             if (i == req_lookup[*idx].rlu_len) {
00890                 /* The correct element was found */
00891                 break;
00892             } else {
00893                 /* The element does not match */
00894                 *idx = -1;
00895                 break;
00896             }
00897         }
00898         
00899         /* Check if all characters read so far fits with any header
00900             we are interested in. */
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                 /* So far, this header fits. */
00905                 break;
00906             }
00907         }
00908         /* Read the next character, ignoring carriage returns. */
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             /* Parse the next header name. */
00942             ch = NextHeaderName(stream, &req_idx);
00943             if (ch == EOF) {
00944                 /* Broken connection, stop parsing. */
00945                 break;
00946             }
00947             if (ch == 0) {
00948                 /* Empty line marks the end of the request header. */
00949                 free(line);
00950                 return 0;
00951             }
00952             if (ch != ':' || req_idx >= NUM_REQUEST_LOOKUP) {
00953                 /* No valid name/value pair or unexpected header line.
00954                    Skip this line. */
00955                 SkipLine(stream);
00956             } else {
00957                 /* At this point we got a header we are interested in.
00958                    Read the rest of this line, it contains the value. */
00959                 if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
00960                     /* Broken connection, stop parsing. */
00961                     break;
00962                 }
00963 
00964                 /* Make sure we got a complete line. */
00965                 cp = strchr(line, '\n');
00966                 if (cp == NULL) {
00967                     /* Incomplete line, skip it. */
00968                     SkipLine(stream);
00969                     /* May be we should return an internal error to the browser. */
00970                 } else {
00971                     /* We got the value, chop off any CR-LF. */
00972                     *cp = '\0';
00973                     if (cp > line && *--cp == '\r') {
00974                         *cp = '\0';
00975                     }
00976                     //printf("Header '%s: %s'\n", req_lookup[req_idx].rlu_str, line);
00977                     /* Store the value in the request info structure.
00978                        Impotant! This switch statement must correspond
00979                        to the req_lookup table. */
00980                     strval = NULL;
00981                     switch (req_idx) {
00982                     case 0:
00983                         /* Accept-encoding */
00984                         strval = &req->req_encoding;
00985                         break;
00986                     case 1:
00987                         /* Authorization: Store as string. */
00988                         strval = &req->req_auth;
00989                         break;
00990 #if HTTP_KEEP_ALIVE_REQ
00991                     case 2:
00992                         /* Connection: Interpret type. */
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                         /* Content-Length: Get size. */
01003                         req->req_length = atol(line);
01004                         break;
01005                     case 4:
01006                         /* Content-Type: Store as string. */
01007                         strval = &req->req_type;
01008                         break;
01009                     case 5:
01010                         /* Cookie: Store as string. */
01011                         strval = &req->req_cookie;
01012                         break;
01013                     case 6:
01014                         /* Host: Store as string. */
01015                         strval = &req->req_host;
01016                         break;
01017 #if !defined(HTTPD_EXCLUDE_DATE)
01018                     case 7:
01019                         /* If-Modified-Since: Interpret RFC date. */
01020                         req->req_ims = RfcTimeParse(line);
01021                         break;
01022 #endif
01023                     case 8:
01024                         /* Referer: Store as string. */
01025                         strval = &req->req_referer;
01026                         break;
01027                     case 9:
01028                         /* User-Agent: Store as string. */
01029                         strval = &req->req_agent;
01030                         break;
01031                     }
01032                     /* Anything to store as a string. */
01033                     if (strval && HeaderFieldValue(strval, line)) {
01034                         break;
01035                     }
01036                 }
01037             }
01038         }
01039         /* All header lines processed. */
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         /* Release resources used on the previous connect. */
01067         DestroyRequestInfo(req);
01068         if ((req = CreateRequestInfo()) == NULL)
01069             break;
01070         if (method)
01071             free(method);
01072 
01073         /* The first line contains method, path and protocol. */
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         /* Parse remaining request header lines. */
01086         if (ParserHeaderLines(stream, req)) {
01087             break;
01088         }
01089 
01090         /* Further break up the first header line. */
01091         path = NextWord(method);
01092         protocol = NextWord(path);
01093         NextWord(protocol);
01094 
01095         /* Determine the request method. */
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         /* Determine the client's HTTP version. */
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         /* Limit the number of requests per connection. */
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