I notice that with or without this patch, apache dumps an extra crlf pair
after the response header and before the first range (when doing
multipart/byteranges). But the example in section 19.2 of rfc2068 doesn't
have that extra crlf... are we compliant?
At any rate I give this a +1 assuming that if we aren't compliant you'll
provide a tweak.
Dean
P.S. gcc's optimizer won't optimize the tail recursion in
internal_byterange()... not that it's really important considering it's
only triggered by errors in the client's request.
On Sun, 9 Feb 1997, Alexei Kosut wrote:
> A thought crossed my head earlier today: after the incident this week
> with Netscape and byteranges, I've decided I no longer trust
> developers to implement specs like HTTP/1.1 correctly (heck, we try
> really hard, and we don't always get it right). So where possible, we
> should make Apache try to avoid breaking clients that might not
> implement HTTP/1.1 to the letter. In other words, don't rely on the
> fine print in the spec, even if it has a MUST for the client end.
>
> One obscure part of HTTP/1.1 that Apache current obeys is that there
> are three ways (at least) to make a connection persistent: it can have
> a Content-Length header, it can use chunked transfer-coding, or it
> can be a multipart/byteranges response (because you can determine
> easily the end of a multipart MIME document).
>
> I now believe that when (if) some clients implement HTTP/1.1, they will
> "forget" about that last one. However, the current Apache will open a
> persistent connection that way. This will result in problematic
> behavior.
>
> So enclosed is a patch that does the obvous thing; it adds a
> Content-Length header to multipart/byteranges responses. This has been
> tested with Netscape Navigator 3.01, and works fine.
>
> This patch also has the added benefit that the browser can now give
> you a percentage done when receiving the file, since it has a total
> content length to compare against. When downloading large PDF files,
> for example (since PDF seems to be what 99.9% of the byteserved
> files are nowadays), getting a percentage done instead of just how
> many bytes received is nice.
>
> Anyhow, this is for inclusion in 1.2b7. Someone please test and make
> sure my addition is correct; I tested it a bit, and it seems to be
> right, but... Note that I say things like "4 + x + 4", not "8 + x", so
> it's more obvious what I'm doing. Compilers should optimize that for
> me, right?
>
> Index: http_protocol.c
> ===================================================================
> RCS file: /export/home/cvs/apache/src/http_protocol.c,v
> retrieving revision 1.98
> diff -c -r1.98 http_protocol.c
> *** http_protocol.c 1997/02/06 21:40:35 1.98
> --- http_protocol.c 1997/02/09 08:00:53
> ***************
> *** 106,111 ****
> --- 106,113 ----
> return 1;
> }
>
> + static int internal_byterange(int, int*, request_rec*, char**, long*, long*);
> +
> int set_byterange (request_rec *r)
> {
> char *range = table_get (r->headers_in, "Range");
> ***************
> *** 161,171 ****
> else {
> /* a multiple range */
> char boundary[33]; /* Long enough */
>
> r->byterange = 2;
> - table_unset(r->headers_out, "Content-Length");
> ap_snprintf(boundary, sizeof(boundary), "%lx%lx", r->request_time, (long)getpid());
> r->boundary = pstrdup(r->pool, boundary);
> }
>
> r->status = PARTIAL_CONTENT;
> --- 163,177 ----
> else {
> /* a multiple range */
> char boundary[33]; /* Long enough */
> + char *r_range = pstrdup(r->pool, range + 6);
> + int tlength = 0;
>
> r->byterange = 2;
> ap_snprintf(boundary, sizeof(boundary), "%lx%lx", r->request_time, (long)getpid());
> r->boundary = pstrdup(r->pool, boundary);
> + while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
> + ap_snprintf(ts, sizeof(ts), "%ld", tlength);
> + table_set(r->headers_out, "Content-Length", ts);
> }
>
> r->status = PARTIAL_CONTENT;
> ***************
> *** 175,205 ****
> }
>
> int each_byterange (request_rec *r, long *offset, long *length) {
> long range_start, range_end;
> char *range;
>
> ! if (!*r->range) {
> ! if (r->byterange > 1)
> ! rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
> return 0;
> }
>
> ! range = getword_nc(r->pool, &r->range, ',');
> if (!parse_byterange(range, r->clength, &range_start, &range_end))
> ! return each_byterange(r, offset, length); /* Skip this one */
>
> if (r->byterange > 1) {
> char *ct = r->content_type ? r->content_type : default_type(r);
> char ts[MAX_STRING_LEN];
> !
> ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, r->clength);
> ! rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
> ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
> NULL);
> }
>
> ! *offset = range_start;
> ! *length = range_end - range_start + 1;
> return 1;
> }
>
> --- 181,243 ----
> }
>
> int 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 had it been called with 1.
> + *
> + * Either case will return 1 if it should be called again, and 0
> + * when done.
> + *
> + */
> +
> + static int internal_byterange(int realreq, int *tlength, request_rec *r,
> + char **r_range, long *offset, long *length) {
> long range_start, range_end;
> char *range;
>
> ! if (!**r_range) {
> ! if (r->byterange > 1) {
> ! if (realreq)
> ! rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
> ! else
> ! *tlength += 4 + strlen(r->boundary) + 4;
> ! }
> return 0;
> }
>
> ! range = getword_nc(r->pool, r_range, ',');
> if (!parse_byterange(range, r->clength, &range_start, &range_end))
> ! /* Skip this one */
> ! return internal_byterange(realreq, tlength, r, r_range, offset,
> ! length);
>
> if (r->byterange > 1) {
> char *ct = r->content_type ? r->content_type : default_type(r);
> char ts[MAX_STRING_LEN];
> !
> ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, r->clength);
> ! if (realreq)
> ! rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
> ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
> 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;
> ! }
> return 1;
> }
>
> ***************
> *** 228,234 ****
> (r->server->keep_alive_max > r->connection->keepalives)) &&
> (r->server->keep_alive_timeout > 0) &&
> (r->status == USE_LOCAL_COPY || r->header_only || length || tenc ||
> ! ((r->proto_num >= 1001) && (r->byterange > 1 || (r->chunked
= 1)))) &&
> (!find_token(r->pool, conn, "close")) &&
> ((ka_sent = find_token(r->pool, conn, "keep-alive")) ||
> r->proto_num >= 1001)) {
> --- 266,272 ----
> (r->server->keep_alive_max > r->connection->keepalives)) &&
> (r->server->keep_alive_timeout > 0) &&
> (r->status == USE_LOCAL_COPY || r->header_only || length || tenc ||
> ! ((r->proto_num >= 1001) && (r->chunked = 1))) &&
> (!find_token(r->pool, conn, "close")) &&
> ((ka_sent = find_token(r->pool, conn, "keep-alive")) ||
> r->proto_num >= 1001)) {
>
>
> --
> ________________________________________________________________________
> Alexei Kosut <akosut@nueva.pvt.k12.ca.us> The Apache HTTP Server
> URL: http://www.nueva.pvt.k12.ca.us/~akosut/ http://www.apache.org/
>
>
|