You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Jeff Trawick <tr...@bellsouth.net> on 2000/10/25 02:46:44 UTC
providing content-length
We should be able to modify the http header filter to recognize that
there is no content-length and that we're using HTTP/1.0, and have it
provide the content length (easier said than done, of course). With
such code, we can use a filter which zaps the content length field
(since it expects to invalidate it) but still have Netscape behave
suitably.
Does anybody have any ideas about controls over when we provide the
Content-Length? Obviously there are some performance issues
involved. It may be quite painful for certain systems to provide
Content-Length on certain pages.
I'm keen to try this out, as I'm hoping it will explain some breakage
with Netscape.
--
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...
Re: providing content-length
Posted by Jeff Trawick <tr...@bellsouth.net>.
rbb@covalent.net writes:
> > > Why? We only add it if we know that we have all the information. We
> > > don't add it if we have to save ANY data off to the side. This seems
> > > rather straight-forward to me, and it is what Roy has said in the past as
> > > well.
> >
> > I'm a bit confused... maybe you can tell me which (if any) is the
> > proper interpretation of what you are saying.
> >
> > . your comment above says (I think) "compute content length if the
> > first brigade is the only brigade (i.e., if the last bucket is EOS)"
> >
> > . your comment in a separate note says (I think) "if we can't send
> > chunked then compute content length, buffering if needed"
> >
> > My patch is closer to the second interpretation.
>
> I'm saying both. :-) If we are allowed to send chunked-encoding then we
> only compute content length if we have all the information in the first
> pass. If we aren't allowed to send chunked-encoding, then we have no
> choice but to buffer the data.
okey dokey... Here is another patch, with simplifications to the
algorithm (wait until the end to calculate content-length) and the
implementation of more required checks to decide whether or not we
should go to the trouble...
Index: include/http_protocol.h
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/include/http_protocol.h,v
retrieving revision 1.35
diff -u -r1.35 http_protocol.h
--- include/http_protocol.h 2000/10/22 13:09:23 1.35
+++ include/http_protocol.h 2000/10/30 19:06:07
@@ -113,6 +113,8 @@
AP_DECLARE(void) ap_send_http_header(request_rec *l);
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b);
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *,
+ ap_bucket_brigade *);
/* Send the response to special method requests */
@@ -533,6 +535,7 @@
apr_status_t ap_http_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode);
apr_status_t ap_dechunk_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode);
+
/* Hooks */
/*
Index: main/http_core.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_core.c,v
retrieving revision 1.190
diff -u -r1.190 http_core.c
--- main/http_core.c 2000/10/26 12:03:07 1.190
+++ main/http_core.c 2000/10/30 19:06:17
@@ -3578,6 +3578,8 @@
ap_register_input_filter("DECHUNK", ap_dechunk_filter, AP_FTYPE_TRANSCODE);
ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_NETWORK);
ap_register_output_filter("HTTP_HEADER", ap_http_header_filter, AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter,
+ AP_FTYPE_HTTP_HEADER);
ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_NETWORK);
ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter,
AP_FTYPE_CONTENT);
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.209
diff -u -r1.209 http_protocol.c
--- main/http_protocol.c 2000/10/26 11:05:07 1.209
+++ main/http_protocol.c 2000/10/30 19:06:19
@@ -1417,6 +1417,7 @@
? &r->server->keep_alive_timeout
: &r->server->timeout);
+ ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection);
ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection);
/* Get the request... */
@@ -2227,6 +2228,77 @@
AP_DECLARE(void) ap_send_http_header(request_rec *r)
{
+}
+
+struct content_length_ctx {
+ ap_bucket_brigade *saved;
+};
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
+ ap_bucket_brigade *b)
+{
+ request_rec *r = f->r;
+ struct content_length_ctx *ctx;
+ apr_status_t rv;
+ ap_bucket *e;
+
+ ctx = f->ctx;
+ if (!ctx) { /* first time through */
+ /* We won't compute a content length if one of the following is true:
+ * . subrequest
+ * . HTTP/0.9
+ * . content length already computed
+ * . can be chunked
+ */
+ if (r->assbackwards ||
+ apr_table_get(r->headers_out, "Content-Length") ||
+ (r->proto_num < HTTP_VERSION(1,1) &&
+ ap_find_last_token(f->r->pool,
+ apr_table_get(r->headers_out,
+ "Transfer-Encoding"),
+ "chunked"))) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, b);
+ }
+
+ f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
+ }
+
+ if (AP_BUCKET_IS_EOS(AP_BRIGADE_LAST(b))) {
+ apr_ssize_t content_length = 0;
+
+ if (ctx->saved) {
+ AP_BRIGADE_CONCAT(ctx->saved, b);
+ b = ctx->saved;
+ }
+
+ AP_BRIGADE_FOREACH(e, b) {
+ if (!AP_BUCKET_IS_EOS(e)) {
+ if (e->length >= 0) {
+ content_length += e->length;
+ }
+ else {
+ const char *ignored;
+ apr_ssize_t length;
+
+ rv = ap_bucket_read(e, &ignored, &length, 1);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ content_length += e->length;
+ }
+ }
+ }
+
+ ap_set_content_length(r, content_length);
+ return ap_pass_brigade(f->next, b);
+ }
+
+ /* save the brigade; we can't pass any data to the next
+ * filter until we have the entire content length
+ */
+ ap_save_brigade(f, &ctx->saved, &b);
+ return APR_SUCCESS;
}
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b)
--
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...
Re: providing content-length
Posted by rb...@covalent.net.
> > Why? We only add it if we know that we have all the information. We
> > don't add it if we have to save ANY data off to the side. This seems
> > rather straight-forward to me, and it is what Roy has said in the past as
> > well.
>
> I'm a bit confused... maybe you can tell me which (if any) is the
> proper interpretation of what you are saying.
>
> . your comment above says (I think) "compute content length if the
> first brigade is the only brigade (i.e., if the last bucket is EOS)"
>
> . your comment in a separate note says (I think) "if we can't send
> chunked then compute content length, buffering if needed"
>
> My patch is closer to the second interpretation.
I'm saying both. :-) If we are allowed to send chunked-encoding then we
only compute content length if we have all the information in the first
pass. If we aren't allowed to send chunked-encoding, then we have no
choice but to buffer the data.
Ryan
_______________________________________________________________________________
Ryan Bloom rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------
Re: providing content-length
Posted by Jeff Trawick <tr...@bellsouth.net>.
rbb@covalent.net writes:
> > Any opinions on the big picture? Content-length is goodness but it
> > can be painful... While I think we need to be able to fix it up, I'm
> > still uncomfortable with always adding it in.
>
> Why? We only add it if we know that we have all the information. We
> don't add it if we have to save ANY data off to the side. This seems
> rather straight-forward to me, and it is what Roy has said in the past as
> well.
I'm a bit confused... maybe you can tell me which (if any) is the
proper interpretation of what you are saying.
. your comment above says (I think) "compute content length if the
first brigade is the only brigade (i.e., if the last bucket is EOS)"
. your comment in a separate note says (I think) "if we can't send
chunked then compute content length, buffering if needed"
My patch is closer to the second interpretation.
Here is the other comment:
>> I also still don't see the decision making process regarding who/what
>> is ABLE to handle 'chunked encoding'. Are you going to 406 if you
>> don't get 'Accept-encoding: chunked' from a UA and something
>> starts to filter without tracking content length? You should.
>
>No. If something doesn't send "Accept-encoding: chunked" and we don't
>have a content-length, then we will buffer the data and compute the
>content-length before we send it.
--
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...
Re: providing content-length
Posted by rb...@covalent.net.
> Any opinions on the big picture? Content-length is goodness but it
> can be painful... While I think we need to be able to fix it up, I'm
> still uncomfortable with always adding it in.
Why? We only add it if we know that we have all the information. We
don't add it if we have to save ANY data off to the side. This seems
rather straight-forward to me, and it is what Roy has said in the past as
well.
Ryan
_______________________________________________________________________________
Ryan Bloom rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------
Re: providing content-length
Posted by Jeff Trawick <tr...@bellsouth.net>.
rbb@covalent.net writes:
> This looks too complex. We only really want to calculate the content
> length if we have the entire response when we get to this filter. The
> filter should be as simple as:
>
> if (AP_BUCKET_IS_EOS(AP_BUCKET_LAST(bb)) {
>
> /* Make sure we soak up all pipe/socket data. */
> AP_BUCKET_FOREACH(e,bb) {
> ap_bucket_read(e, &str, &length);
> }
> /* get the content length */
> AP_BUCKET_FOREACH(e,bb) {
> length += e->length;
> }
> r->content_length = length;
> ap_pass_brigade(f->next, bb);
> }
> ap_pass_brigade(f->next, bb);
yep...
Any opinions on the big picture? Content-length is goodness but it
can be painful... While I think we need to be able to fix it up, I'm
still uncomfortable with always adding it in.
Roy?
--
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...
Re: providing content-length
Posted by rb...@covalent.net.
> > We should be able to modify the http header filter to recognize that
> > there is no content-length and that we're using HTTP/1.0, and have it
> > provide the content length (easier said than done, of course). With
> > such code, we can use a filter which zaps the content length field
> > (since it expects to invalidate it) but still have Netscape behave
> > suitably.
> >
> > Does anybody have any ideas about controls over when we provide the
> > Content-Length? Obviously there are some performance issues
> > involved. It may be quite painful for certain systems to provide
> > Content-Length on certain pages.
>
> Here is a patch to provide content length. I'm not sure when the
> content-length calculator should kick in... Under directive control?
> Note that it doesn't currently implement the check for r->chunked
> because r->chunked hasn't been calculated yet. But hey, we get nice
> progress bars with Content-Length anyway... who needs chunked? (only
> 3% serious)
This looks too complex. We only really want to calculate the content
length if we have the entire response when we get to this filter. The
filter should be as simple as:
if (AP_BUCKET_IS_EOS(AP_BUCKET_LAST(bb)) {
/* Make sure we soak up all pipe/socket data. */
AP_BUCKET_FOREACH(e,bb) {
ap_bucket_read(e, &str, &length);
}
/* get the content length */
AP_BUCKET_FOREACH(e,bb) {
length += e->length;
}
r->content_length = length;
ap_pass_brigade(f->next, bb);
}
ap_pass_brigade(f->next, bb);
Ryan
_______________________________________________________________________________
Ryan Bloom rbb@apache.org
406 29th St.
San Francisco, CA 94131
-------------------------------------------------------------------------------
Re: providing content-length
Posted by Jeff Trawick <tr...@bellsouth.net>.
Jeff Trawick <tr...@bellsouth.net> writes:
> We should be able to modify the http header filter to recognize that
> there is no content-length and that we're using HTTP/1.0, and have it
> provide the content length (easier said than done, of course). With
> such code, we can use a filter which zaps the content length field
> (since it expects to invalidate it) but still have Netscape behave
> suitably.
>
> Does anybody have any ideas about controls over when we provide the
> Content-Length? Obviously there are some performance issues
> involved. It may be quite painful for certain systems to provide
> Content-Length on certain pages.
Here is a patch to provide content length. I'm not sure when the
content-length calculator should kick in... Under directive control?
Note that it doesn't currently implement the check for r->chunked
because r->chunked hasn't been calculated yet. But hey, we get nice
progress bars with Content-Length anyway... who needs chunked? (only
3% serious)
Comments?
Index: main/http_core.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_core.c,v
retrieving revision 1.190
diff -u -r1.190 http_core.c
--- main/http_core.c 2000/10/26 12:03:07 1.190
+++ main/http_core.c 2000/10/27 02:25:34
@@ -3578,6 +3578,8 @@
ap_register_input_filter("DECHUNK", ap_dechunk_filter, AP_FTYPE_TRANSCODE);
ap_register_input_filter("CORE_IN", core_input_filter, AP_FTYPE_NETWORK);
ap_register_output_filter("HTTP_HEADER", ap_http_header_filter, AP_FTYPE_HTTP_HEADER);
+ ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter,
+ AP_FTYPE_HTTP_HEADER);
ap_register_output_filter("CORE", core_output_filter, AP_FTYPE_NETWORK);
ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter,
AP_FTYPE_CONTENT);
Index: main/http_protocol.c
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/main/http_protocol.c,v
retrieving revision 1.209
diff -u -r1.209 http_protocol.c
--- main/http_protocol.c 2000/10/26 11:05:07 1.209
+++ main/http_protocol.c 2000/10/27 02:25:38
@@ -1417,6 +1417,7 @@
? &r->server->keep_alive_timeout
: &r->server->timeout);
+ ap_add_output_filter("CONTENT_LENGTH", NULL, r, r->connection);
ap_add_output_filter("HTTP_HEADER", NULL, r, r->connection);
/* Get the request... */
@@ -2227,6 +2228,70 @@
AP_DECLARE(void) ap_send_http_header(request_rec *r)
{
+}
+
+struct content_length_ctx {
+ apr_size_t content_length;
+ ap_bucket_brigade *saved;
+};
+
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *f,
+ ap_bucket_brigade *b)
+{
+ request_rec *r = f->r;
+ struct content_length_ctx *ctx;
+ apr_status_t rv;
+ ap_bucket *e;
+
+ /* We'll go to the trouble of providing a content-length only if it is
+ * . not a subrequest
+ * . HTTP/1.0 or higher
+ * . no content length already
+ * . not chunked XXX no logic for this currently
+ */
+ if (r->assbackwards ||
+ apr_table_get(r->headers_out, "Content-Length")) {
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next, b);
+ }
+
+ ctx = f->ctx;
+ if (!ctx) {
+ f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
+ }
+
+ AP_BRIGADE_FOREACH(e, b) {
+ if (!AP_BUCKET_IS_EOS(e)) {
+ if (e->length >= 0) {
+ ctx->content_length += e->length;
+ }
+ else {
+ const char *ignored;
+ apr_ssize_t length;
+
+ rv = ap_bucket_read(e, &ignored, &length, 1);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ ctx->content_length += e->length;
+ }
+ }
+ }
+
+ if (AP_BUCKET_IS_EOS(AP_BRIGADE_LAST(b))) {
+ ap_set_content_length(r, ctx->content_length);
+ if (ctx->saved) {
+ AP_BRIGADE_CONCAT(ctx->saved, b);
+ b = ctx->saved;
+ }
+ return ap_pass_brigade(f->next, b);
+ }
+
+ /* save the brigade; we can't pass any data to the next
+ * filter until we have the entire content length
+ */
+ ap_save_brigade(f, &ctx->saved, &b);
+ return APR_SUCCESS;
}
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b)
Index: include/http_protocol.h
===================================================================
RCS file: /home/cvspublic/apache-2.0/src/include/http_protocol.h,v
retrieving revision 1.35
diff -u -r1.35 http_protocol.h
--- include/http_protocol.h 2000/10/22 13:09:23 1.35
+++ include/http_protocol.h 2000/10/27 02:25:39
@@ -113,6 +113,8 @@
AP_DECLARE(void) ap_send_http_header(request_rec *l);
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, ap_bucket_brigade *b);
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *,
+ ap_bucket_brigade *);
/* Send the response to special method requests */
@@ -533,6 +535,7 @@
apr_status_t ap_http_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode);
apr_status_t ap_dechunk_filter(ap_filter_t *f, ap_bucket_brigade *b, ap_input_mode_t mode);
+
/* Hooks */
/*
--
Jeff Trawick | trawick@ibm.net | PGP public key at web site:
http://www.geocities.com/SiliconValley/Park/9289/
Born in Roswell... married an alien...