httpd: stop reading headers using 1-byte read()
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 18 Aug 2007 14:22:09 +0000 (14:22 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 18 Aug 2007 14:22:09 +0000 (14:22 -0000)
get_line                                              73     105     +32
httpd_main                                           769     766      -3
send_cgi_and_exit                                   1583    1577      -6
send_headers                                         449     432     -17
handle_incoming_and_exit                            2217    2172     -45
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/4 up/down: 32/-71)            Total: -39 bytes

networking/httpd.c

index cd46f49eff42d01572a55e5f2469b21314bccfcd..0727144351b79f517d5a4ed426c71dbdcd2f9a30 100644 (file)
@@ -131,11 +131,12 @@ typedef struct Htaccess_IP {
 } Htaccess_IP;
 
 struct globals {
-       int verbose;
+       int verbose;            /* must be int (used by getopt32) */
        smallint flg_deny_all;
 
        unsigned rmt_ip;
-       unsigned rmt_port;       /* for set env REMOTE_PORT */
+       unsigned rmt_port;      /* for set env REMOTE_PORT */
+       char *rmt_ip_str;       /* for set env REMOTE_ADDR */
        const char *bind_addr_or_port;
 
        const char *g_query;
@@ -145,26 +146,27 @@ struct globals {
        const char *found_mime_type;
        const char *found_moved_temporarily;
        time_t last_mod;
-       off_t ContentLength;          /* -1 - unknown */
-       Htaccess_IP *ip_a_d;          /* config allow/deny lines */
+       off_t ContentLength;    /* -1 - unknown */
+       Htaccess_IP *ip_a_d;    /* config allow/deny lines */
 
        USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
        USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
        USE_FEATURE_HTTPD_CGI(char *referer;)
        USE_FEATURE_HTTPD_CGI(char *user_agent;)
 
-       char *rmt_ip_str;        /* for set env REMOTE_ADDR */
-
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-       Htaccess *g_auth;        /* config user:password lines */
+       Htaccess *g_auth;       /* config user:password lines */
 #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-       Htaccess *mime_a;             /* config mime types */
+       Htaccess *mime_a;       /* config mime types */
 #endif
 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-       Htaccess *script_i;           /* config script interpreters */
+       Htaccess *script_i;     /* config script interpreters */
 #endif
-       char iobuf[MAX_MEMORY_BUF];
+       char *iobuf;            /* [MAX_MEMORY_BUF] */
+#define hdr_buf bb_common_bufsiz1
+       char *hdr_ptr;
+       int hdr_cnt;
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -189,6 +191,8 @@ struct globals {
 #define mime_a            (G.mime_a           )
 #define script_i          (G.script_i         )
 #define iobuf             (G.iobuf            )
+#define hdr_ptr           (G.hdr_ptr          )
+#define hdr_cnt           (G.hdr_cnt          )
 #define INIT_G() do { \
        PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
        USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -196,6 +200,7 @@ struct globals {
        ContentLength = -1; \
 } while (0)
 
+
 typedef enum {
        HTTP_OK = 200,
        HTTP_MOVED_TEMPORARILY = 302,
@@ -881,12 +886,21 @@ static void send_headers_and_exit(HttpResponseNum responseNum)
 static int get_line(void)
 {
        int count = 0;
+       char c;
+
+       while (1) {
+               if (hdr_cnt <= 0) {
+                       hdr_cnt = safe_read(0, hdr_buf, sizeof(hdr_buf));
+                       if (hdr_cnt <= 0)
+                               break;
+                       hdr_ptr = hdr_buf;
+               }
+               iobuf[count] = c = *hdr_ptr++;
+               hdr_cnt--;
 
-       /* We must not read extra chars. Reading byte-by-byte... */
-       while (read(0, iobuf + count, 1) == 1) {
-               if (iobuf[count] == '\r')
+               if (c == '\r')
                        continue;
-               if (iobuf[count] == '\n') {
+               if (c == '\n') {
                        iobuf[count] = '\0';
                        return count;
                }
@@ -928,7 +942,6 @@ static void send_cgi_and_exit(
        char *fullpath;
        char *script;
        char *purl;
-       size_t post_read_size, post_read_idx;
        int buf_count;
        int status;
        int pid = 0;
@@ -1080,10 +1093,10 @@ static void send_cgi_and_exit(
        /* First, restore variables possibly changed by child */
        xfunc_error_retval = 0;
 
-       /* Prepare for pumping data */
+       /* Prepare for pumping data.
+        * iobuf is used for CGI -> network data,
+        * hdr_buf is for network -> CGI data (POSTDATA) */
        buf_count = 0;
-       post_read_size = 0;
-       post_read_idx = 0; /* for gcc */
        close(fromCgi.wr);
        close(toCgi.rd);
 
@@ -1091,25 +1104,25 @@ static void send_cgi_and_exit(
         * and send it to the peer. So please no SIGPIPEs! */
        signal(SIGPIPE, SIG_IGN);
 
+       /* This loop still looks messy. What is an exit criteria?
+        * "CGI's output closed"? Or "CGI has exited"?
+        * What to do if CGI has closed both input and output, but
+        * didn't exit? etc... */
+
+       /* NB: breaking out of this loop jumps to log_and_exit() */
        while (1) {
                fd_set readSet;
                fd_set writeSet;
-               char wbuf[128];
                int nfound;
                int count;
 
-               /* This loop still looks messy. What is an exit criteria?
-                * "CGI's output closed"? Or "CGI has exited"?
-                * What to do if CGI has closed both input and output, but
-                * didn't exit? etc... */
-
                FD_ZERO(&readSet);
                FD_ZERO(&writeSet);
                FD_SET(fromCgi.rd, &readSet);
-               if (bodyLen > 0 || post_read_size > 0) {
+               if (bodyLen > 0 || hdr_cnt > 0) {
                        FD_SET(toCgi.wr, &writeSet);
                        nfound = toCgi.wr > fromCgi.rd ? toCgi.wr : fromCgi.rd;
-                       if (post_read_size == 0)
+                       if (hdr_cnt <= 0)
                                FD_SET(0, &readSet);
                        /* Now wait on the set of sockets! */
                        nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL);
@@ -1135,30 +1148,30 @@ static void send_cgi_and_exit(
                        break;
                }
 
-               if (post_read_size > 0 && FD_ISSET(toCgi.wr, &writeSet)) {
+               if (hdr_cnt > 0 && FD_ISSET(toCgi.wr, &writeSet)) {
                        /* Have data from peer and can write to CGI */
-                       count = safe_write(toCgi.wr, wbuf + post_read_idx, post_read_size);
+                       count = safe_write(toCgi.wr, hdr_ptr, hdr_cnt);
                        /* Doesn't happen, we dont use nonblocking IO here
                         *if (count < 0 && errno == EAGAIN) {
                         *      ...
                         *} else */
                        if (count > 0) {
-                               post_read_idx += count;
-                               post_read_size -= count;
+                               hdr_ptr += count;
+                               hdr_cnt -= count;
                        } else {
-                               post_read_size = bodyLen = 0; /* EOF/broken pipe to CGI */
+                               hdr_cnt = bodyLen = 0; /* EOF/broken pipe to CGI */
                        }
-               } else if (bodyLen > 0 && post_read_size == 0
+               } else if (bodyLen > 0 && hdr_cnt == 0
                 && FD_ISSET(0, &readSet)
                ) {
                        /* We expect data, prev data portion is eaten by CGI
                         * and there *is* data to read from the peer
                         * (POSTDATA?) */
-                       count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
-                       count = safe_read(0, wbuf, count);
+                       count = bodyLen > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : bodyLen;
+                       count = safe_read(0, hdr_buf, count);
                        if (count > 0) {
-                               post_read_size = count;
-                               post_read_idx = 0;
+                               hdr_cnt = count;
+                               hdr_ptr = hdr_buf;
                                bodyLen -= count;
                        } else {
                                bodyLen = 0; /* closed */
@@ -1195,7 +1208,7 @@ static void send_cgi_and_exit(
                                                full_write(1, HTTP_200, sizeof(HTTP_200)-1);
                                                full_write(1, rbuf, buf_count);
                                        }
-                                       break; /* closed */
+                                       break; /* CGI stdout is closed, exiting */
                                }
                                buf_count += count;
                                count = 0;
@@ -1379,6 +1392,7 @@ static int checkPermIP(void)
        return !flg_deny_all;
 }
 
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 /*
  * Check the permission file for access password protected.
  *
@@ -1390,7 +1404,6 @@ static int checkPermIP(void)
  *
  * Returns 1 if request is OK.
  */
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 static int checkPerm(const char *path, const char *request)
 {
        Htaccess *cur;
@@ -1457,7 +1470,6 @@ static int checkPerm(const char *path, const char *request)
 
        return prev == NULL;
 }
-
 #endif  /* FEATURE_HTTPD_BASIC_AUTH */
 
 /*
@@ -1495,6 +1507,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        int credentials = -1;  /* if not required this is Ok */
 #endif
 
+       /* Allocation of iobuf is postponed until now
+        * (IOW, server process doesn't need to waste 8k) */
+       iobuf = xmalloc(MAX_MEMORY_BUF);
+
        rmt_port = get_nport(&fromAddr->sa);
        rmt_port = ntohs(rmt_port);
        rmt_ip = 0;
@@ -1731,6 +1747,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
        }
 #endif  /* FEATURE_HTTPD_CGI */
+
        if (purl[-1] == '/')
                strcpy(purl, "index.html");
        if (stat(test, &sb) == 0) {