OSDN Git Service

* Added serf library.
[modchxj/mod_chxj.git] / src / serf / buckets / response_buckets.c
1 /* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
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 #include <stdlib.h>
17
18 #include <apr_lib.h>
19 #include <apr_strings.h>
20 #include <apr_date.h>
21
22 #include "serf.h"
23 #include "serf_bucket_util.h"
24
25
26 typedef struct {
27     serf_bucket_t *stream;
28     serf_bucket_t *body;        /* Pointer to the stream wrapping the body. */
29     serf_bucket_t *headers;     /* holds parsed headers */
30
31     enum {
32         STATE_STATUS_LINE,      /* reading status line */
33         STATE_HEADERS,          /* reading headers */
34         STATE_BODY,             /* reading body */
35         STATE_TRAILERS,         /* reading trailers */
36         STATE_DONE              /* we've sent EOF */
37     } state;
38
39     /* Buffer for accumulating a line from the response. */
40     serf_linebuf_t linebuf;
41
42     serf_status_line sl;
43
44     int chunked;                /* Do we need to read trailers? */
45     int head_req;               /* Was this a HEAD request? */
46 } response_context_t;
47
48
49 SERF_DECLARE(serf_bucket_t *) serf_bucket_response_create(
50     serf_bucket_t *stream,
51     serf_bucket_alloc_t *allocator)
52 {
53     response_context_t *ctx;
54
55     ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
56     ctx->stream = stream;
57     ctx->body = NULL;
58     ctx->headers = serf_bucket_headers_create(allocator);
59     ctx->state = STATE_STATUS_LINE;
60     ctx->chunked = 0;
61     ctx->head_req = 0;
62
63     serf_linebuf_init(&ctx->linebuf);
64
65     return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
66 }
67
68 SERF_DECLARE(void) serf_bucket_response_set_head(
69     serf_bucket_t *bucket)
70 {
71     response_context_t *ctx = bucket->data;
72
73     ctx->head_req = 1;
74 }
75
76 SERF_DECLARE(serf_bucket_t *) serf_bucket_response_get_headers(
77     serf_bucket_t *bucket)
78 {
79     return ((response_context_t *)bucket->data)->headers;
80 }
81
82
83 static void serf_response_destroy_and_data(serf_bucket_t *bucket)
84 {
85     response_context_t *ctx = bucket->data;
86
87     if (ctx->state != STATE_STATUS_LINE) {
88         serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
89     }
90
91     serf_bucket_destroy(ctx->stream);
92     if (ctx->body != NULL)
93         serf_bucket_destroy(ctx->body);
94     serf_bucket_destroy(ctx->headers);
95
96     serf_default_destroy_and_data(bucket);
97 }
98
99 static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
100 {
101     return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
102 }
103
104 static apr_status_t parse_status_line(response_context_t *ctx,
105                                       serf_bucket_alloc_t *allocator)
106 {
107     int res;
108     char *reason; /* ### stupid APR interface makes this non-const */
109
110     /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */
111     res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
112     if (!res) {
113         /* Not an HTTP response?  Well, at least we won't understand it. */
114         return APR_EGENERAL;
115     }
116
117     ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
118                                         ctx->linebuf.line[7] - '0');
119     ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
120
121     /* Skip leading spaces for the reason string. */
122     if (apr_isspace(*reason)) {
123         reason++;
124     }
125
126     /* Copy the reason value out of the line buffer. */
127     ctx->sl.reason = serf_bstrmemdup(allocator, reason,
128                                      ctx->linebuf.used
129                                      - (reason - ctx->linebuf.line));
130
131     return APR_SUCCESS;
132 }
133
134 /* This code should be replaced with header buckets. */
135 static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
136 {
137     apr_status_t status;
138
139     /* RFC 2616 says that CRLF is the only line ending, but we can easily
140      * accept any kind of line ending.
141      */
142     status = fetch_line(ctx, SERF_NEWLINE_ANY);
143     if (SERF_BUCKET_READ_ERROR(status)) {
144         return status;
145     }
146     /* Something was read. Process it. */
147
148     if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
149         const char *end_key;
150         const char *c;
151
152         end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
153         if (!c) {
154             /* Bad headers? */
155             return APR_EGENERAL;
156         }
157
158         /* Skip over initial : and spaces. */
159         while (apr_isspace(*++c))
160             continue;
161
162         /* Always copy the headers (from the linebuf into new mem). */
163         /* ### we should be able to optimize some mem copies */
164         serf_bucket_headers_setx(
165             ctx->headers,
166             ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
167             c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
168     }
169
170     return status;
171 }
172
173 /* Perform one iteration of the state machine.
174  *
175  * Will return when one the following conditions occurred:
176  *  1) a state change
177  *  2) an error
178  *  3) the stream is not ready or at EOF
179  *  4) APR_SUCCESS, meaning the machine can be run again immediately
180  */
181 static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
182 {
183     apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
184
185     switch (ctx->state) {
186     case STATE_STATUS_LINE:
187         /* RFC 2616 says that CRLF is the only line ending, but we can easily
188          * accept any kind of line ending.
189          */
190         status = fetch_line(ctx, SERF_NEWLINE_ANY);
191         if (SERF_BUCKET_READ_ERROR(status))
192             return status;
193
194         if (ctx->linebuf.state == SERF_LINEBUF_READY) {
195             /* The Status-Line is in the line buffer. Process it. */
196             status = parse_status_line(ctx, bkt->allocator);
197             if (status)
198                 return status;
199
200             /* Okay... move on to reading the headers. */
201             ctx->state = STATE_HEADERS;
202         }
203         else {
204             /* The connection closed before we could get the next
205              * response.  Treat the request as lost so that our upper
206              * end knows the server never tried to give us a response.
207              */
208             if (APR_STATUS_IS_EOF(status)) {
209                 return SERF_ERROR_REQUEST_LOST;
210             }
211         }
212         break;
213     case STATE_HEADERS:
214         status = fetch_headers(bkt, ctx);
215         if (SERF_BUCKET_READ_ERROR(status))
216             return status;
217
218         /* If an empty line was read, then we hit the end of the headers.
219          * Move on to the body.
220          */
221         if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
222             const void *v;
223
224             /* Advance the state. */
225             ctx->state = STATE_BODY;
226
227             ctx->body =
228                 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
229
230             /* Are we C-L, chunked, or conn close? */
231             v = serf_bucket_headers_get(ctx->headers, "Content-Length");
232             if (v) {
233                 apr_size_t length;
234                 length = apr_strtoi64(v, NULL, 10);
235                 if (errno == ERANGE) {
236                     return APR_FROM_OS_ERROR(ERANGE);
237                 }
238                 ctx->body = serf_bucket_limit_create(ctx->body, length,
239                                                      bkt->allocator);
240             }
241             else {
242                 v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
243
244                 /* Need to handle multiple transfer-encoding. */
245                 if (v && strcasecmp("chunked", v) == 0) {
246                     ctx->chunked = 1;
247                     ctx->body = serf_bucket_dechunk_create(ctx->body,
248                                                            bkt->allocator);
249                 }
250
251                 if (!v && (ctx->sl.code == 204 || ctx->sl.code == 304)) {
252                     ctx->state = STATE_DONE;
253                 }
254             }
255             v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
256             if (v) {
257                 /* Need to handle multiple content-encoding. */
258                 if (v && strcasecmp("gzip", v) == 0) {
259                     ctx->body =
260                         serf_bucket_deflate_create(ctx->body, bkt->allocator,
261                                                    SERF_DEFLATE_GZIP);
262                 }
263                 else if (v && strcasecmp("deflate", v) == 0) {
264                     ctx->body =
265                         serf_bucket_deflate_create(ctx->body, bkt->allocator,
266                                                    SERF_DEFLATE_DEFLATE);
267                 }
268             }
269             /* If we're a HEAD request, we don't receive a body. */
270             if (ctx->head_req) {
271                 ctx->state = STATE_DONE;
272             }
273         }
274         break;
275     case STATE_BODY:
276         /* Don't do anything. */
277         break;
278     case STATE_TRAILERS:
279         status = fetch_headers(bkt, ctx);
280         if (SERF_BUCKET_READ_ERROR(status))
281             return status;
282
283         /* If an empty line was read, then we're done. */
284         if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
285             ctx->state = STATE_DONE;
286             return APR_EOF;
287         }
288         break;
289     case STATE_DONE:
290         return APR_EOF;
291     default:
292         /* Not reachable */
293         return APR_EGENERAL;
294     }
295
296     return status;
297 }
298
299 static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
300 {
301     apr_status_t status;
302
303     /* Keep reading and moving through states if we aren't at the BODY */
304     while (ctx->state != STATE_BODY) {
305         status = run_machine(bkt, ctx);
306
307         /* Anything other than APR_SUCCESS means that we cannot immediately
308          * read again (for now).
309          */
310         if (status)
311             return status;
312     }
313     /* in STATE_BODY */
314
315     return APR_SUCCESS;
316 }
317
318 SERF_DECLARE(apr_status_t) serf_bucket_response_wait_for_headers(
319     serf_bucket_t *bucket)
320 {
321     response_context_t *ctx = bucket->data;
322
323     return wait_for_body(bucket, ctx);
324 }
325
326 SERF_DECLARE(apr_status_t) serf_bucket_response_status(
327     serf_bucket_t *bkt,
328     serf_status_line *sline)
329 {
330     response_context_t *ctx = bkt->data;
331     apr_status_t status;
332
333     if (ctx->state != STATE_STATUS_LINE) {
334         /* We already read it and moved on. Just return it. */
335         *sline = ctx->sl;
336         return APR_SUCCESS;
337     }
338
339     /* Running the state machine once will advance the machine, or state
340      * that the stream isn't ready with enough data. There isn't ever a
341      * need to run the machine more than once to try and satisfy this. We
342      * have to look at the state to tell whether it advanced, though, as
343      * it is quite possible to advance *and* to return APR_EAGAIN.
344      */
345     status = run_machine(bkt, ctx);
346     if (ctx->state == STATE_HEADERS) {
347         *sline = ctx->sl;
348     }
349     else {
350         /* Indicate that we don't have the information yet. */
351         sline->version = 0;
352     }
353
354     return status;
355 }
356
357 static apr_status_t serf_response_read(serf_bucket_t *bucket,
358                                        apr_size_t requested,
359                                        const char **data, apr_size_t *len)
360 {
361     response_context_t *ctx = bucket->data;
362     apr_status_t rv;
363
364     rv = wait_for_body(bucket, ctx);
365     if (rv) {
366         /* It's not possible to have read anything yet! */
367         if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
368             *len = 0;
369         }
370         return rv;
371     }
372
373     rv = serf_bucket_read(ctx->body, requested, data, len);
374     if (APR_STATUS_IS_EOF(rv)) {
375         if (ctx->chunked) {
376             ctx->state = STATE_TRAILERS;
377             /* Mask the result. */
378             rv = APR_SUCCESS;
379         }
380         else {
381             ctx->state = STATE_DONE;
382         }
383     }
384     return rv;
385 }
386
387 static apr_status_t serf_response_readline(serf_bucket_t *bucket,
388                                            int acceptable, int *found,
389                                            const char **data, apr_size_t *len)
390 {
391     response_context_t *ctx = bucket->data;
392     apr_status_t rv;
393
394     rv = wait_for_body(bucket, ctx);
395     if (rv) {
396         return rv;
397     }
398
399     /* Delegate to the stream bucket to do the readline. */
400     return serf_bucket_readline(ctx->body, acceptable, found, data, len);
401 }
402
403 /* ### need to implement */
404 #define serf_response_read_iovec NULL
405 #define serf_response_read_for_sendfile NULL
406 #define serf_response_peek NULL
407
408 SERF_DECLARE_DATA const serf_bucket_type_t serf_bucket_type_response = {
409     "RESPONSE",
410     serf_response_read,
411     serf_response_readline,
412     serf_response_read_iovec,
413     serf_response_read_for_sendfile,
414     serf_default_read_bucket,
415     serf_response_peek,
416     serf_response_destroy_and_data,
417 };