httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jerenkra...@apache.org
Subject cvs commit: httpd-2.0/include http_protocol.h
Date Thu, 24 Jan 2002 23:59:51 GMT
jerenkrantz    02/01/24 15:59:51

  Modified:    .        CHANGES
               server   protocol.c
               include  http_protocol.h
  Log:
  Rewrite ap_rgetline to remove the need to have an "internal" brigade stored
  in the core_module structure by using the AP_MODE_SPECULATIVE filter mode
  to determine if MIME-continuation should occur.
  
  Notes:
  - ap_rgetline has a new prototype.
  - ap_rgetline returns APR_ENOSPC when we are out of buffer space.
  
  All direct callers of ap_rgetline are now adjusted to handle this new API.
  ap_getline will mimic the old API for now.
  
  Reviewed by:	Ryan Morgan
  
  Revision  Changes    Path
  1.533     +3 -0      httpd-2.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/CHANGES,v
  retrieving revision 1.532
  retrieving revision 1.533
  diff -u -r1.532 -r1.533
  --- CHANGES	24 Jan 2002 23:47:30 -0000	1.532
  +++ CHANGES	24 Jan 2002 23:59:50 -0000	1.533
  @@ -1,5 +1,8 @@
   Changes with Apache 2.0.31-dev
   
  +  *) Refactor ap_rgetline so that it does not use an internal brigade.
  +     Change ap_rgetline's prototype to return errors.  [Justin Erenkrantz]
  +
     *) Remove mod_auth_db.  [Justin Erenkrantz]
   
     *) Do not install unnecessary pcre headers like config.h and internal.h.
  
  
  
  1.67      +303 -153  httpd-2.0/server/protocol.c
  
  Index: protocol.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/protocol.c,v
  retrieving revision 1.66
  retrieving revision 1.67
  diff -u -r1.66 -r1.67
  --- protocol.c	19 Jan 2002 07:45:18 -0000	1.66
  +++ protocol.c	24 Jan 2002 23:59:51 -0000	1.67
  @@ -187,10 +187,16 @@
    * caused by MIME folding (or broken clients) if fold != 0, and place it
    * in the buffer s, of size n bytes, without the ending newline.
    *
  - * Returns: 
  - *     the length of s (normal case),
  - *     n               (buffer full),
  - *    -1               (other errors)
  + * If s is NULL, ap_rgetline will allocate necessary memory from r->pool.
  + *
  + * Returns APR_SUCCESS if there are no problems and sets *read to be
  + * the full length of s.
  + *
  + * APR_ENOSPC is returned if there is not enough buffer space.
  + * Other errors may be returned on other errors.
  + *
  + * The LF is *not* returned in the buffer.  Therefore, a *read of 0
  + * indicates that an empty line was read.
    *
    * Notes: Because the buffer uses 1 char for NUL, the most we can return is 
    *        (n - 1) actual characters.  
  @@ -198,152 +204,279 @@
    *        If no LF is detected on the last line due to a dropped connection 
    *        or a full buffer, that's considered an error.
    */
  -AP_DECLARE(int) ap_rgetline(char **s, int n, request_rec *r, int fold)
  +AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, 
  +                                     apr_size_t *read, request_rec *r, 
  +                                     int fold)
   {
  -    char *pos;
  -    char *last_char;
  -    const char *temp;
  -    int retval;
  -    apr_size_t total = 0;
  -    int looking_ahead = 0;
  -    apr_size_t length;
  -    core_request_config *req_cfg;
  +    apr_status_t rv;
       apr_bucket_brigade *b;
       apr_bucket *e;
  -    int do_alloc = (*s == NULL);
  -    apr_size_t alloc_size = 0;
  +    apr_size_t bytes_handled = 0, current_alloc = 0;
  +    apr_off_t bytes_read;
  +    char *pos, *last_char = *s;
  +    int do_alloc = (*s == NULL), saw_eos = 0;
  +
  +    b = apr_brigade_create(r->pool);
  +    rv = ap_get_brigade(r->input_filters, b, AP_MODE_GETLINE,
  +                        APR_BLOCK_READ, &bytes_read);
   
  -    req_cfg = (core_request_config *)
  -                ap_get_module_config(r->request_config, &core_module);
  -    b = req_cfg->bb;
  -    /* make sure it's empty unless we're folding */ 
  -    AP_DEBUG_ASSERT(fold || APR_BRIGADE_EMPTY(b));
  -
  -    while (1) {
  -        if (APR_BRIGADE_EMPTY(b)) {
  -            apr_off_t zero = 0;
  -            if ((retval = ap_get_brigade(r->input_filters, b,
  -                                         AP_MODE_GETLINE,
  -                                         APR_BLOCK_READ,
  -                                         &zero)) != APR_SUCCESS ||
  -                APR_BRIGADE_EMPTY(b)) {
  -                apr_brigade_destroy(b);
  -                return -1;
  -            }
  -        }
  -        e = APR_BRIGADE_FIRST(b); 
  +    if (rv != APR_SUCCESS) {
  +        return rv;
  +    }
  +
  +    /* Something horribly wrong happened.  Someone didn't block! */
  +    if (APR_BRIGADE_EMPTY(b)) {
  +        return APR_EGENERAL; 
  +    }
  +
  +    APR_BRIGADE_FOREACH(e, b) {
  +        const char *str;
  +        apr_size_t len;
  +
  +        /* If we see an EOS, don't bother doing anything more. */
           if (APR_BUCKET_IS_EOS(e)) {
  +            saw_eos = 1; 
  +            break;
  +        }
  +
  +        rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
  +
  +        if (rv != APR_SUCCESS) {
               apr_brigade_destroy(b);
  -            return -1;
  +            return rv;
           }
  -        if (e->length == 0) {
  -            apr_bucket_delete(e);
  -            continue;
  +
  +        /* Would this overrun our buffer?  If so, we'll die. */
  +        if (n < bytes_handled + len) {
  +            apr_brigade_destroy(b); 
  +            return APR_ENOSPC;
           }
  -        retval = apr_bucket_read(e, &temp, &length, APR_BLOCK_READ);
  -        if (retval != APR_SUCCESS) {
  -            apr_brigade_destroy(b);
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, retval, r, "apr_bucket_read() failed");
  -            if (total) {
  -                break; /* report previously-read data to caller, do ap_xlate_proto_to_ascii()
*/
  +
  +        /* Do we have to handle the allocation ourselves? */
  +        if (do_alloc) {
  +            /* We'll assume the common case where one bucket is enough. */
  +            if (!*s) {
  +                current_alloc = len;
  +                *s = apr_palloc(r->pool, len);
               }
  -            else {
  -                return -1;
  +            else if (bytes_handled + len > current_alloc) {
  +                /* We resize to the next power of 2. */
  +                apr_size_t new_size = current_alloc;
  +                char *new_buffer;
  +                do {
  +                    new_size *= 2;
  +                } while (bytes_handled + len > new_size);
  +                new_buffer = apr_palloc(r->pool, new_size);
  +                /* Copy what we already had. */
  +                memcpy(new_buffer, *s, bytes_handled);
  +                current_alloc = new_size;
  +                *s = new_buffer;
               }
           }
  +        /* Just copy the rest of the data to the end of the old buffer. */
  +        pos = *s + bytes_handled;
  +        memcpy(pos, str, len);
  +        last_char = pos + len - 1;
   
  -        if ((looking_ahead) && (*temp != APR_ASCII_BLANK) && (*temp !=
APR_ASCII_TAB)) { 
  -            /* can't fold because next line isn't indented, 
  -             * so return what we have.  lookahead brigade is 
  -             * stashed on req_cfg->bb
  -             */
  -            AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(req_cfg->bb));
  -            break;
  -        }
  -        if (total + length - 1 < (apr_size_t)n) {
  +        /* We've now processed that new data - update accordingly. */
  +        bytes_handled += len;
  +    }
  +
  +    /* We no longer need the returned brigade. */
  +    apr_brigade_destroy(b);
  +
  +    /* We likely aborted early before reading anything or we read no 
  +     * data.  Technically, this might be success condition.  But,
  +     * probably means something is horribly wrong.  For now, we'll
  +     * treat this as APR_SUCCESS, but it may be worth re-examining.
  +     */
  +    if (bytes_handled == 0) {
  +        *read = 0;
  +        return APR_SUCCESS; 
  +    }
  +
  +    /* If we didn't get a full line of input, try again. */
  +    if (*last_char != APR_ASCII_LF) {
  +        /* Do we have enough space? We may be full now. */
  +        if (bytes_handled < n) {
  +            apr_size_t next_size, next_len;
  +            char *tmp;
  +     
  +            /* If we're doing the allocations for them, we have to
  +             * give ourselves a NULL and copy it on return.
  +             */ 
               if (do_alloc) {
  -                if (!*s) {
  -                    alloc_size = length;
  -                    *s = apr_palloc(r->pool, length + 2); /* +2 for LF, null */
  -                }
  -                else if (total + length > alloc_size) {
  -                    apr_size_t new_size = alloc_size;
  -                    char *new_buffer;
  -                    do {
  -                        new_size *= 2;
  -                    } while (total + length > new_size);
  -                    new_buffer = apr_palloc(r->pool, new_size + 2);
  -                    memcpy(new_buffer, *s, total);
  -                    alloc_size = new_size;
  -                    *s = new_buffer;
  -                }
  +                tmp = NULL;
  +            } else {
  +                /* We're not null terminated yet. */
  +                tmp = last_char + 1;
               }
  -            pos = *s + total;
  -            last_char = pos + length - 1;
  -            memcpy(pos, temp, length);
  -            apr_bucket_delete(e);
  -        }
  -        else {
  -            /* input line was larger than the caller's buffer */
  -            apr_brigade_destroy(b); 
   
  -            /* don't need to worry about req_cfg->bb being bogus.
  -             * the request is about to die, and ErrorDocument
  -             * redirects get a new req_cfg->bb
  -             */
  -
  -            return n;
  -        }
  -        
  -        pos = last_char;        /* Point at the last character           */
  -
  -        if (*pos == APR_ASCII_LF) { /* Did we get a full line of input?      */
  -                
  -            if (pos > *s && *(pos - 1) == APR_ASCII_CR) {
  -                --pos;          /* zap optional CR before LF             */
  +            next_size = n - bytes_handled;
  +
  +            rv = ap_rgetline(&tmp, next_size, &next_len, r, fold);
  +
  +            if (rv != APR_SUCCESS) {
  +                return rv;
               }
  -                
  -            /*
  -             * 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) == APR_ASCII_BLANK || *(pos - 1) == APR_ASCII_TAB)) {
  -                --pos;          /* trim extra trailing spaces or tabs    */
  +
  +            if (do_alloc && next_len > 0) {
  +                char *new_buffer;
  +                apr_size_t new_size = bytes_handled + next_len;
  +                /* Again we need to alloc an extra two bytes for LF, null */
  +                new_buffer = apr_palloc(r->pool, new_size);
  +                /* Copy what we already had. */
  +                memcpy(new_buffer, *s, bytes_handled);
  +                memcpy(new_buffer + bytes_handled, tmp, next_len);
  +                current_alloc = new_size;
  +                *s = new_buffer;
               }
  -            *pos = '\0';        /* zap end of string                     */
  -            total = pos - *s;   /* update total string length            */
   
  -            /* look ahead another line if line folding is desired 
  -             * and this line isn't empty
  -             */
  -            if (fold && total) {
  -                looking_ahead = 1;
  +            bytes_handled += next_len;
  +            last_char = *s + bytes_handled - 1;
  +        }
  +        else {
  +            return APR_ENOSPC;
  +        }
  +    }
  +
  +    /* We now go backwards over any CR (if present) or white spaces.
  +     *
  +     * 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.
  +     */
  +    pos = last_char;
  +    if (pos > *s && *(pos - 1) == APR_ASCII_CR) {
  +        --pos;
  +    }
  +
  +    /* 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.
  +     */
  +    while (pos > ((*s) + 1) && 
  +           (*(pos - 1) == APR_ASCII_BLANK || *(pos - 1) == APR_ASCII_TAB)) {
  +        --pos;
  +    }
  +
  +    /* Since we want to remove the LF from the line, we'll go ahead
  +     * and set this last character to be the term NULL and reset 
  +     * bytes_handled accordingly.
  +     */
  +    *pos = '\0';
  +    last_char = pos;
  +    bytes_handled = pos - *s;
  +   
  +    /* If we're folding, we have more work to do. 
  +     *
  +     * Note that if an EOS was seen, we know we can't have another line.
  +     */
  +    if (fold && bytes_handled && !saw_eos) {
  +        const char *str;
  +        apr_size_t len;
  +
  +        /* We only care about the first byte. */
  +        bytes_read = 1;
  +        rv = ap_get_brigade(r->input_filters, b, AP_MODE_SPECULATIVE,
  +                            APR_BLOCK_READ, &bytes_read);
  +
  +        if (rv != APR_SUCCESS) {
  +            return rv;
  +        }
  +
  +        e = APR_BRIGADE_FIRST(b);
  +
  +        /* If we see an EOS, don't bother doing anything more. */
  +        if (APR_BUCKET_IS_EOS(e)) {
  +            *read = bytes_handled;
  +            return APR_SUCCESS;
  +        }
  +
  +        rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ);
  +
  +        if (rv != APR_SUCCESS) {
  +            return rv;
  +        }
  +
  +        /* Found one, so call ourselves again to get the next line. 
  +         *
  +         * FIXME: If the folding line is completely blank, should we
  +         * stop folding?  Does that require also looking at the next
  +         * char?
  +         */
  +        if (*str == APR_ASCII_BLANK || *str == APR_ASCII_TAB) {
  +            /* Do we have enough space? We may be full now. */
  +            if (bytes_handled < n) {
  +                apr_size_t next_size, next_len;
  +                char *tmp;
  +       
  +                /* If we're doing the allocations for them, we have to
  +                 * give ourselves a NULL and copy it on return.
  +                 */ 
  +                if (do_alloc) {
  +                    tmp = NULL;           
  +                } else { 
  +                    /* We're null terminated. */
  +                    tmp = last_char;
  +                }
  +
  +                next_size = n - bytes_handled;
  +
  +                rv = ap_rgetline(&tmp, next_size, &next_len, r, fold);
  +
  +                if (rv != APR_SUCCESS) {
  +                    return rv;
  +                }
  +
  +                if (do_alloc && next_len > 0) {
  +                    char *new_buffer;
  +                    apr_size_t new_size = bytes_handled + next_len;
  +                    /* Again we need to alloc an extra two bytes for LF, null */
  +                    new_buffer = apr_palloc(r->pool, new_size);
  +                    /* Copy what we already had. */
  +                    memcpy(new_buffer, *s, bytes_handled);
  +                    memcpy(new_buffer + bytes_handled, tmp, next_len);
  +                    current_alloc = new_size;
  +                    *s = new_buffer;
  +                }
  +
  +                *read = bytes_handled + next_len;
  +                return APR_SUCCESS;
               }
               else {
  -                AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
  -                break;
  +                return APR_ENOSPC;
               }
           }
  -        else {
  -            /* no LF yet...character mode client (telnet)...keep going
  -             * bump past last character read,   
  -             * and set total in case we bail before finding a LF   
  -             */
  -            total = ++pos - *s;
  -            looking_ahead = 0;  /* only appropriate right after LF       */ 
  -        }
       }
  -    ap_xlate_proto_from_ascii(*s, total);
  -    return total;
  +
  +    /* FIXME: Can we optimize this at all by placing it a different layer? */
  +    ap_xlate_proto_from_ascii(*s, bytes_handled);
  +    *read = bytes_handled;
  +    return APR_SUCCESS;
   }
   
   AP_DECLARE(int) ap_getline(char *s, int n, request_rec *r, int fold)
   {
       char *tmp_s = s;
  -    return ap_rgetline(&tmp_s, n, r, fold);
  +    apr_status_t rv;
  +    apr_size_t len;
  +
  +    rv = ap_rgetline(&tmp_s, n, &len, r, fold);
  +
  +    /* Map the out-of-space condition to the old API. */
  +    if (rv == APR_ENOSPC) {
  +        return n;
  +    }
  +
  +    /* Anything else is just bad. */
  +    if (rv != APR_SUCCESS) {
  +        return -1;
  +    }
  +
  +    return (int)len;
   }
   
   /* parse_uri: break apart the uri
  @@ -409,7 +542,7 @@
       conn_rec *conn = r->connection;
   #endif
       int major = 1, minor = 0;   /* Assume HTTP/1.0 if non-"HTTP" protocol */
  -    int len;
  +    apr_size_t len;
   
       /* Read past empty lines until we get a real request line,
        * a read error, the connection closes (EOF), or we timeout.
  @@ -426,16 +559,20 @@
        * have to block during a read.
        */
   
  -    while ((len = ap_rgetline(&(r->the_request),
  -                              DEFAULT_LIMIT_REQUEST_LINE + 2, r, 0)) <= 0) {
  -        if (len < 0) {             /* includes EOF */
  -	    /* this is a hack to make sure that request time is set,
  -	     * it's not perfect, but it's better than nothing 
  -	     */
  -	    r->request_time = apr_time_now();
  +    do {
  +        apr_status_t rv;
  +
  +        rv = ap_rgetline(&(r->the_request), DEFAULT_LIMIT_REQUEST_LINE + 2,
  +                         &len, r, 0);
  +
  +        if (rv != APR_SUCCESS) {
  +            /* Something went horribly wrong. */
  +            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "read_request_line() failed");
  +	        r->request_time = apr_time_now();
               return 0;
           }
  -    }
  +    } while (len <= 0);
  +
       /* we've probably got something to do, ignore graceful restart requests */
   
       r->request_time = apr_time_now();
  @@ -502,7 +639,7 @@
   {
       char* field;
       char *value;
  -    int len;
  +    apr_size_t len;
       int fields_read = 0;
       apr_table_t *tmp_headers;
   
  @@ -513,31 +650,45 @@
        * Read header lines until we get the empty separator line, a read error,
        * the connection closes (EOF), reach the server limit, or we timeout.
        */
  -    field = NULL;
  -    while ((len = ap_rgetline(&field, DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2,
  -                              r, 1)) > 0) {
  +    while(1) {
  +        apr_status_t rv;
   
  -        if (r->server->limit_req_fields &&
  -            (++fields_read > r->server->limit_req_fields)) {
  +        field = NULL;
  +        rv = ap_rgetline(&field, DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2,
  +                         &len, r, 1);
  +
  +        /* ap_rgetline returns APR_ENOSPC if it fills up the buffer before 
  +         * finding the end-of-line.  This is only going to happen if it 
  +         * exceeds the configured limit for a field size.
  +         */
  +        if (rv == APR_ENOSPC) {
               r->status = HTTP_BAD_REQUEST;
               apr_table_setn(r->notes, "error-notes",
  -			   "The number of request header fields exceeds "
  -			   "this server's limit.");
  +                apr_pstrcat(r->pool,
  +                            "Size of a request header field "
  +                            "exceeds server limit.<br />\n"
  +                            "<pre>\n",
  +                            ap_escape_html(r->pool, field),
  +                            "</pre>\n", NULL));
               return;
           }
  -        /* ap_getline returns (size of max buffer - 1) if it fills up the
  -         * buffer before finding the end-of-line.  This is only going to
  -         * happen if it exceeds the configured limit for a field size.
  -         */
  -        if (len > r->server->limit_req_fieldsize) {
  +
  +        if (rv != APR_SUCCESS) {
  +            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "get_mime_headers() failed");
  +            return;
  +        }
  +
  +        /* Found a blank line, stop. */
  +        if (len == 0) {
  +            break;
  +        }
  +
  +        if (r->server->limit_req_fields &&
  +            (++fields_read > r->server->limit_req_fields)) {
               r->status = HTTP_BAD_REQUEST;
               apr_table_setn(r->notes, "error-notes",
  -			   apr_pstrcat(r->pool,
  -				       "Size of a request header field "
  -				       "exceeds server limit.<br />\n"
  -				       "<pre>\n",
  -				       ap_escape_html(r->pool, field),
  -				       "</pre>\n", NULL));
  +			   "The number of request header fields exceeds "
  +			   "this server's limit.");
               return;
           }
   
  @@ -557,10 +708,9 @@
           ++value;
           while (*value == ' ' || *value == '\t') {
               ++value;            /* Skip to start of value   */
  -	}
  +        }
   
  -	apr_table_addn(tmp_headers, field, value);
  -        field = NULL; /* to cause ap_rgetline to allocate a new one */
  +        apr_table_addn(tmp_headers, field, value);
       }
   
       apr_table_overlap(r->headers_in, tmp_headers, APR_OVERLAP_TABLES_MERGE);
  
  
  
  1.68      +6 -5      httpd-2.0/include/http_protocol.h
  
  Index: http_protocol.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/include/http_protocol.h,v
  retrieving revision 1.67
  retrieving revision 1.68
  diff -u -r1.67 -r1.68
  --- http_protocol.h	2 Dec 2001 23:13:32 -0000	1.67
  +++ http_protocol.h	24 Jan 2002 23:59:51 -0000	1.68
  @@ -545,14 +545,15 @@
    *          should be read; if *s==NULL, a buffer of the necessary size
    *          to hold the data will be allocated from the request pool
    * @param n The size of the buffer
  + * @param read The length of the line.
    * @param r The request
    * @param fold Whether to merge continuation lines
  - * @return The length of the line, if successful
  - *         n, if the line is too big to fit in the buffer
  - *         -1 for miscellaneous errors
  - * @deffunc int ap_method_number_of(const char *method)
  + * @return APR_SUCCESS, if successful
  + *         APR_ENOSPC, if the line is too big to fit in the buffer
  + *         Other errors where appropriate
    */
  -AP_DECLARE(int) ap_rgetline(char **s, int n, request_rec *r, int fold);
  +AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n, apr_size_t *read,
  +                                     request_rec *r, int fold);
   
   /**
    * Get the method number associated with the given string, assumed to
  
  
  

Mime
View raw message