]> granicus.if.org Git - apache/blob - modules/http/chunk_filter.c
Keep track of time taken to process requests again.
[apache] / modules / http / chunk_filter.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /*
17  * chunk_filter.c --- HTTP/1.1 chunked transfer encoding filter.
18  */
19
20 #include "apr_strings.h"
21 #include "apr_thread_proc.h"    /* for RLIMIT stuff */
22
23 #define APR_WANT_STRFUNC
24 #include "apr_want.h"
25
26 #define CORE_PRIVATE
27 #include "httpd.h"
28 #include "http_config.h"
29 #include "http_connection.h"
30 #include "http_core.h"
31 #include "http_protocol.h"      /* For index_of_response().  Grump. */
32 #include "http_request.h"
33
34 #include "util_filter.h"
35 #include "util_ebcdic.h"
36 #include "ap_mpm.h"
37 #include "scoreboard.h"
38
39 #include "mod_core.h"
40
41 apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
42 {
43 #define ASCII_CRLF  "\015\012"
44 #define ASCII_ZERO  "\060"
45     conn_rec *c = f->r->connection;
46     apr_bucket_brigade *more;
47     apr_bucket *e;
48     apr_status_t rv;
49
50     for (more = NULL; b; b = more, more = NULL) {
51         apr_off_t bytes = 0;
52         apr_bucket *eos = NULL;
53         apr_bucket *flush = NULL;
54         /* XXX: chunk_hdr must remain at this scope since it is used in a 
55          *      transient bucket.
56          */
57         char chunk_hdr[20]; /* enough space for the snprintf below */
58
59
60         for (e = APR_BRIGADE_FIRST(b);
61              e != APR_BRIGADE_SENTINEL(b);
62              e = APR_BUCKET_NEXT(e))
63         {
64             if (APR_BUCKET_IS_EOS(e)) {
65                 /* there shouldn't be anything after the eos */
66                 eos = e;
67                 break;
68             }
69             if (APR_BUCKET_IS_FLUSH(e)) {
70                 flush = e;
71             }
72             else if (e->length == (apr_size_t)-1) {
73                 /* unknown amount of data (e.g. a pipe) */
74                 const char *data;
75                 apr_size_t len;
76
77                 rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
78                 if (rv != APR_SUCCESS) {
79                     return rv;
80                 }
81                 if (len > 0) {
82                     /*
83                      * There may be a new next bucket representing the
84                      * rest of the data stream on which a read() may
85                      * block so we pass down what we have so far.
86                      */
87                     bytes += len;
88                     more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
89                     break;
90                 }
91                 else {
92                     /* If there was nothing in this bucket then we can
93                      * safely move on to the next one without pausing
94                      * to pass down what we have counted up so far.
95                      */
96                     continue;
97                 }
98             }
99             else {
100                 bytes += e->length;
101             }
102         }
103
104         /*
105          * XXX: if there aren't very many bytes at this point it may
106          * be a good idea to set them aside and return for more,
107          * unless we haven't finished counting this brigade yet.
108          */
109         /* if there are content bytes, then wrap them in a chunk */
110         if (bytes > 0) {
111             apr_size_t hdr_len;
112             /*
113              * Insert the chunk header, specifying the number of bytes in
114              * the chunk.
115              */
116             hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
117                                    "%" APR_UINT64_T_HEX_FMT CRLF, (apr_uint64_t)bytes);
118             ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
119             e = apr_bucket_transient_create(chunk_hdr, hdr_len,
120                                             c->bucket_alloc);
121             APR_BRIGADE_INSERT_HEAD(b, e);
122
123             /*
124              * Insert the end-of-chunk CRLF before an EOS or
125              * FLUSH bucket, or appended to the brigade
126              */
127             e = apr_bucket_immortal_create(ASCII_CRLF, 2, c->bucket_alloc);
128             if (eos != NULL) {
129                 APR_BUCKET_INSERT_BEFORE(eos, e);
130             }
131             else if (flush != NULL) {
132                 APR_BUCKET_INSERT_BEFORE(flush, e);
133             }
134             else {
135                 APR_BRIGADE_INSERT_TAIL(b, e);
136             }
137         }
138
139         /* RFC 2616, Section 3.6.1
140          *
141          * If there is an EOS bucket, then prefix it with:
142          *   1) the last-chunk marker ("0" CRLF)
143          *   2) the trailer
144          *   3) the end-of-chunked body CRLF
145          *
146          * If there is no EOS bucket, then do nothing.
147          *
148          * XXX: it would be nice to combine this with the end-of-chunk
149          * marker above, but this is a bit more straight-forward for
150          * now.
151          */
152         if (eos != NULL) {
153             /* XXX: (2) trailers ... does not yet exist */
154             e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
155                                            /* <trailers> */
156                                            ASCII_CRLF, 5, c->bucket_alloc);
157             APR_BUCKET_INSERT_BEFORE(eos, e);
158         }
159
160         /* pass the brigade to the next filter. */
161         rv = ap_pass_brigade(f->next, b);
162         if (rv != APR_SUCCESS || eos != NULL) {
163             return rv;
164         }
165     }
166     return APR_SUCCESS;
167 }