httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jeff Trawick <trawi...@bellsouth.net>
Subject [PATCH] reworking getline and friends
Date Wed, 11 Oct 2000 02:09:17 GMT
Aside from any remaining blunders I introduced with buckets, the
following getline code should work pretty well.  It is straight from
1.3, along with its friend bgets() :)  Thanks to Greg Ames for the
idea...  I started rewriting getline() from scratch, then Greg
suggested that we look at last week's getline(), but after finding a
number of "issues" in both versions it seemed worthwhile to go back to 
trusted code and move forward from there.

This patch has:

  resync of getline() based very closely on 1.3's getline()

  a bgets() based pretty closely on 1.3's ap_bgets(), but it calls
  ap_get_next_brigade() instead of ap_bread() to get data from the
  network 

  an eof flag in the c, corresponding to the old B_EOF flag

  http_filter() doesn't mess with the header; bgets() has that
  knowledge

  bgets() and http_filter() share yet another brigade with raw data
  from the network -- c->raw_data; presumably dechunking code would
  share c->raw_data too; when bgets() grabs some of the data after the
  end of the header, it goes back into c->raw_data for http_filter()
  to see; when http_filter() grabs some of the data after the end of
  the body, it goes back into c->raw_data for bgets() to see

Comments?  

The diff is a mess unfortunately...  A diff between the 1.3 functions
and these versions would be simpler...

Index: main/http_core.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_core.c,v
retrieving revision 1.159
diff -u -r1.159 http_core.c
--- main/http_core.c	2000/10/10 02:14:11	1.159
+++ main/http_core.c	2000/10/11 01:56:54
@@ -3326,9 +3326,9 @@
             AP_BRIGADE_INSERT_TAIL(b, e);
         }
         else {
-            /* XXX need to trigger EOS/EOF processing;
-             * for now, return empty brigade because that is what
-             * getline() looks for */
+            /* trigger EOS/EOF processing;
+             * return empty brigade and set eof flag */
+            f->c->eof = 1;
         }
     }
     else {
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.155
diff -u -r1.155 http_protocol.c
--- main/http_protocol.c	2000/10/10 17:04:19	1.155
+++ main/http_protocol.c	2000/10/11 01:56:58
@@ -856,98 +856,151 @@
     return AP_HTTP_METHODS[methnum];
 }
 
-typedef struct http_filter_ctx {
-    ap_bucket_brigade *b;
-} http_ctx_t;
-
 apr_status_t http_filter(ap_filter_t *f, ap_bucket_brigade *b)
 {
-#define ASCII_CR '\015'
-#define ASCII_LF '\012'
-    ap_bucket *e;
-    char *buff;
-    apr_ssize_t length;
-    char *pos;
-    int state = 0;
-    http_ctx_t *ctx = f->ctx;
-    ap_bucket_brigade *bb;
-    apr_status_t rv;
+    /* If we aren't moving through the body, defer to the next filter */
+    if (f->c->remaining > 0) {
+        ap_bucket *e;
+        ap_bucket_brigade *rd;
+        int remain = f->c->remaining;
+        apr_status_t rv;
 
-    if (!ctx) {
-        f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
-        if ((rv = ap_get_brigade(f->next, b)) != APR_SUCCESS) {
-            return rv;
-        }
-    }
-    else {
-        if (ctx->b) {
-            AP_BRIGADE_CONCAT(b, ctx->b);
-            ctx->b = NULL; /* XXX we just leaked a brigade structure */
-        }
-        else {
-            if ((rv = ap_get_brigade(f->next, b)) != APR_SUCCESS) {
+        rd = f->c->raw_data;
+        if (AP_BRIGADE_EMPTY(rd)) {
+            rv = ap_get_brigade(f->next, rd);
+            if (rv != APR_SUCCESS) {
                 return rv;
             }
         }
-    }
 
-    if (f->c->remaining > 0) {
-        int remain = f->c->remaining;
-        e = AP_BRIGADE_FIRST(b);
-        while (remain > e->length && e != AP_BRIGADE_SENTINEL(b)) {
+        e = AP_BRIGADE_FIRST(rd);
+        while (remain > e->length && 
+               e != AP_BRIGADE_SENTINEL(rd)) {
+            /* Move the entire bucket to the caller's brigade */
             remain -= e->length;
-            e = AP_BUCKET_NEXT(e);
+            AP_BUCKET_REMOVE(e);
+            AP_BRIGADE_INSERT_TAIL(b, e);
+            e = AP_BRIGADE_FIRST(rd);
         }
-        if (e != AP_BRIGADE_SENTINEL(b)) {
+        if (e != AP_BRIGADE_SENTINEL(rd)) {
             if (remain <= e->length) {
+                /* Move first part of this bucket to the caller's brigade. */
                 e->split(e, remain);
                 remain = 0;
+                AP_BUCKET_REMOVE(e);
+                AP_BRIGADE_INSERT_TAIL(b, e);
             }
-            bb = ap_brigade_split(b, AP_BUCKET_NEXT(e));
-            f->c->remaining = remain;
-            ctx->b = bb;
-            return APR_SUCCESS;
-        }
-        else {
-            f->c->remaining = remain;
-            ctx->b = NULL;
-            return APR_SUCCESS;
         }
+        f->c->remaining = remain;
+        return APR_SUCCESS;
     }
+    else {
+        return ap_get_brigade(f->next, b);
+    }
+}
 
-    AP_BRIGADE_FOREACH(e, b) {
+#define ASCII_CR '\015'
+#define ASCII_LF '\012'
 
-        if ((rv = e->read(e, (const char **)&buff, &length, 0)) != APR_SUCCESS)
{
-            return rv;
-        }
+static int bgets(char *buff, int n, conn_rec *c)
+{
+    ap_bucket *e;
+    ap_bucket_brigade *b;
+    const char *temp;
+    apr_ssize_t length;
+    apr_status_t rv;
+    int ch, ct;
+    apr_ssize_t i;
 
-        pos = buff + 1;
-        while (pos < buff + length) {
+    if (!c->raw_data) {
+        b = ap_brigade_create(c->pool);
+    }
+    else {
+        b = c->raw_data;
+    }
 
-            /* We are at the start of one line, but it actually has data. */
-            if ((*pos != ASCII_LF) && (*pos != ASCII_CR)) {
-                state = 0;
-            }
-            else {
-                if (*pos == ASCII_LF) {
-                    state++;
+    ct = 0;
+    i = 0;
+    length = 0;
+    e = NULL;
+    for (;;) {
+        if (i == length) {            /* out of data */
+            if (e) {
+                AP_BUCKET_REMOVE(e);
+                e->destroy(e);
+                e = NULL;
+            }                
+            if (AP_BRIGADE_EMPTY(b)) {
+                rv = ap_get_brigade(c->input_filters, b);
+                if (rv != APR_SUCCESS) {
+                    buff[ct] = '\0';
+                    return ct ? ct : -1;
                 }
-            }
-            
-            if ((*pos == ASCII_LF) && (*(pos - 1) == ASCII_CR)) {
-                *(pos - 1) = ASCII_LF;
-                *pos = '\0';
+                if (AP_BRIGADE_EMPTY(b)) {
+                    break;
+                }
             }
-            pos++;
-            if (state == 2) {
-                e->split(e, pos - buff);
-                bb = ap_brigade_split(b, AP_BUCKET_NEXT(e));
-                ctx->b = bb;
-                return APR_SUCCESS;
+
+            e = AP_BRIGADE_FIRST(b); 
+            rv = e->read(e, &temp, &length, 0);
+            if (rv != APR_SUCCESS) {
+                buff[ct] = '\0';
+                return ct ? ct : -1;
             }
+            i = 0;
+            continue;                 /* restart with new data, skip over
+                                       * 0-length bucket */
+        }
+
+        ch = temp[i];
+        ++i;
+        if (ch == ASCII_LF) {
+            if (ct == 0)
+                buff[ct++] = ASCII_LF;
+            else if (buff[ct - 1] == ASCII_CR)
+                buff[ct - 1] = ASCII_LF;
+            else if (ct < n - 1)
+                buff[ct++] = ASCII_LF;
+            else
+                i--;        /* no room for LF */
+            break;
+        }
+        if (ct == n - 1) {
+            i--;            /* push back ch */
+            break;
+        }
+        
+        buff[ct++] = ch;
+    }
+
+    buff[ct] = '\0';
+    c->raw_data = b;
+    if (e) {
+        if (i != length) {
+            e->split(e, i);
+        }
+        AP_BUCKET_REMOVE(e);
+        e->destroy(e);
+    }
+
+    return ct;
+}
+
+static int blookc(char *next, conn_rec *c)
+{
+    if (c->raw_data && !AP_BRIGADE_EMPTY(c->raw_data)) {
+        const char *temp;
+        apr_ssize_t length;
+        ap_bucket *e;
+
+        e = AP_BRIGADE_FIRST(c->raw_data);
+        if (e->read(e, &temp, &length, 0) == APR_SUCCESS &&
+            length >= 1) {
+            *next = *temp;
+            return 1;
         }
     }
-    return APR_SUCCESS;
+    return 0;
 }
 
 /* Get a line of protocol input, including any continuation lines
@@ -965,121 +1018,53 @@
  */
 static int getline(char *s, int n, conn_rec *c, int fold)
 {
-    char *pos;
-    const char *toss;
-    const char *temp;
+    char *pos, next;
     int retval;
     int total = 0;
-    apr_ssize_t length;
-    ap_bucket_brigade *b;
-    ap_bucket *e;
-    
-    
-#ifdef APACHE_XLATE_XXX
-    /* When getline() is called, the HTTP protocol is in a state
-     * where we MUST be reading "plain text" protocol stuff,
-     * (Request line, MIME headers, Chunk sizes) regardless of
-     * the MIME type and conversion setting of the document itself.
-     * Save the current setting of the translation handle for
-     * uploads, then temporarily set it to the one used for headers
-     * (and restore it before returning).
-     */
-    AP_PUSH_INPUTCONVERSION_STATE(in, ap_hdrs_from_ascii);
-#endif /*APACHE_XLATE*/
 
     pos = s;
-
-    if (!c->input_data) {
-        b = ap_brigade_create(c->pool);
-    }
-    else {
-        b = c->input_data;
-    }
-
-    if (AP_BRIGADE_EMPTY(b)) {
-        ap_get_brigade(c->input_filters, b);
-    }
 
-    if (AP_BRIGADE_EMPTY(b)) {
-        return -1;
-    }
-    e = AP_BRIGADE_FIRST(b); 
-    while (1) {
-        while (e->length == 0) {
-            AP_BUCKET_REMOVE(e);
-            e->destroy(e);
-
-            ap_get_brigade(c->input_filters, b);
-            if (!AP_BRIGADE_EMPTY(b)) {
-                e = AP_BRIGADE_FIRST(b); 
-            }
-            else {
-                return -1;
-            }
-        }
-        retval = e->read(e, &temp, &length, 0);
-        /* retval == 0 on SUCCESS */
+    do {
+        retval = bgets(pos, n, c);     /* retval == -1 if error, 0 if EOF */
 
-        if (retval != 0) {
-            total = ((length < 0) && (total == 0)) ? -1 : total;
+        if (retval <= 0) {
+            total = ((retval < 0) && (total == 0)) ? -1 : total;
             break;
         }
 
-	/* check for line end using ascii encoding, even on an ebcdic box
-	 * since this is raw protocol data fresh from the network
-	 */
-        if ((toss = ap_strchr_c(temp, ASCII_LF)) != NULL) { 
-            length = toss - temp + 1;
-            e->split(e, length + (temp[length] == '\0'));
-            apr_cpystrn(pos, temp, length + 1);
-	    
-            AP_BUCKET_REMOVE(e);
-            e->destroy(e);
-        }
-        c->input_data = b;
-        e = AP_BRIGADE_FIRST(b); 
-/**** XXX
- *    Check for folding
- * Continue appending if line folding is desired and
- * the last line was not empty and we have room in the buffer and
- * the next line begins with a continuation character.
- *       if (!fold || (retval == 0) && (n > 1)
- *	     && (retval = e->read(e, ) 
- *	     && ((next == ' ') || (next == '\t')));
- */
-        /* length is the number of characters read, not including NUL    */
-
-        n -= length;            /* Keep track of how much of s is full   */
-        pos += (length - 1);    /* and where s ends                      */
-        total += length;        /* and how long s has become             */
+        /* retval is the number of characters read, not including NUL      */
+
+        n -= retval;            /* Keep track of how much of s is full     */
+        pos += (retval - 1);    /* and where s ends                        */
+        total += retval;        /* and how long s has become               */
 
-        if (*pos == '\n') {     /* Did we get a full line of input?      */
+        if (*pos == '\n') {     /* Did we get a full line of input?        */
             /*
              * Trim any extra trailing spaces or tabs except for the first
              * space or tab at the beginning of a blank string.  This makes
              * it much easier to check field values for exact matches, and
              * saves memory as well.  Terminate string at end of line.
              */
-            while (pos > (s + 1) && (*(pos - 1) == ' '
-				     || *(pos - 1) == '\t')) {
-                --pos;          /* trim extra trailing spaces or tabs    */
-                --total;        /* but not one at the beginning of line  */
+            while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t'))
{
+                --pos;          /* trim extra trailing spaces or tabs      */
+                --total;        /* but not one at the beginning of line    */
                 ++n;
             }
             *pos = '\0';
             --total;
             ++n;
-            break;
         }
-        else {
-	    break;       /* if not, input line exceeded buffer size */
-	}
-    }
-#ifdef APACHE_XLATE_XXX
-    /* restore translation handle */
-    AP_POP_INPUTCONVERSION_STATE(in);
-#endif /*APACHE_XLATE*/
+        else
+            break;       /* if not, input line exceeded buffer size */
 
+        /* Continue appending if line folding is desired and
+         * the last line was not empty and we have room in the buffer and
+         * the next line begins with a continuation character.
+         */
+    } while (fold && (retval != 1) && (n > 1)
+                  && (blookc(&next, c) == 1)
+                  && ((next == ' ') || (next == '\t')));
+
     return total;
 }
 
@@ -1162,7 +1147,7 @@
     ap_bsetflag(conn->client, B_SAFEREAD, 1); 
     ap_bflush(conn->client);
     while ((len = getline(l, sizeof(l), conn, 0)) <= 0) {
-        if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
+        if ((len < 0) || conn->eof) {
 	    ap_bsetflag(conn->client, B_SAFEREAD, 0);
 	    /* this is a hack to make sure that request time is set,
 	     * it's not perfect, but it's better than nothing 
@@ -2440,10 +2425,17 @@
     apr_status_t rv;
     apr_int32_t timeout;
 
+    if (!r->connection->input_data) {
+        r->connection->input_data = ap_brigade_create(r->connection->pool);
+    }
 
     if (!r->read_chunked) {     /* Content-length read */
         ap_bucket *b;
         const char *tempbuf;
+
+        if (!r->remaining) {
+            return 0;
+        }
 
         len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
 
Index: include/httpd.h
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/include/httpd.h,v
retrieving revision 1.96
diff -u -r1.96 httpd.h
--- include/httpd.h	2000/10/09 14:15:32	1.96
+++ include/httpd.h	2000/10/11 01:57:01
@@ -907,6 +907,12 @@
     /** Location to store data read from the client.
      *  @defvar ap_bucket_brigade *input_data */
     struct ap_bucket_brigade *input_data;
+    /** Location to store raw data read from the client.
+     *  @defvar ap_bucket_brigade *raw_data */
+    struct ap_bucket_brigade *raw_data;
+    /** Flag to indicate when we've hit eof for this connection
+     *  @defvar int eof */
+    int eof;
     /** A list of output filters to be used for this connection
      *  @defvar ap_filter_t *filters */
     struct ap_filter_t *output_filters;


-- 
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
     http://www.geocities.com/SiliconValley/Park/9289/
          Born in Roswell... married an alien...

Mime
View raw message