httpd shrink and logging update, part 5 of 7
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 17 Aug 2007 19:20:07 +0000 (19:20 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 17 Aug 2007 19:20:07 +0000 (19:20 -0000)
   text    data     bss     dec     hex filename
   9836       0       0    9836    266c busybox.t1/networking/httpd.o.orig
   9724       0       0    9724    25fc busybox.t2/networking/httpd.o
   9657       0       0    9657    25b9 busybox.t3/networking/httpd.o
   9342       0       0    9342    247e busybox.t4/networking/httpd.o
   9342       0       0    9342    247e busybox.t5/networking/httpd.o
   9262       0       0    9262    242e busybox.t6/networking/httpd.o
   9283       0       0    9283    2443 busybox.t7/networking/httpd.o
   9334       0       0    9334    2476 busybox.t8/networking/httpd.o

networking/httpd.c

index 69d994a47991d0f3bec129ac4a9f77a2a540c4a9..81660b2e67814eff4b4d7235eaa3e8cea0c112d7 100644 (file)
@@ -20,7 +20,7 @@
  * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
  *
  *
- * When a url contains "cgi-bin" it is assumed to be a cgi script.  The
+ * When a url starts by "/cgi-bin/" it is assumed to be a cgi script.  The
  * server changes directory to the location of the script and executes it
  * after setting QUERY_STRING and other environment variables.
  *
  * root configuration file.  If -c is set and the file is not found, the
  * server exits with an error.
  *
-*/
+ */
 
 #include "libbb.h"
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
 #include <sys/sendfile.h>
 #endif
 
+//#define DEBUG 1
+#define DEBUG 0
+
 /* amount of buffering in a pipe */
 #ifndef PIPE_BUF
 # define PIPE_BUF 4096
 #endif
 
-static const char default_path_httpd_conf[] ALIGN1 = "/etc";
-static const char httpd_conf[] ALIGN1 = "httpd.conf";
-
-#define TIMEOUT 60
+#define MAX_MEMORY_BUF 8192    /* IO buffer */
 
-// Note: busybox xfuncs are not used because we want the server to keep running
-// if something bad happens due to a malformed user request.
-// As a result, all memory allocations after daemonize
-// are checked rigorously
+#define HEADER_READ_TIMEOUT 60
 
-//#define DEBUG 1
-#define DEBUG 0
+static const char default_path_httpd_conf[] ALIGN1 = "/etc";
+static const char httpd_conf[] ALIGN1 = "httpd.conf";
 
-#define MAX_MEMORY_BUF 8192    /* IO buffer */
+typedef struct has_next_ptr {
+       struct has_next_ptr *next;
+} has_next_ptr;
 
+/* Must have "next" as a first member */
 typedef struct Htaccess {
-       char *after_colon;
        struct Htaccess *next;
+       char *after_colon;
        char before_colon[1];  /* really bigger, must be last */
 } Htaccess;
 
+/* Must have "next" as a first member */
 typedef struct Htaccess_IP {
+       struct Htaccess_IP *next;
        unsigned ip;
        unsigned mask;
        int allow_deny;
-       struct Htaccess_IP *next;
 } Htaccess_IP;
 
 struct globals {
@@ -199,30 +200,6 @@ struct globals {
        ContentLength = -1; \
 } while (0)
 
-static const char request_GET[] ALIGN1 = "GET";    /* size algorithmic optimize */
-
-static const char *const suffixTable[] = {
-/* Warning: shorter equivalent suffix in one line must be first */
-       ".htm.html", "text/html",
-       ".jpg.jpeg", "image/jpeg",
-       ".gif", "image/gif",
-       ".png", "image/png",
-       ".txt.h.c.cc.cpp", "text/plain",
-       ".css", "text/css",
-       ".wav", "audio/wav",
-       ".avi", "video/x-msvideo",
-       ".qt.mov", "video/quicktime",
-       ".mpe.mpeg", "video/mpeg",
-       ".mid.midi", "audio/midi",
-       ".mp3", "audio/mpeg",
-#if 0                        /* unpopular */
-       ".au", "audio/basic",
-       ".pac", "application/x-ns-proxy-autoconfig",
-       ".vrml.wrl", "model/vrml",
-#endif
-       0, "application/octet-stream" /* default */
-};
-
 typedef enum {
        HTTP_OK = 200,
        HTTP_MOVED_TEMPORARILY = 302,
@@ -285,11 +262,32 @@ static const HttpEnumString httpResponseNames[] = {
 };
 
 
-static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
 
+static void free_llist(has_next_ptr **pptr)
+{
+       has_next_ptr *cur = *pptr;
+       while (cur) {
+               has_next_ptr *t = cur;
+               cur = cur->next;
+               free(t);
+       }
+       *pptr = NULL;
+}
 
-#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
+ || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
+{
+       free_llist((has_next_ptr**)pptr);
+}
+#endif
 
+static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
+{
+       free_llist((has_next_ptr**)pptr);
+}
 
 static int scan_ip(const char **ep, unsigned *ip, unsigned char endc)
 {
@@ -365,33 +363,8 @@ static int scan_ip_mask(const char *ipm, unsigned *ip, unsigned *mask)
        return 0;
 }
 
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
- || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
- || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-static void free_config_lines(Htaccess **pprev)
-{
-       Htaccess *prev = *pprev;
-
-       while (prev) {
-               Htaccess *cur = prev;
-
-               prev = cur->next;
-               free(cur);
-       }
-       *pprev = NULL;
-}
-#endif
-
-/* flag */
-#define FIRST_PARSE          0
-#define SUBDIR_PARSE         1
-#define SIGNALED_PARSE       2
-#define FIND_FROM_HTTPD_ROOT 3
-/****************************************************************************
- *
- > $Function: parse_conf()
- *
- * $Description: parse configuration file into in-memory linked list.
+/*
+ * Parse configuration file into in-memory linked list.
  *
  * The first non-white character is examined to determine if the config line
  * is one of the following:
@@ -404,14 +377,14 @@ static void free_config_lines(Htaccess **pprev)
  * are also discarded.  That is, previous settings are retained if flag is
  * SUBDIR_PARSE.
  *
- * $Parameters:
- *      (const char *) path . . null for ip address checks, path for password
- *                              checks.
- *      (int) flag  . . . . . . the source of the parse request.
- *
- * $Return: (None)
- *
- ****************************************************************************/
+ * path   Path where to look for httpd.conf (without filename).
+ * flag   Type of the parse request.
+ */
+/* flag */
+#define FIRST_PARSE          0
+#define SUBDIR_PARSE         1
+#define SIGNALED_PARSE       2
+#define FIND_FROM_HTTPD_ROOT 3
 static void parse_conf(const char *path, int flag)
 {
        FILE *f;
@@ -423,38 +396,28 @@ static void parse_conf(const char *path, int flag)
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
        Htaccess *cur;
 #endif
-
        const char *cf = configFile;
        char buf[160];
        char *p0 = NULL;
        char *c, *p;
+       Htaccess_IP *pip;
 
-       /* free previous ip setup if present */
-       Htaccess_IP *pip = ip_a_d;
-
-       while (pip) {
-               Htaccess_IP *cur_ipl = pip;
-
-               pip = cur_ipl->next;
-               free(cur_ipl);
-       }
-       ip_a_d = NULL;
-
+       /* discard old rules */
+       free_Htaccess_IP_list(&ip_a_d);
        flg_deny_all = 0;
-
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \
  || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
        /* retain previous auth and mime config only for subdir parse */
        if (flag != SUBDIR_PARSE) {
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-               free_config_lines(&g_auth);
+               free_Htaccess_list(&g_auth);
 #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-               free_config_lines(&mime_a);
+               free_Htaccess_list(&mime_a);
 #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-               free_config_lines(&script_i);
+               free_Htaccess_list(&script_i);
 #endif
        }
 #endif
@@ -463,7 +426,7 @@ static void parse_conf(const char *path, int flag)
                cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
                if (cf == NULL) {
                        if (flag == FIRST_PARSE)
-                       bb_error_msg_and_die(bb_msg_memory_exhausted);
+                               bb_error_msg_and_die(bb_msg_memory_exhausted);
                        return;
                }
                sprintf((char *)cf, "%s/%s", path, httpd_conf);
@@ -486,21 +449,21 @@ static void parse_conf(const char *path, int flag)
        /* This could stand some work */
        while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) {
                c = NULL;
-               for (p = p0; *p0 != 0 && *p0 != '#'; p0++) {
+               for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) {
                        if (!isspace(*p0)) {
                                *p++ = *p0;
                                if (*p0 == ':' && c == NULL)
-                               c = p;
+                                       c = p;
                        }
                }
-               *p = 0;
+               *p = '\0';
 
                /* test for empty or strange line */
-               if (c == NULL || *c == 0)
+               if (c == NULL || *c == '\0')
                        continue;
                p0 = buf;
                if (*p0 == 'd')
-                               *p0 = 'D';
+                       *p0 = 'D';
                if (*c == '*') {
                        if (*p0 == 'D') {
                                /* memorize deny all */
@@ -535,7 +498,7 @@ static void parse_conf(const char *path, int flag)
                                }
                                pip->allow_deny = *p0;
                                if (*p0 == 'D') {
-                                       /* Deny:form_IP move top */
+                                       /* Deny:from_IP move top */
                                        pip->next = ip_a_d;
                                        ip_a_d = pip;
                                } else {
@@ -555,12 +518,12 @@ static void parse_conf(const char *path, int flag)
                }
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
                if (*p0 == '/') {
-                       /* make full path from httpd root / curent_path / config_line_path */
-                       cf = flag == SUBDIR_PARSE ? path : "";
+                       /* make full path from httpd root / current_path / config_line_path */
+                       cf = (flag == SUBDIR_PARSE ? path : "");
                        p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
                        if (p0 == NULL)
                                continue;
-                       c[-1] = 0;
+                       c[-1] = '\0';
                        sprintf(p0, "/%s%s", cf, buf);
 
                        /* another call bb_simplify_path */
@@ -571,9 +534,9 @@ static void parse_conf(const char *path, int flag)
                                        if (*cf == '/') {    /* skip duplicate (or initial) slash */
                                                continue;
                                        } else if (*cf == '.') {
-                                               if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
+                                               if (cf[1] == '/' || cf[1] == '\0') { /* remove extra '.' */
                                                        continue;
-                                               } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
+                                               } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == '\0')) {
                                                        ++cf;
                                                        if (p > p0) {
                                                                while (*--p != '/') /* omit previous dir */;
@@ -588,7 +551,7 @@ static void parse_conf(const char *path, int flag)
                        if ((p == p0) || (*p != '/')) {      /* not a trailing slash */
                                ++p;                             /* so keep last character */
                        }
-                       *p = 0;
+                       *p = '\0';
                        sprintf(p0, "%s:%s", p0, c);
                }
 #endif
@@ -658,23 +621,14 @@ static void parse_conf(const char *path, int flag)
 }
 
 #if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
-/****************************************************************************
- *
- > $Function: encodeString()
- *
- * $Description: Given a string, html encode special characters.
- *   This is used for the -e command line option to provide an easy way
- *   for scripts to encode result data without confusing browsers.  The
- *   returned string pointer is memory allocated by malloc().
- *
- * $Parameters:
- *      (const char *) string . . The first string to encode.
- *
- * $Return: (char *) . . . .. . . A pointer to the encoded string.
- *
- * $Errors: Returns a null string ("") if memory is not available.
+/*
+ * Given a string, html-encode special characters.
+ * This is used for the -e command line option to provide an easy way
+ * for scripts to encode result data without confusing browsers.  The
+ * returned string pointer is memory allocated by malloc().
  *
- ****************************************************************************/
+ * Returns a pointer to the encoded string (malloced).
+ */
 static char *encodeString(const char *string)
 {
        /* take the simple route and encode everything */
@@ -694,25 +648,18 @@ static char *encodeString(const char *string)
 }
 #endif          /* FEATURE_HTTPD_ENCODE_URL_STR */
 
-/****************************************************************************
- *
- > $Function: decodeString()
- *
- * $Description: Given a URL encoded string, convert it to plain ascii.
- *   Since decoding always makes strings smaller, the decode is done in-place.
- *   Thus, callers should strdup() the argument if they do not want the
- *   argument modified.  The return is the original pointer, allowing this
- *   function to be easily used as arguments to other functions.
- *
- * $Parameters:
- *      (char *) string . . . The first string to decode.
- *      (int)    option_d . . 1 if called for httpd -d
- *
- * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
+/*
+ * Given a URL encoded string, convert it to plain ascii.
+ * Since decoding always makes strings smaller, the decode is done in-place.
+ * Thus, callers should strdup() the argument if they do not want the
+ * argument modified.  The return is the original pointer, allowing this
+ * function to be easily used as arguments to other functions.
  *
- * $Errors: None
+ * string    The first string to decode.
+ * option_d  1 if called for httpd -d
  *
- ****************************************************************************/
+ * Returns a pointer to the decoded string (same as input).
+ */
 static char *decodeString(char *orig, int option_d)
 {
        /* note that decoded string is always shorter than original */
@@ -754,9 +701,9 @@ static char *decodeString(char *orig, int option_d)
 
 
 #if ENABLE_FEATURE_HTTPD_CGI
-/****************************************************************************
+/*
  * setenv helpers
- ****************************************************************************/
+ */
 static void setenv1(const char *name, const char *value)
 {
        if (!value)
@@ -773,10 +720,10 @@ static void setenv_long(const char *name, long value)
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 /*
- * Decode a base 64 data stream as per rfc1521.
+ * Decode a base64 data stream as per rfc1521.
  * Note that the rfc states that non base64 chars are to be ignored.
- * Since the decode always results in a shorter size than the input, it is
- * OK to pass the input arg as an output arg.
+ * Since the decode always results in a shorter size than the input,
+ * it is OK to pass the input arg as an output arg.
  * Parameter: a pointer to a base64 encoded string.
  * Decoded data is stored in-place.
  */
@@ -818,18 +765,9 @@ static void decodeBase64(char *Data)
 }
 #endif
 
-
-/****************************************************************************
- *
- > $Function: openServer()
- *
- * $Description: create a listen server socket on the designated port.
- *
- * $Return: (int)  . . . A connection socket. -1 for errors.
- *
- * $Errors: None
- *
- ****************************************************************************/
+/*
+ * Create a listen server socket on the designated port.
+ */
 #if BB_MMU
 static int openServer(void)
 {
@@ -853,6 +791,8 @@ static int openServer(void)
  */
 static int sendHeaders(HttpResponseNum responseNum)
 {
+       static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
+
        const char *responseString = "";
        const char *infoString = NULL;
        const char *mime_type;
@@ -1318,6 +1258,28 @@ static void send_cgi_and_exit(
 static void send_file_and_exit(const char *url) ATTRIBUTE_NORETURN;
 static void send_file_and_exit(const char *url)
 {
+       static const char *const suffixTable[] = {
+       /* Warning: shorter equivalent suffix in one line must be first */
+               ".htm.html", "text/html",
+               ".jpg.jpeg", "image/jpeg",
+               ".gif",      "image/gif",
+               ".png",      "image/png",
+               ".txt.h.c.cc.cpp", "text/plain",
+               ".css",      "text/css",
+               ".wav",      "audio/wav",
+               ".avi",      "video/x-msvideo",
+               ".qt.mov",   "video/quicktime",
+               ".mpe.mpeg", "video/mpeg",
+               ".mid.midi", "audio/midi",
+               ".mp3",      "audio/mpeg",
+#if 0                        /* unpopular */
+               ".au",       "audio/basic",
+               ".pac",      "application/x-ns-proxy-autoconfig",
+               ".vrml.wrl", "model/vrml",
+#endif
+               NULL
+       };
+
        char *suffix;
        int f;
        int fd;
@@ -1330,25 +1292,31 @@ static void send_file_and_exit(const char *url)
 
        suffix = strrchr(url, '.');
 
-       for (table = suffixTable; *table; table += 2)
-               if (suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
-                       try_suffix += strlen(suffix);
-                       if (*try_suffix == 0 || *try_suffix == '.')
-                               break;
+       /* If not found, set default as "application/octet-stream";  */
+       found_mime_type = "application/octet-stream";
+       if (suffix) {
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+               Htaccess *cur;
+#endif
+               for (table = suffixTable; *table; table += 2) {
+                       try_suffix = strstr(table[0], suffix);
+                       if (try_suffix) {
+                               try_suffix += strlen(suffix);
+                               if (*try_suffix == '\0' || *try_suffix == '.') {
+                                       found_mime_type = table[1];
+                                       break;
+                               }
+                       }
                }
-       /* also, if not found, set default as "application/octet-stream";  */
-       found_mime_type = table[1];
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-       if (suffix) {
-               Htaccess * cur;
                for (cur = mime_a; cur; cur = cur->next) {
                        if (strcmp(cur->before_colon, suffix) == 0) {
                                found_mime_type = cur->after_colon;
                                break;
                        }
                }
+#endif
        }
-#endif  /* FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
 
        if (DEBUG)
                bb_error_msg("sending file '%s' content-type: %s",
@@ -1426,27 +1394,21 @@ static int checkPermIP(void)
        return !flg_deny_all;
 }
 
-/****************************************************************************
- *
- > $Function: checkPerm()
- *
- * $Description: Check the permission file for access password protected.
- *
- *   If config file isn't present, everything is allowed.
- *   Entries are of the form you can see example from header source
+/*
+ * Check the permission file for access password protected.
  *
- * $Parameters:
- *      (const char *) path  . . . . The file path.
- *      (const char *) request . . . User information to validate.
+ * If config file isn't present, everything is allowed.
+ * Entries are of the form you can see example from header source
  *
- * $Return: (int)  . . . . . . . . . 1 if request OK, 0 otherwise.
+ * path      The file path.
+ * request   User information to validate.
  *
- ****************************************************************************/
-
+ * Returns 1 if request is OK.
+ */
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 static int checkPerm(const char *path, const char *request)
 {
-       Htaccess * cur;
+       Htaccess *cur;
        const char *p;
        const char *p0;
 
@@ -1481,7 +1443,7 @@ static int checkPerm(const char *path, const char *request)
                                char *cipher;
                                char *pp;
 
-                               if (strncmp(p, request, u-request) != 0) {
+                               if (strncmp(p, request, u - request) != 0) {
                                        /* user uncompared */
                                        continue;
                                }
@@ -1528,15 +1490,17 @@ static void exit_on_signal(int sig)
 static void handle_incoming_and_exit(void) ATTRIBUTE_NORETURN;
 static void handle_incoming_and_exit(void)
 {
+       static const char request_GET[] ALIGN1 = "GET";
+
        char *url;
        char *purl;
        int count;
-       int blank = -1;
+       int http_major_version;
        char *test;
        struct stat sb;
        int ip_allowed;
 #if ENABLE_FEATURE_HTTPD_CGI
-       const char *prequest = request_GET;
+       const char *prequest;
        unsigned long length = 0;
        char *cookie = 0;
        char *content_type = 0;
@@ -1546,21 +1510,24 @@ static void handle_incoming_and_exit(void)
        int credentials = -1;  /* if not required this is Ok */
 #endif
 
+       /* Install timeout handler */
        sa.sa_handler = exit_on_signal;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0; /* no SA_RESTART */
        sigaction(SIGALRM, &sa, NULL);
+       alarm(HEADER_READ_TIMEOUT);
 
-       alarm(TIMEOUT);
        if (!get_line())
                _exit(0);  /* EOF or error or empty line */
 
+       /* Determine type of request (GET/POST) */
        purl = strpbrk(iobuf, " \t");
        if (purl == NULL) {
                send_headers_and_exit(HTTP_BAD_REQUEST);
        }
        *purl = '\0';
 #if ENABLE_FEATURE_HTTPD_CGI
+       prequest = request_GET;
        if (strcasecmp(iobuf, prequest) != 0) {
                prequest = "POST";
                if (strcasecmp(iobuf, prequest) != 0) {
@@ -1573,8 +1540,10 @@ static void handle_incoming_and_exit(void)
        }
 #endif
        *purl = ' ';
-       count = sscanf(purl, " %[^ ] HTTP/%d.%*d", iobuf, &blank);
 
+       /* Copy URL from after "GET "/"POST " to stack-allocated char[] */
+       http_major_version = -1;
+       count = sscanf(purl, " %[^ ] HTTP/%d.%*d", iobuf, &http_major_version);
        if (count < 1 || iobuf[0] != '/') {
                /* Garbled request/URL */
                send_headers_and_exit(HTTP_BAD_REQUEST);
@@ -1584,7 +1553,8 @@ static void handle_incoming_and_exit(void)
                send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
        }
        strcpy(url, iobuf);
-       /* extract url args if present */
+
+       /* Extract url args if present */
        test = strchr(url, '?');
        g_query = NULL;
        if (test) {
@@ -1592,6 +1562,7 @@ static void handle_incoming_and_exit(void)
                g_query = test;
        }
 
+       /* Decode URL escape sequences */
        test = decodeString(url, 0);
        if (test == NULL)
                send_headers_and_exit(HTTP_BAD_REQUEST);
@@ -1600,7 +1571,8 @@ static void handle_incoming_and_exit(void)
                send_headers_and_exit(HTTP_NOT_FOUND);
        }
 
-       /* algorithm stolen from libbb bb_simplify_path(),
+       /* Canonicalize path */
+       /* Algorithm stolen from libbb bb_simplify_path(),
         * but don't strdup and reducing trailing slash and protect out root */
        purl = test = url;
        do {
@@ -1631,13 +1603,14 @@ static void handle_incoming_and_exit(void)
        *++purl = '\0';       /* so keep last character */
        test = purl;          /* end ptr */
 
-       /* If URL is directory, adding '/' */
+       /* If URL is a directory, add '/' */
        if (test[-1] != '/') {
                if (is_directory(url + 1, 1, &sb)) {
                        found_moved_temporarily = url;
                }
        }
 
+       /* Log it */
        if (verbose > 1)
                bb_error_msg("url:%s", url);
 
@@ -1653,15 +1626,17 @@ static void handle_incoming_and_exit(void)
                }
                *test = '/';
        }
-       if (blank >= 0) {
-               /* read until blank line for HTTP version specified, else parse immediate */
+       if (http_major_version >= 0) {
+               /* Request was with "... HTTP/n.m", and n >= 0 */
+
+               /* Read until blank line for HTTP version specified, else parse immediate */
                while (1) {
-                       alarm(TIMEOUT);
+                       alarm(HEADER_READ_TIMEOUT);
                        if (!get_line())
                                break; /* EOF or error or empty line */
-
                        if (DEBUG)
                                bb_error_msg("header: '%s'", iobuf);
+
 #if ENABLE_FEATURE_HTTPD_CGI
                        /* try and do our best to parse more lines */
                        if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
@@ -1707,6 +1682,7 @@ static void handle_incoming_and_exit(void)
                } /* while extra header reading */
        }
 
+       /* We read everything, disable peer timeout */
        alarm(0);
 
        if (strcmp(bb_basename(url), httpd_conf) == 0 || ip_allowed == 0) {
@@ -1727,9 +1703,9 @@ static void handle_incoming_and_exit(void)
        test = url + 1;      /* skip first '/' */
 
 #if ENABLE_FEATURE_HTTPD_CGI
-       if (strncmp(test, "cgi-bin", 7) == 0) {
-               if (test[7] == '/' && test[8] == '\0') {
-                       /* protect listing cgi-bin/ */
+       if (strncmp(test, "cgi-bin/", 8) == 0) {
+               if (test[8] == '\0') {
+                       /* protect listing "cgi-bin/" */
                        send_headers_and_exit(HTTP_FORBIDDEN);
                }
                send_cgi_and_exit(url, prequest, length, cookie, content_type);
@@ -1768,7 +1744,11 @@ static void handle_incoming_and_exit(void)
                        send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type);
                }
        }
-#endif  /* FEATURE_HTTPD_CGI */
+#endif
+       /* else {
+        *      fall through to send_file, it errors out if open fails
+        * }
+        */
 
        send_file_and_exit(test);
 
@@ -1813,6 +1793,11 @@ static void handle_incoming_and_exit(void)
 static void mini_httpd(int server) ATTRIBUTE_NORETURN;
 static void mini_httpd(int server)
 {
+       /* NB: it's best to not use xfuncs in this loop before fork().
+        * Otherwise server may die on transient errors (temporary
+        * out-of-memory condition, etc), which is Bad(tm).
+        * Try to do any dangerous calls after fork.
+        */
        while (1) {
                int n;
                len_and_sockaddr fromAddr;
@@ -1825,18 +1810,6 @@ static void mini_httpd(int server)
                        continue;
                /* set the KEEPALIVE option to cull dead connections */
                setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
-               accepted_socket = n;
-
-               n = get_nport(&fromAddr.sa);
-               tcp_port = ntohs(n);
-               rmt_ip = 0;
-               if (fromAddr.sa.sa_family == AF_INET) {
-                       rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
-               }
-               if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
-                       free(rmt_ip_str);
-                       rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddr.len);
-               }
 
                if (fork() == 0) {
                        /* child */
@@ -1844,6 +1817,16 @@ static void mini_httpd(int server)
                        /* Do not reload config on HUP */
                        signal(SIGHUP, SIG_IGN);
 #endif
+                       accepted_socket = n;
+                       n = get_nport(&fromAddr.sa);
+                       tcp_port = ntohs(n);
+                       rmt_ip = 0;
+                       if (fromAddr.sa.sa_family == AF_INET) {
+                               rmt_ip = ntohl(fromAddr.sin.sin_addr.s_addr);
+                       }
+                       if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
+                               rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr.sa, fromAddr.len);
+                       }
                        if (verbose) {
                                /* this trick makes -v logging much simpler */
                                applet_name = rmt_ip_str;
@@ -1853,7 +1836,7 @@ static void mini_httpd(int server)
                        handle_incoming_and_exit();
                }
                /* parent, or fork failed */
-               close(accepted_socket);
+               close(n);
        } /* while (1) */
        /* never reached */
 }
@@ -1883,12 +1866,13 @@ static void mini_httpd_inetd(void)
 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
 static void sighup_handler(int sig)
 {
-       /* set and reset */
        struct sigaction sa;
 
        parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
+
+       memset(&sa, 0, sizeof(sa));
        sa.sa_handler = sighup_handler;
-       sigemptyset(&sa.sa_mask);
+       /*sigemptyset(&sa.sa_mask); - memset should be enough */
        sa.sa_flags = SA_RESTART;
        sigaction(SIGHUP, &sa, NULL);
 }
@@ -1930,12 +1914,13 @@ int httpd_main(int argc, char **argv)
        USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
        USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
 
+       INIT_G();
+
 #if ENABLE_LOCALE_SUPPORT
        /* Undo busybox.c: we want to speak English in http (dates etc) */
        setlocale(LC_TIME, "C");
 #endif
 
-       INIT_G();
        home_httpd = xrealloc_getcwd_or_warn(NULL);
        opt_complementary = "vv"; /* counter */
        /* We do not "absolutize" path given by -h (home) opt.
@@ -2012,13 +1997,12 @@ int httpd_main(int argc, char **argv)
        }
 #endif
 
+#if BB_MMU
 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
        sighup_handler(0);
 #else
        parse_conf(default_path_httpd_conf, FIRST_PARSE);
 #endif
-
-#if BB_MMU
        if (opt & OPT_INETD)
                mini_httpd_inetd();
        if (!(opt & OPT_FOREGROUND))