httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Alexei Kosut <ako...@nueva.pvt.k12.ca.us>
Subject [PATCH] multipart/byteranges + keepalive
Date Sun, 09 Feb 1997 08:11:19 GMT
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/


Mime
View raw message