]> git.karo-electronics.de Git - karo-tx-redboot.git/blobdiff - packages/net/athttpd/v2_0/src/http.c
Initial revision
[karo-tx-redboot.git] / packages / net / athttpd / v2_0 / src / http.c
diff --git a/packages/net/athttpd/v2_0/src/http.c b/packages/net/athttpd/v2_0/src/http.c
new file mode 100644 (file)
index 0000000..bb7fc28
--- /dev/null
@@ -0,0 +1,1048 @@
+/* =================================================================
+ *
+ *      http.c
+ *
+ *      Handles the client requests.
+ *
+ * ================================================================= 
+ * ####ECOSGPLCOPYRIGHTBEGIN####
+ * -------------------------------------------
+ * This file is part of eCos, the Embedded Configurable Operating
+ * System.
+ * Copyright (C) 2005, 2007 eCosCentric Ltd.
+ * 
+ * eCos is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 or (at your option)
+ * any later version.
+ * 
+ * eCos is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with eCos; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * 
+ * As a special exception, if other files instantiate templates or
+ * use macros or inline functions from this file, or you compile this
+ * file and link it with other works to produce a work based on this
+ * file, this file does not by itself cause the resulting work to be
+ * covered by the GNU General Public License. However the source code
+ * for this file must still be made available in accordance with
+ * section (3) of the GNU General Public License.
+ * 
+ * This exception does not invalidate any other reasons why a work
+ * based on this file might be covered by the GNU General Public
+ * License.
+ *
+ * -------------------------------------------
+ * ####ECOSGPLCOPYRIGHTEND####
+ * =================================================================
+ * #####DESCRIPTIONBEGIN####
+ * 
+ *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
+ *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
+ *                Lars Povlsen    (lpovlsen@vitesse.com)
+ *  Date:         2006-06-12
+ *  Purpose:      
+ *  Description:  
+ *               
+ * ####DESCRIPTIONEND####
+ * 
+ * =================================================================
+ */
+
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <cyg/kernel/kapi.h>           // Kernel API.
+#include <cyg/infra/diag.h>            // For diagnostic printing.
+#include <network.h>
+#include <time.h>
+
+#include <cyg/hal/hal_tables.h>
+#include <cyg/fileio/fileio.h>
+#include <stdio.h>                     // sprintf().
+#include <stdlib.h>
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER
+#include <cyg/objloader/elf.h>
+#include <cyg/objloader/objelf.h>
+#endif
+
+#include <cyg/athttpd/http.h>
+#include <cyg/athttpd/socket.h>
+#include <cyg/athttpd/handler.h>
+#include <cyg/athttpd/forms.h>
+
+cyg_int32 debug_print = 0;
+
+const char *day_of_week[7] = {"Sun", "Mon", "Tue", "Wed", 
+                                 "Thu", "Fri", "Sat"};
+const char *month_of_year[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 
+                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+const char *home_pages[] = {"index.html",   "index.htm",
+                            "default.html", "home.html"};
+CYG_HAL_TABLE_BEGIN(cyg_httpd_mime_table, httpd_mime_table);
+CYG_HAL_TABLE_END(cyg_httpd_mime_table_end, httpd_mime_table);
+
+__externC cyg_httpd_mime_table_entry cyg_httpd_mime_table[];
+__externC cyg_httpd_mime_table_entry cyg_httpd_mime_table_end[];
+
+// Standard handlers added by default. Correspond to the most used extensions.
+// The user can add his/her own later.
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_htm_entry, "htm",
+                                       "text/html; charset=iso-8859-1");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_html_entry, "html", 
+                                       "text/html; charset=iso-8859-1");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_gif_entry, "gif", "image/gif");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_jpg_entry, "jpg", "image/jpg");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_png_entry, "png", "image/png");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_css_entry, "css", "text/css");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_js_entry, "js", "application/x-javascript");
+
+void 
+cyg_httpd_send_error(cyg_int32 err_type)
+{
+    httpstate.status_code = err_type;
+    
+    // Errors pages close the socket and are never cached.
+    httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
+
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    diag_printf("Sending error: %d\n", err_type);
+#endif    
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+    // Check if the user has defines his own error pages.
+    struct stat sp;
+    char file_name[CYG_HTTPD_MAXPATH];
+    strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
+    if (file_name[strlen(file_name)-1] != '/')
+        strcat(file_name, "/");
+    strcat(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ERRORDIR);
+    if (file_name[strlen(file_name)-1] != '/')
+        strcat(file_name, "/");
+    sprintf(file_name + strlen(file_name), "error_%d.html", err_type);
+    cyg_httpd_cleanup_filename(file_name);
+    cyg_int32 rc = stat(file_name, &sp);
+    if (rc == 0)
+    {
+        char *extension = rindex(file_name, '.');
+        if (extension == NULL)
+            // No extension in the file name.
+            httpstate.mime_type = 0;
+        else
+            httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
+
+        httpstate.payload_len  = sp.st_size;
+        cyg_int32 header_length = cyg_httpd_format_header();
+        cyg_httpd_write(httpstate.outbuffer, header_length);
+    
+        // File found.
+        FILE *fp = fopen(file_name, "r");
+        if(fp == NULL)
+            return;
+
+        ssize_t payload_size = fread(httpstate.outbuffer, 
+                                     1, 
+                                     CYG_HTTPD_MAXOUTBUFFER, 
+                                     fp);
+        while (payload_size > 0)
+        {
+            ssize_t bytes_written = cyg_httpd_write_chunked(httpstate.outbuffer, 
+                                                            payload_size);
+            if (bytes_written != payload_size)
+                break;
+
+            payload_size = fread(httpstate.outbuffer, 
+                                 1, 
+                                 CYG_HTTPD_MAXOUTBUFFER, 
+                                 fp);
+        }
+
+        fclose(fp);
+        return;
+    }
+#endif    
+
+    // Because the size of the frame is not known upfront (every error message
+    //  is different and thus has different length) we use chunked frames to
+    //  send the message out.
+#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
+    httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+#endif
+    
+    httpstate.mode |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
+    httpstate.status_code = err_type;
+    httpstate.last_modified = -1;
+    httpstate.mime_type = "text/html";
+    cyg_int32 header_length = cyg_httpd_format_header();
+    cyg_httpd_write(httpstate.outbuffer, header_length);
+    
+    // If no file has been defined, send a simple notification. We must use
+    //  chunked frames, because we do not know upfron the length of the
+    //  packet we have to send.
+    strcpy(httpstate.outbuffer,
+           "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n");
+    switch (err_type)
+    {
+    case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
+        strcat(httpstate.outbuffer,
+               "<html><head><title>301 Moved Permanently</title></head>\r\n"
+               "<body><h1>Moved Permanently</h1>\r\n"
+               "<p>The document has moved <a href=\"");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\">here</a>.\r\n");
+        break;
+    case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>302 Found</title></head>\r\n"
+               "<body><h1>Redirect</h1>\r\n"
+               "<p>Please continue <a href=\"");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\">here</a>.\r\n");
+        break;
+    case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>401 Not Authorized</title></head>\r\n");
+        strcat(httpstate.outbuffer, 
+               "<body><p>Authorization required to access this URL.</p>\r\n");
+        break;    
+    case CYG_HTTPD_STATUS_NOT_MODIFIED:
+        cyg_httpd_end_chunked();
+        return;
+    case CYG_HTTPD_STATUS_NOT_FOUND:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>404 Not Found</title></head>\r\n");
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "<p>The requested URL: %s was not found on this server</p>\r\n",
+                httpstate.url);
+        break;
+    case CYG_HTTPD_STATUS_SYSTEM_ERROR:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>500 Server Error</title></head>\r\n");
+        strcat(httpstate.outbuffer, 
+               "<p>The server encountered an unexpected condition that "
+               "prevented it from fulfilling the request"
+               " by the client</p>\r\n");
+        break;
+    case CYG_HTTPD_STATUS_NOT_IMPLEMENTED:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>501 Not Implemented</title></head>\r\n");
+        strcat(httpstate.outbuffer, 
+               "<p>The method requested is not implemented</p>\r\n");
+        break;
+    default:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>400 Bad Request</title></head>\r\n");
+        strcat(httpstate.outbuffer, 
+               "<p>Bad request</p>\r\n");
+        break;
+    }
+    
+    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+            "<hr>%s at %d.%d.%d.%d Port %d\r\n</body></html>\r\n",
+            CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID,
+            httpstate.host[0],
+            httpstate.host[1],
+            httpstate.host[2],
+            httpstate.host[3],
+            CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
+    
+    cyg_httpd_write_chunked(httpstate.outbuffer, strlen(httpstate.outbuffer));
+    cyg_httpd_end_chunked();
+}
+
+// Return a time_t that is always UTC (aka GMT).
+time_t
+cyg_httpd_parse_date(char *time)
+{
+    int    i;
+    char   month[4];
+    struct tm tm_mod;
+
+    // We are going to get rid of the day of the week. This is always the first
+    //  part of the string, separated by a blank.
+    time = strchr( time, ' ');
+    if ( time == NULL)
+        return 0;
+    time++;
+
+    /// RFC1123. The date is in the format: Sun, 06 Nov 1994 08:49:37 GMT.
+    cyg_int32 rc = sscanf(time,
+                          "%2d %3s %4d %2d:%2d:%2d GMT",
+                          &tm_mod.tm_mday,
+                          month,
+                          &tm_mod.tm_year,
+                          &tm_mod.tm_hour,
+                          &tm_mod.tm_min,
+                          &tm_mod.tm_sec);
+    if (rc != 6)
+    {
+        // RFC1036. The date is in the format: Sunday, 06-Nov-94 08:49:37 GMT.
+        rc = sscanf(time,
+                    "%2d-%3s-%2d %2d:%2d:%2d GMT",
+                    &tm_mod.tm_mday,
+                    month,
+                    &tm_mod.tm_year,
+                    &tm_mod.tm_hour,
+                    &tm_mod.tm_min,
+                    &tm_mod.tm_sec);
+        if (rc != 6)
+        {
+            // asctime().
+            rc = sscanf(time,"%3s %2d %2d:%2d:%2d %4d",
+                        month,
+                        &tm_mod.tm_mday,
+                        &tm_mod.tm_hour,
+                        &tm_mod.tm_min,
+                        &tm_mod.tm_sec,
+                        &tm_mod.tm_year);
+            if (rc != 6)
+                return 0;
+        }
+    }
+
+    for (i = 0; i < sizeof(month_of_year); i++)
+        if (strcmp(month, month_of_year[i]) == 0)
+        {
+            tm_mod.tm_mon = i;
+            if (tm_mod.tm_year > 1900)
+                tm_mod.tm_year -= 1900;
+            return mktime(&tm_mod);
+        }
+
+    return 0;
+}
+
+// Finds the mime string into the mime_table associated with a specific 
+//  extension. Returns the MIME type to send in the header, or NULL if the
+//  extension is not in the table.
+char*
+cyg_httpd_find_mime_string(char *ext)
+{
+    cyg_httpd_mime_table_entry *entry = cyg_httpd_mime_table;
+
+    while (entry != cyg_httpd_mime_table_end)
+    {
+        if (!strcmp((const char*)ext, entry->extension))
+            return entry->mime_string;
+        entry++;
+    }
+            
+    return (char*)0;
+}
+
+void
+cyg_httpd_cleanup_filename(char *filename)
+{
+    char *src = strstr(filename, "//");
+    while (src != 0)
+    {
+        strcpy(src + 1, src + 2);
+        src = strstr(filename, "//");
+    }
+
+    src = strstr(filename, "/./");
+    while (src != 0)
+    {
+        strcpy(src + 1, src + 3);
+        src = strstr(filename, "/./");
+    }
+
+    src = strstr(filename, "/../");
+    while (src != 0)
+    {
+        char *comp1 = filename, *comp2 = filename;
+
+        // Search the path component before this redirection.
+        while ((comp1 = strchr(comp1, '/')) != src)
+            comp2 = ++comp1;
+
+        strcpy(comp2, src + 4);
+        src = strstr(filename, "/../");
+    }
+}
+
+cyg_int32
+cyg_httpd_initialize(void)
+{
+    httpstate.post_data = NULL;
+    httpstate.needs_auth = (cyg_httpd_auth_table_entry *)0;
+    return 0;
+}
+
+void
+cyg_httpd_append_homepage(char *root)
+{
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+    struct stat sp;
+    cyg_int32 i;
+
+    cyg_int32 root_len = strlen(root);
+    for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
+    {
+        root[root_len] = '\0';
+        sprintf(root + root_len, "%s", home_pages[i]);
+        cyg_int32 rc = stat(root, &sp);
+        if (rc == 0)
+            return;
+    }
+    root[root_len] = 0;
+#endif    
+    
+#ifdef CYGDAT_NET_ATHTTPD_ALTERNATE_HOME    
+    if (strcmp(root, "/") == 0)
+        // The client is trying to open the main index file.
+        strcat(root, CYGDAT_NET_ATHTTPD_ALTERNATE_HOME);
+#endif    
+}
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+void
+cyg_httpd_send_file(char *name)
+{
+    cyg_int32  err;
+    FILE      *fp;
+    struct stat sp;
+    char       file_name[CYG_HTTPD_MAXPATH];
+
+    strcpy(file_name, CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR);
+    if (file_name[strlen(file_name)-1] != '/')
+        strcat(file_name, "/");
+    strcat(file_name, name);
+    cyg_httpd_cleanup_filename(file_name);
+        
+    // Check if the file is in the file system. This will also give us the
+    //  size of the file, to be used in the HTTP header.
+    cyg_int32 rc = stat(file_name, &sp);
+    if (rc < 0)
+    {
+        // Before giving up, we make a last ditch attempt at finding a file
+        //  within the internal resources of the server. The user can add
+        //  his/her own files to the table.
+        cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(name);
+        if (p != NULL)
+        {
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+            diag_printf("Sending Internal Resource: %s\n", name);
+#endif    
+            cyg_httpd_send_ires(p);
+        }    
+        else    
+            cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+        return;
+    }
+    
+    if (S_ISDIR(sp.st_mode))
+    {
+        char tmp_url[CYG_HTTPD_MAXURL];
+        strcpy(tmp_url, httpstate.url);
+        // Directories need a trialing slash, and if missing, we'll redirect
+        //  the client to the right URL. This is called (appropriately
+        //  enough) "Trailing-Slash Redirection". 
+        if (name[strlen(name)-1] != '/')
+        {
+            sprintf(httpstate.url,
+                    "http://%d.%d.%d.%d:%d%s/",
+                    httpstate.host[0],
+                    httpstate.host[1],
+                    httpstate.host[2],
+                    httpstate.host[3],
+                    CYGNUM_NET_ATHTTPD_SERVEROPT_PORT,
+                    tmp_url);
+            cyg_httpd_send_error(CYG_HTTPD_STATUS_MOVED_PERMANENTLY);
+            return;
+        }
+
+        // We are going to try to locate an index page in the directory we got
+        //  in the URL. 
+        cyg_httpd_append_homepage(file_name);
+        if (file_name[strlen(file_name)-1] == '/')
+        {
+#ifdef CYGOPT_NET_ATHTTPD_USE_DIRLIST
+            // No home page found, we are sending a directory listing.
+            cyg_httpd_send_directory_listing(name);
+#else
+            cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+#endif
+            return;
+        }
+        stat(file_name, &sp);
+    }
+    
+    httpstate.last_modified = sp.st_mtime;
+
+    // Let's see if we luck out and can send a 304.
+    if ((httpstate.modified_since != -1) && 
+                   (httpstate.modified_since >= httpstate.last_modified))
+    {                   
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_MODIFIED);
+        return;
+    }    
+    else    
+        httpstate.status_code = CYG_HTTPD_STATUS_OK;
+
+    // Here we'll look for an extension to the file. Consider the case where
+    //  there might be more than one dot in the file name. We'll look for just
+    //  the last one, then we'll check the extension.
+    char *extension = rindex(file_name, '.');
+    if (extension == NULL)
+        httpstate.mime_type = 0;
+    else    
+        httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
+
+    httpstate.payload_len  = sp.st_size;
+    httpstate.mode &= ~CYG_HTTPD_MODE_NO_CACHE;
+    cyg_int32 payload_size = cyg_httpd_format_header();
+    if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0)
+    {                 
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+        diag_printf("Sending header only for URL: %s\n", file_name);
+#endif    
+        send(httpstate.sockets[httpstate.client_index].descriptor, 
+             httpstate.outbuffer, 
+             payload_size,
+             0);
+        return;
+    }
+
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    diag_printf("Sending file: %s\n", file_name);
+#endif    
+    fp = fopen(file_name, "r");
+    if (fp == NULL)
+    {
+        // We should really read errno and send messages accordingly...
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
+        return;
+    }
+    
+    // Fill up the rest of the buffer and send it out.
+    cyg_int32 bread = fread(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                            1, 
+                            CYG_HTTPD_MAXOUTBUFFER - payload_size,
+                            fp);
+    cyg_httpd_write(httpstate.outbuffer, payload_size + bread);
+
+    ssize_t bytes_written = 0;
+    sp.st_size -= bread;
+    while (bytes_written < sp.st_size)
+    {
+        bread = fread(httpstate.outbuffer, 1, CYG_HTTPD_MAXOUTBUFFER, fp);
+        bytes_written += cyg_httpd_write(httpstate.outbuffer, bread);
+    }    
+    
+    err = fclose(fp);
+    if (err < 0)
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
+}
+#endif
+
+cyg_int32
+cyg_httpd_format_header(void)
+{
+    sprintf(httpstate.outbuffer, "HTTP/1.1 %d", httpstate.status_code);
+    time_t time_val = time(NULL);
+    
+    // Error messages (i.e. with status other than OK, automatically add
+    //  the no-cache header.
+    switch (httpstate.status_code)
+    {
+    case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
+        strcat(httpstate.outbuffer, " Moved Permanently\r\n");
+        strcat(httpstate.outbuffer, "Location: ");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\r\n");
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "Content-Length: %d\r\n",
+                httpstate.payload_len);
+        break;
+    case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+        strcat(httpstate.outbuffer, " Found\r\n");
+        strcat(httpstate.outbuffer, "Location: ");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\r\n");
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "Content-Length: %d\r\n",
+                httpstate.payload_len);
+        break;
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+    case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
+        // A 401 error closes the connection right away.
+        httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+        strcat(httpstate.outbuffer, " Not Authorized\r\n");
+        
+        // Here we should set the proper header based on the authentication
+        //  required (httpstate.needs_authMode) but for now, with only
+        //  Basic Authentication supported, there is no need to do so.
+        if (httpstate.needs_auth->auth_mode == CYG_HTTPD_AUTH_BASIC)
+        {
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                    "WWW-Authenticate: Basic realm=\"%s\"\r\n",
+                    httpstate.needs_auth->auth_domainname);
+        }
+        else             
+        {
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                     "WWW-Authenticate: Digest realm=\"%s\", ",
+                     httpstate.needs_auth->auth_domainname);
+            strftime(cyg_httpd_md5_nonce, 
+                     33,
+                     TIME_FORMAT_RFC1123,
+                     gmtime(&time_val));
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                    "nonce=\"%s\", ", cyg_httpd_md5_nonce);
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                    "opaque=\"%s\", ", 
+                    CYG_HTTPD_MD5_AUTH_OPAQUE);
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                    "stale=false, algorithm=%s, qop=\"%s\"\r\n",
+                    CYG_HTTPD_MD5_AUTH_NAME,
+                    CYG_HTTPD_MD5_AUTH_QOP);
+        }
+        break;
+#endif
+    case CYG_HTTPD_STATUS_NOT_MODIFIED:
+        strcat(httpstate.outbuffer, " Not Modified\r\n");
+        break;
+    case CYG_HTTPD_STATUS_NOT_FOUND:
+        strcat(httpstate.outbuffer, " Not Found\r\n");
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "Content-Length: %d\r\n", 
+                httpstate.payload_len);
+        break;
+    case CYG_HTTPD_STATUS_METHOD_NOT_ALLOWED:
+        strcat(httpstate.outbuffer, " Method Not Allowed\r\n");
+        break;
+    default:
+        strcat(httpstate.outbuffer, " OK\r\n");
+        if ((httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED) == 0)
+            sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                    "Content-Length: %d\r\n", 
+                    httpstate.payload_len);
+        break;
+    }
+
+    strcat(httpstate.outbuffer, "Date: ");
+    strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+             CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+             TIME_FORMAT_RFC1123,
+             gmtime(&time_val));
+    strcat(httpstate.outbuffer, "\r\n");
+    
+    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+            "Server: %s\r\n", 
+            CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID);
+    
+    if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
+        strcat(httpstate.outbuffer, "Connection: close\r\n");
+    else
+        strcat(httpstate.outbuffer, "Connection: keep-alive\r\n");
+
+    // When we cannot find the appropriate MIME type, we'll send a default type.
+    if (httpstate.mime_type == 0)
+        httpstate.mime_type = CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE;
+    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+            "Content-Type: %s\r\n", 
+            httpstate.mime_type);
+
+    if (httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED)
+        strcat(httpstate.outbuffer, "Transfer-Encoding: chunked\r\n");
+
+    if (httpstate.mode & CYG_HTTPD_MODE_NO_CACHE)
+        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
+        
+    if (httpstate.last_modified != -1)
+    {
+        time_val = httpstate.last_modified;
+        strcat(httpstate.outbuffer, "Last-Modified: "); 
+        strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+                 CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+                 TIME_FORMAT_RFC1123,
+                 gmtime(&time_val));
+        strcat(httpstate.outbuffer, "\r\n");
+
+#if (CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME != 0)                 
+        time_val += CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME;
+        strcat(httpstate.outbuffer, "Expires: "); 
+        strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+                 CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+                 TIME_FORMAT_RFC1123,
+                 gmtime(&time_val));
+        strcat(httpstate.outbuffer, "\r\n");
+#endif
+    }        
+                 
+    // There must be 2 carriage returns between the header and the body, 
+    //  so if you modify this function make sure that there is another 
+    //  CRLF already terminating the buffer thus far.
+    strcat(httpstate.outbuffer, "\r\n");
+    return strlen(httpstate.outbuffer);
+}
+
+void
+cyg_httpd_handle_method_GET(void)
+{
+#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) ||\
+                             defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL)
+    // If the URL is a CGI script, there is a different directory...
+    if (httpstate.url[0] == '/' &&
+                    !strncmp(httpstate.url + 1, 
+                              CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR, 
+                              strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
+    {                              
+        cyg_httpd_exec_cgi();
+        return;
+    }
+    // If the OBJLOADER package is not loaded, then the request for a library
+    //  will likely generate a 404.
+#endif    
+
+    // User defined handlers take precedence over other forms of response.
+    handler h = cyg_httpd_find_handler();
+    if (h != 0)
+    {
+        h(&httpstate);
+        return;
+    }
+    
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_FS
+    // No handler, we'll redirect to the file system.
+    cyg_httpd_send_file(httpstate.url);
+#else
+    // If we do not have a file system, we look for the file within the 
+    //  internal resources of the server. The user can add his/her own files
+    //  to the table.
+    if (strcmp(httpstate.url, "/") == 0)
+    {
+        int i;
+        cyg_httpd_ires_table_entry *p;
+        for (i = 0; i < sizeof(home_pages)/sizeof(char*); i++)
+        {
+            httpstate.url[1] = '\0';
+            strcat(httpstate.url, home_pages[i]);
+            p = cyg_httpd_find_ires(httpstate.url);
+            if (p != NULL)
+            {
+                cyg_httpd_send_ires(p);
+                return;
+            }    
+        }        
+    }
+    else
+    {
+        cyg_httpd_ires_table_entry *p = cyg_httpd_find_ires(httpstate.url);
+        if (p != NULL)
+        {
+            cyg_httpd_send_ires(p);
+            return;
+        }    
+    }        
+    cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+#endif    
+}
+
+char*
+cyg_httpd_get_URL(char* p)
+{
+    char* dest = httpstate.url;
+
+    // First get rid of multiple leading slashes.
+    while ((p[0] == '/') && (p[1] == '/'))
+       p++;
+
+    // Store the url, and check if there is a form result in it.
+    while ((*p != ' ') && (*p != '?') &&
+            ((dest - httpstate.url) <= CYG_HTTPD_MAXURL))
+    {
+        // Look for encoded characters in the URL.
+        if (*p == '%') 
+        {
+            p++;
+            cyg_int8 ch = cyg_httpd_from_hex(*p++);
+            if (ch == -1)
+            {
+                cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+                return (char*)0;
+            }
+            *dest = ch << 4;
+            ch = cyg_httpd_from_hex(*p++);
+            if (ch == -1)
+            {
+                cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+                return (char*)0;
+            }
+            *dest += ch;
+            dest++;
+        }
+        else 
+            *dest++ = *p++;
+    }
+
+    // Terminate the file name...
+    *dest = '\0';
+
+    // The URL must start with a leading slash.
+    if (httpstate.url[0] != '/') 
+    {
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+        return (char*)0;
+    }
+    return p;
+}
+
+char*
+cyg_httpd_parse_POST(char* p)
+{
+    httpstate.method = CYG_HTTPD_METHOD_POST;
+    char *cp = cyg_httpd_get_URL(p);
+    if (cp == 0)
+        return (char*)0;
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    diag_printf("POST Request URL: %s\n", httpstate.url);
+#endif    
+
+    while (*cp++ != '\n');
+    return cp;
+}
+
+char*
+cyg_httpd_parse_GET(char* p)
+{
+    char *cp = cyg_httpd_get_URL(p);
+    if (cp == 0)
+        return 0;
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    if ( httpstate.method == CYG_HTTPD_METHOD_GET)
+        diag_printf("GET Request URL: %s\n", httpstate.url);
+    else    
+        diag_printf("HEAD Request URL: %s\n", httpstate.url);
+#endif    
+
+    if (*cp == '?')
+        // If we have a GET header with form variables we'll get the
+        //  variables out of it and store them in the variable table.
+        // Can we assume that HEAD request can have form variables?
+        // That will be a yes until I learn otherwise.
+        cp = cyg_httpd_store_form_data(++cp);
+
+    // Run to end of line.
+    while (*cp++ != '\n');
+    return cp;
+}
+
+char*
+cyg_httpd_process_header(char *p)
+{
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+    // Clear the previous request's response. The client properly authenticated
+    //  will always reinitialize this variable during the header parsing
+    //  process. This variable is also commandeered to hold the hashed
+    //  username:password duo in the basic authentication.
+    cyg_httpd_md5_response[0] = '\0';
+#endif
+
+    // The deafult for HTTP 1.1 is keep-alive connections, unless specifically
+    //  closed by the far end.
+    httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA |\
+                                        CYG_HTTPD_MODE_SEND_HEADER_ONLY);
+    httpstate.modified_since = -1;
+    httpstate.content_len = 0;
+    while (p < httpstate.request_end)
+    {
+        if (strncasecmp("GET ", p, 4) == 0)
+        {
+            // We need separate flags for HEAD and SEND_HEADERS_ONLY since
+            //  we can send a header only even in the case of a GET request
+            //  (as a 304 response.)
+            httpstate.method = CYG_HTTPD_METHOD_GET;
+            httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+            p = cyg_httpd_parse_GET(p + 4);
+            if (p ==0)
+                return (char*)0;
+        }
+        else if (strncasecmp("POST ", p, 5) == 0)
+        {
+            p = cyg_httpd_parse_POST(p + 5);
+            if (p ==0)
+                return (char*)0;
+        }
+        else if (strncasecmp("HEAD ", p, 5) == 0)
+        {
+            httpstate.method = CYG_HTTPD_METHOD_HEAD;
+            httpstate.mode |= CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+            p = cyg_httpd_parse_GET(p + 5);
+            if (p ==0)
+                return (char*)0;
+        }
+        else if (strncasecmp(p, "Content-Length: ", 16) == 0)
+        {
+            p = strchr(p, ':') + 2;
+            if (p)
+                // In the case of a POST request, this is the total length of
+                //  the payload, which might be spread across several frames.
+                httpstate.content_len = atoi(p);
+            while (*p++ != '\n');
+        }
+        else if (strncasecmp(p, "Content-Type: ", 14) == 0)
+        {
+            p = strchr(p, ':') + 2;
+            if (p)
+                // In the case of a POST request, this is the total length of
+                //  the payload, which might be spread across several frames.
+                if (strncasecmp(p,
+                                "application/x-www-form-urlencoded",
+                                33) == 0)
+                    httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+            while (*p++ != '\n');
+        }
+        else if (strncasecmp("Host:", p, 5) == 0)
+        {
+            p += 5;
+            if (*p == ' ')
+                p++;
+            sscanf(p,
+                   "%d.%d.%d.%d",
+                   &httpstate.host[0],
+                   &httpstate.host[1],
+                   &httpstate.host[2],
+                   &httpstate.host[3]);
+            while (*p++ != '\n');
+        }
+        else if (strncasecmp("If-Modified-Since:", p, 18) == 0)
+        {
+            p += 18;
+            if ( *p == ' ')
+                p++;
+            httpstate.modified_since = cyg_httpd_parse_date(p);
+            while (*p++ != '\n');
+        }
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+        else if (strncasecmp("Authorization:", p, 14) == 0)
+        {
+            p += 14;
+            while (*p == ' ')
+                p++;
+            if (strncasecmp("Basic", p, 5) == 0)
+            {
+                p += 5;
+                while (*p == ' ')
+                    p++;
+                cyg_int32 auth_data_length = 0;    
+                while (*p != '\n') 
+                {
+                    // We are going to copy only up to 
+                    //  AUTH_STORAGE_BUFFER_LENGTH characters to prevent
+                    //  overflow of the cyg_httpd_md5_response variable.
+                    if (auth_data_length < AUTH_STORAGE_BUFFER_LENGTH)
+                        if ((*p != '\r') && (*p != ' '))
+                            cyg_httpd_md5_response[auth_data_length++] = *p;
+                    p++;
+                }    
+                p++;        
+                cyg_httpd_md5_response[auth_data_length] = '\0';
+            }
+            else if (strncasecmp(p, "Digest", 6) == 0)
+            {
+                p += 6;
+                while (*p == ' ')
+                   p++;
+                while (*p != '\n')
+                {
+                    if (strncasecmp(p, "realm=", 6) == 0)
+                        p = cyg_httpd_digest_skip(p + 6);
+                    else if (strncasecmp(p, "username=", 9) == 0)
+                        p = cyg_httpd_digest_skip(p + 9);
+                    else if (strncasecmp(p, "nonce=", 6) == 0)
+                        p = cyg_httpd_digest_skip(p + 6);
+                    else if (strncasecmp(p, "response=", 9) == 0)
+                        p = cyg_httpd_digest_data(cyg_httpd_md5_response, 
+                                                  p + 9);
+                    else if (strncasecmp(p, "cnonce=", 7) == 0)
+                        p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7);
+                    else if (strncasecmp(p, "qop=", 4) == 0)
+                        p = cyg_httpd_digest_skip(p + 4);
+                    else if (strncasecmp(p, "nc=", 3) == 0)
+                        p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount, 
+                                                  p + 3);
+                    else if (strncasecmp(p, "algorithm=", 10) == 0)
+                        p = cyg_httpd_digest_skip(p + 10);
+                    else if (strncasecmp(p, "opaque=", 7) == 0)
+                        p = cyg_httpd_digest_skip(p + 7);
+                    else if (strncasecmp(p, "uri=", 4) == 0)
+                        p = cyg_httpd_digest_skip(p + 4);
+                    else
+                        p++;    
+                }
+                p++;
+            }    
+            else
+                while (*p++ != '\n');
+        }   
+#endif // CYGOPT_NET_ATHTTPD_USE_AUTH
+        else if (strncasecmp(p, "Connection:", 11) == 0)
+        {
+            p += 11;
+            while (*p == ' ')
+               p++;
+            if (strncasecmp(p, "close", 5) == 0)
+                httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+            while (*p++ != '\n');
+        }
+        else
+            // We'll just dump the rest of the line and move on to the next.
+            while (*p++ != '\n');
+    }
+    return p;
+}
+
+void
+cyg_httpd_process_method(void)
+{
+    char* p = httpstate.inbuffer;
+    
+    // Some browsers send an extra '\r\n' after the POST data that is not
+    //  accounted in the "Content-Length:" field. We are going to junk all
+    //  the leading returns and line carriages we find.
+    while ((*p == '\r') || (*p =='\n'))
+        p++;
+
+    while (*p != '\0')
+    {
+        p = cyg_httpd_process_header(p);
+        if (p == 0)
+            return;
+
+#ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
+        // Let's check that the requested URL is not inside some directory that 
+        //  needs authentication.
+        cyg_httpd_auth_table_entry* auth = 
+                                  cyg_httpd_is_authenticated(httpstate.url);
+        if (auth != 0)
+        {
+            cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_AUTHORIZED);
+            return;
+        }
+#endif
+        switch (httpstate.method)
+        {
+            case CYG_HTTPD_METHOD_GET:
+            case CYG_HTTPD_METHOD_HEAD:
+                cyg_httpd_handle_method_GET();
+                break;
+            case CYG_HTTPD_METHOD_POST:
+                cyg_httpd_handle_method_POST();
+                return;
+                break;
+            default:
+                cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_IMPLEMENTED);
+                return;
+            break;
+        }
+    }    
+}
+