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