httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From f...@apache.org
Subject cvs commit: apache-1.3/src/main http_protocol.c
Date Thu, 18 Jan 2001 03:34:26 GMT
fanf        01/01/17 19:34:25

  Modified:    src      CHANGES
               src/main http_protocol.c
  Log:
  Rewrite byterange handling so that:
   * the syntax is parsed properly
   * unsatisfiable requests cause a 416 error
   * the API is completely preserved
  
  PR:             6973
  Submitted by:   fanf, wrowe
  Reviewed by:    jim, stoddard, trawick, (dirkx, martin)
  
  Revision  Changes    Path
  1.1613    +4 -0      apache-1.3/src/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/CHANGES,v
  retrieving revision 1.1612
  retrieving revision 1.1613
  diff -u -u -r1.1612 -r1.1613
  --- CHANGES	2001/01/04 04:12:41	1.1612
  +++ CHANGES	2001/01/18 03:34:25	1.1613
  @@ -1,5 +1,9 @@
   Changes with Apache 1.3.15
   
  +  *) Fix Range header processing to properly parse the syntax specified
  +     in RFC 2616, and properly handle unsatisfiable requests by returning
  +     a 416 error. PR#6973 [Tony Finch, William Rowe]
  +
     *) Remove some human-readable fluff from the machine-readable mod_status
        display. PR#7025 [Youichirou Koga <y-koga@apache.or.jp>]
   
  
  
  
  1.297     +202 -139  apache-1.3/src/main/http_protocol.c
  
  Index: http_protocol.c
  ===================================================================
  RCS file: /home/cvs/apache-1.3/src/main/http_protocol.c,v
  retrieving revision 1.296
  retrieving revision 1.297
  diff -u -u -r1.296 -r1.297
  --- http_protocol.c	2001/01/15 17:05:01	1.296
  +++ http_protocol.c	2001/01/18 03:34:25	1.297
  @@ -141,48 +141,139 @@
       return type;
   }
   
  -static int parse_byterange(char *range, long clength, long *start, long *end)
  +enum byterange_token {
  +    BYTERANGE_OK,
  +    BYTERANGE_EMPTY,
  +    BYTERANGE_BADSYNTAX,
  +    BYTERANGE_UNSATISFIABLE
  +};
  +
  +static enum byterange_token
  +    parse_byterange(request_rec *r, long *start, long *end)
   {
  -    char *dash = strchr(range, '-');
  +    /* parsing first, semantics later */
   
  -    if (!dash)
  -        return 0;
  +    while (ap_isspace(*r->range))
  +        ++r->range;
   
  -    if ((dash == range)) {
  -        /* In the form "-5" */
  -        *start = clength - atol(dash + 1);
  -        *end = clength - 1;
  +    /* check for an empty range, which is OK */
  +    if (*r->range == '\0') {
  +	return BYTERANGE_EMPTY;
       }
  -    else {
  -        *dash = '\0';
  -        dash++;
  -        *start = atol(range);
  -        if (*dash)
  -            *end = atol(dash);
  -        else                    /* "5-" */
  -            *end = clength - 1;
  +    else if (*r->range == ',') {
  +	++r->range;
  +	return BYTERANGE_EMPTY;
       }
  +
  +    if (ap_isdigit(*r->range))
  +	*start = strtol(r->range, (char **)&r->range, 10);
  +    else
  +	*start = -1;
  +
  +    while (ap_isspace(*r->range))
  +        ++r->range;
  +
  +    if (*r->range != '-')
  +	return BYTERANGE_BADSYNTAX;
  +    ++r->range;
   
  -    if (*start < 0)
  -	*start = 0;
  +    while (ap_isspace(*r->range))
  +        ++r->range;
   
  -    if (*end >= clength)
  -        *end = clength - 1;
  +    if (ap_isdigit(*r->range))
  +	*end = strtol(r->range, (char **)&r->range, 10);
  +    else
  +	*end = -1;
   
  -    if (*start > *end)
  -	return 0;
  +    while (ap_isspace(*r->range))
  +        ++r->range;
   
  -    return (*start > 0 || *end < clength - 1);
  +    /* check the end of the range */
  +    if (*r->range == ',') {
  +	++r->range;
  +    }
  +    else if (*r->range != '\0') {
  +	return BYTERANGE_BADSYNTAX;
  +    }
  +
  +    /* parsing done; now check the numbers */
  +
  +    if (*start < 0) { /* suffix-byte-range-spec */
  +	if (*end < 0) /* no numbers */
  +	    return BYTERANGE_BADSYNTAX;
  +	*start = r->clength - *end;
  +	if (*start < 0)
  +	    *start = 0;
  +	*end = r->clength - 1;
  +    }
  +    else {
  +	if (*end >= 0 && *start > *end) /* out-of-order range */
  +	    return BYTERANGE_BADSYNTAX;
  +	if (*end < 0 || *end >= r->clength)
  +	    *end = r->clength - 1;
  +    }
  +    /* RFC 2616 is somewhat unclear about what we should do if the end
  +     * is missing and the start is after the clength. The robustness
  +     * principle says we should accept it as an unsatisfiable range.
  +     * We accept suffix-byte-range-specs like -0 for the same reason.
  +     */
  +    if (*start >= r->clength)
  +	return BYTERANGE_UNSATISFIABLE;
  +
  +    return BYTERANGE_OK;
   }
   
  -static int internal_byterange(int, long *, request_rec *, const char **, long *,
  -                              long *);
  +/* If this function is called with output=1, it will spit out the
  + * correct headers for a byterange chunk. If output=0 it will not
  + * output anything but just return the number of bytes it would have
  + * output. If start or end are less than 0 then it will do a byterange
  + * chunk trailer instead of a header.
  + */
  +static int byterange_boundary(request_rec *r, long start, long end, int output)
  +{
  +    int length = 0;
   
  +#ifdef CHARSET_EBCDIC
  +    /* determine current setting of conversion flag,
  +     * set to ON (protocol strings MUST be converted)
  +     * and reset to original setting before returning
  +     */
  +    PUSH_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client, 1);
  +#endif /*CHARSET_EBCDIC*/
  +
  +    if (start < 0 || end < 0) {
  +	if (output)
  +	    ap_rvputs(r, CRLF "--", r->boundary, "--" CRLF, NULL);
  +	else
  +	    length = 4 + strlen(r->boundary) + 4;
  +    }
  +    else {
  +	const char *ct = make_content_type(r, r->content_type);
  +	char ts[MAX_STRING_LEN];
  +
  +	ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", start, end, r->clength);
  +	if (output)
  +	    ap_rvputs(r, CRLF "--", r->boundary, CRLF "Content-type: ",
  +		      ct, CRLF "Content-range: bytes ", ts, CRLF CRLF,
  +		      NULL);
  +	else
  +	    length = 4 + strlen(r->boundary) + 16
  +		+ strlen(ct) + 23 + strlen(ts) + 4;
  +    }
  +
  +#ifdef CHARSET_EBCDIC
  +    POP_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client);
  +#endif /*CHARSET_EBCDIC*/
  +
  +    return length;
  +}
  +
   API_EXPORT(int) ap_set_byterange(request_rec *r)
   {
       const char *range, *if_range, *match;
  -    long range_start, range_end;
  -
  +    long length, start, end, one_start, one_end;
  +    int ranges, empty;
  +    
       if (!r->clength || r->assbackwards)
           return 0;
   
  @@ -202,6 +293,7 @@
       if (!range || strncasecmp(range, "bytes=", 6)) {
           return 0;
       }
  +    range += 6;
   
       /* Check the If-Range header for Etag or Date.
        * Note that this check will return false (as required) if either
  @@ -218,129 +310,100 @@
               return 0;
       }
   
  -    if (!strchr(range, ',')) {
  -        /* A single range */
  -        if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength,
  -                             &range_start, &range_end))
  -            return 0;
  -
  -        r->byterange = 1;
  +    /*
  +     * Parse the byteranges, counting how many of them there are and
  +     * the total number of bytes we will send to the client. This is a
  +     * dummy run for the while(ap_each_byterange()) loop that the
  +     * caller will perform if we return 1.
  +     */
  +    r->range = range;
  +    r->boundary = ap_psprintf(r->pool, "%lx%lx",
  +			      r->request_time, (long) getpid());
  +    length = 0;
  +    ranges = 0;
  +    empty = 1;
  +    do {
  +	switch (parse_byterange(r, &start, &end)) {
  +	case BYTERANGE_UNSATISFIABLE:
  +	    empty = 0;
  +	    break;
  +	default:
  +	    /* be more defensive here? */
  +	case BYTERANGE_BADSYNTAX:
  +	    r->boundary = NULL;
  +	    r->range = NULL;
  +	    return 0;
  +	case BYTERANGE_EMPTY:
  +	    break;
  +	case BYTERANGE_OK:
  +	    ++ranges;
  +	    length += byterange_boundary(r, start, end, 0)
  +		+ end - start + 1;
  +	    /* save in case of unsatisfiable ranges */
  +	    one_start = start;
  +	    one_end = end;
  +	    break;
  +	}
  +    } while (*r->range != '\0');
   
  +    if (ranges == 0) {
  +	/* no ranges or only unsatisfiable ranges */
  +	if (empty || if_range) {
  +	    r->boundary = NULL;
  +	    r->range = NULL;
  +	    return 0;
  +	}
  +	else {
  +	    ap_table_setn(r->headers_out, "Content-Range",
  +		ap_psprintf(r->pool, "bytes */%ld", r->clength));
  +	    r->boundary = NULL;
  +	    r->range = range;
  +	    r->header_only = 1;
  +	    r->status = HTTP_RANGE_NOT_SATISFIABLE;
  +	    return 1;
  +	}
  +    }
  +    else if (ranges == 1) {
  +	/* simple handling of a single range -- no boundaries */
           ap_table_setn(r->headers_out, "Content-Range",
   	    ap_psprintf(r->pool, "bytes %ld-%ld/%ld",
  -		range_start, range_end, r->clength));
  -        ap_table_setn(r->headers_out, "Content-Length",
  -	    ap_psprintf(r->pool, "%ld", range_end - range_start + 1));
  +		one_start, one_end, r->clength));
  +	ap_table_setn(r->headers_out, "Content-Length",
  +	    ap_psprintf(r->pool, "%ld", one_end - one_start + 1));
  +	r->boundary = NULL;
  +	r->byterange = 1;
  +	r->range = range;
  +	r->status = PARTIAL_CONTENT;
  +	return 1;
       }
       else {
  -        /* a multiple range */
  -        const char *r_range = ap_pstrdup(r->pool, range + 6);
  -        long tlength = 0;
  -	int ret;
  -	
  -        r->byterange = 2;
  -        r->boundary = ap_psprintf(r->pool, "%lx%lx",
  -				r->request_time, (long) getpid());
  -        do {
  -	    /* Loop while we have another range spec to process */
  -	    ret = internal_byterange(0, &tlength, r, &r_range, NULL, NULL);
  -	} while (ret == 1);
  -	/* If an error occured processing one of the range specs, we
  -	 * must fail */
  -	if (ret < 0) {
  -	    r->byterange = 0;
  -	    return 0;
  -	}
  -        ap_table_setn(r->headers_out, "Content-Length",
  -	    ap_psprintf(r->pool, "%ld", tlength));
  +	/* multiple ranges */
  +	length += byterange_boundary(r, -1, -1, 0);
  +	ap_table_setn(r->headers_out, "Content-Length",
  +	    ap_psprintf(r->pool, "%ld", length));
  +	r->byterange = 2;
  +	r->range = range;
  +	r->status = PARTIAL_CONTENT;
  +	return 1;
       }
  -
  -    r->status = PARTIAL_CONTENT;
  -    r->range = range + 6;
  -
  -    return 1;
   }
   
   API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length)
   {
  -    return internal_byterange(1, NULL, r, &r->range, offset, length);
  -}
  -
  -/* If this function is called with realreq=1, it will spit out
  - * the correct headers for a byterange chunk, and set offset and
  - * length to the positions they should be.
  - *
  - * If it is called with realreq=0, it will add to tlength the length
  - * it *would* have used with realreq=1.
  - *
  - * Either case will return 1 if it should be called again, 0 when done,
  - * or -1 if an error occurs AND realreq=0.
  - */
  -static int internal_byterange(int realreq, long *tlength, request_rec *r,
  -                              const char **r_range, long *offset, long *length)
  -{
  -    long range_start, range_end;
  -    char *range;
  -#ifdef CHARSET_EBCDIC
  -    /* determine current setting of conversion flag,
  -     * set to ON (protocol strings MUST be converted)
  -     * and reset to original setting before returning
  -     */
  -    PUSH_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client, 1);
  -#endif /*CHARSET_EBCDIC*/
  -
  -    if (!**r_range) {
  -        if (r->byterange > 1) {
  -            if (realreq)
  -                ap_rvputs(r, CRLF "--", r->boundary, "--" CRLF, NULL);
  -            else
  -                *tlength += 4 + strlen(r->boundary) + 4;
  -        }
  -#ifdef CHARSET_EBCDIC
  -        POP_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client);
  -#endif /*CHARSET_EBCDIC*/
  -        return 0;
  -    }
  -
  -    range = ap_getword(r->pool, r_range, ',');
  -    if (!parse_byterange(range, r->clength, &range_start, &range_end)) {
  -#ifdef CHARSET_EBCDIC
  -        POP_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client);
  -#endif /*CHARSET_EBCDIC*/
  -	if (!realreq)
  -	    /* Return error on invalid syntax */
  -	    return -1;
  -	else
  -	    /* Should never get here */
  -	    return 0;
  -    }
  +    long start, end;
   
  -    if (r->byterange > 1) {
  -        const char *ct = make_content_type(r, r->content_type);
  -        char ts[MAX_STRING_LEN];
  -
  -        ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end,
  -                    r->clength);
  -        if (realreq)
  -            ap_rvputs(r, CRLF "--", r->boundary, CRLF "Content-type: ",
  -                   ct, CRLF "Content-range: bytes ", ts, CRLF CRLF,
  -                   NULL);
  -        else
  -            *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 +
  -                        strlen(ts) + 4;
  -    }
  -
  -    if (realreq) {
  -        *offset = range_start;
  -        *length = range_end - range_start + 1;
  -    }
  -    else {
  -        *tlength += range_end - range_start + 1;
  -    }
  -#ifdef CHARSET_EBCDIC
  -    POP_EBCDIC_OUTPUTCONVERSION_STATE(r->connection->client);
  -#endif /*CHARSET_EBCDIC*/
  -    return 1;
  +    do {
  +	if (parse_byterange(r, &start, &end) == BYTERANGE_OK) {
  +	    if (r->byterange > 1)
  +		byterange_boundary(r, start, end, 1);
  +	    *offset = start;
  +	    *length = end - start + 1;
  +	    return 1;
  +	}
  +    } while (*r->range != '\0');
  +    if (r->byterange > 1)
  +	byterange_boundary(r, -1, -1, 1);
  +    return 0;
   }
   
   API_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
  
  
  

Mime
View raw message