1251877Speter/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2251877Speter *
3251877Speter * Licensed under the Apache License, Version 2.0 (the "License");
4251877Speter * you may not use this file except in compliance with the License.
5251877Speter * You may obtain a copy of the License at
6251877Speter *
7251877Speter *     http://www.apache.org/licenses/LICENSE-2.0
8251877Speter *
9251877Speter * Unless required by applicable law or agreed to in writing, software
10251877Speter * distributed under the License is distributed on an "AS IS" BASIS,
11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12251877Speter * See the License for the specific language governing permissions and
13251877Speter * limitations under the License.
14251877Speter */
15251877Speter
16251877Speter#include <apr_lib.h>
17251877Speter#include <apr_strings.h>
18251877Speter#include <apr_date.h>
19251877Speter
20251877Speter#include "serf.h"
21251877Speter#include "serf_bucket_util.h"
22253895Speter#include "serf_private.h"
23251877Speter
24251877Spetertypedef struct {
25251877Speter    serf_bucket_t *stream;
26251877Speter    serf_bucket_t *body;        /* Pointer to the stream wrapping the body. */
27251877Speter    serf_bucket_t *headers;     /* holds parsed headers */
28251877Speter
29251877Speter    enum {
30251877Speter        STATE_STATUS_LINE,      /* reading status line */
31251877Speter        STATE_HEADERS,          /* reading headers */
32251877Speter        STATE_BODY,             /* reading body */
33251877Speter        STATE_TRAILERS,         /* reading trailers */
34251877Speter        STATE_DONE              /* we've sent EOF */
35251877Speter    } state;
36251877Speter
37251877Speter    /* Buffer for accumulating a line from the response. */
38251877Speter    serf_linebuf_t linebuf;
39251877Speter
40251877Speter    serf_status_line sl;
41251877Speter
42251877Speter    int chunked;                /* Do we need to read trailers? */
43251877Speter    int head_req;               /* Was this a HEAD request? */
44251877Speter} response_context_t;
45251877Speter
46251877Speter
47251877Speterserf_bucket_t *serf_bucket_response_create(
48251877Speter    serf_bucket_t *stream,
49251877Speter    serf_bucket_alloc_t *allocator)
50251877Speter{
51251877Speter    response_context_t *ctx;
52251877Speter
53251877Speter    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
54251877Speter    ctx->stream = stream;
55251877Speter    ctx->body = NULL;
56251877Speter    ctx->headers = serf_bucket_headers_create(allocator);
57251877Speter    ctx->state = STATE_STATUS_LINE;
58251877Speter    ctx->chunked = 0;
59251877Speter    ctx->head_req = 0;
60251877Speter
61251877Speter    serf_linebuf_init(&ctx->linebuf);
62251877Speter
63251877Speter    return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
64251877Speter}
65251877Speter
66251877Spetervoid serf_bucket_response_set_head(
67251877Speter    serf_bucket_t *bucket)
68251877Speter{
69251877Speter    response_context_t *ctx = bucket->data;
70251877Speter
71251877Speter    ctx->head_req = 1;
72251877Speter}
73251877Speter
74251877Speterserf_bucket_t *serf_bucket_response_get_headers(
75251877Speter    serf_bucket_t *bucket)
76251877Speter{
77251877Speter    return ((response_context_t *)bucket->data)->headers;
78251877Speter}
79251877Speter
80251877Speter
81251877Speterstatic void serf_response_destroy_and_data(serf_bucket_t *bucket)
82251877Speter{
83251877Speter    response_context_t *ctx = bucket->data;
84251877Speter
85251877Speter    if (ctx->state != STATE_STATUS_LINE) {
86251877Speter        serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
87251877Speter    }
88251877Speter
89251877Speter    serf_bucket_destroy(ctx->stream);
90251877Speter    if (ctx->body != NULL)
91251877Speter        serf_bucket_destroy(ctx->body);
92251877Speter    serf_bucket_destroy(ctx->headers);
93251877Speter
94251877Speter    serf_default_destroy_and_data(bucket);
95251877Speter}
96251877Speter
97251877Speterstatic apr_status_t fetch_line(response_context_t *ctx, int acceptable)
98251877Speter{
99251877Speter    return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
100251877Speter}
101251877Speter
102251877Speterstatic apr_status_t parse_status_line(response_context_t *ctx,
103251877Speter                                      serf_bucket_alloc_t *allocator)
104251877Speter{
105251877Speter    int res;
106251877Speter    char *reason; /* ### stupid APR interface makes this non-const */
107251877Speter
108251877Speter    /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */
109251877Speter    res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
110251877Speter    if (!res) {
111251877Speter        /* Not an HTTP response?  Well, at least we won't understand it. */
112251877Speter        return SERF_ERROR_BAD_HTTP_RESPONSE;
113251877Speter    }
114251877Speter
115251877Speter    ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
116251877Speter                                        ctx->linebuf.line[7] - '0');
117251877Speter    ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
118251877Speter
119251877Speter    /* Skip leading spaces for the reason string. */
120251877Speter    if (apr_isspace(*reason)) {
121251877Speter        reason++;
122251877Speter    }
123251877Speter
124251877Speter    /* Copy the reason value out of the line buffer. */
125251877Speter    ctx->sl.reason = serf_bstrmemdup(allocator, reason,
126251877Speter                                     ctx->linebuf.used
127251877Speter                                     - (reason - ctx->linebuf.line));
128251877Speter
129251877Speter    return APR_SUCCESS;
130251877Speter}
131251877Speter
132251877Speter/* This code should be replaced with header buckets. */
133251877Speterstatic apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
134251877Speter{
135251877Speter    apr_status_t status;
136251877Speter
137251877Speter    /* RFC 2616 says that CRLF is the only line ending, but we can easily
138251877Speter     * accept any kind of line ending.
139251877Speter     */
140251877Speter    status = fetch_line(ctx, SERF_NEWLINE_ANY);
141251877Speter    if (SERF_BUCKET_READ_ERROR(status)) {
142251877Speter        return status;
143251877Speter    }
144251877Speter    /* Something was read. Process it. */
145251877Speter
146251877Speter    if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
147251877Speter        const char *end_key;
148251877Speter        const char *c;
149251877Speter
150251877Speter        end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
151251877Speter        if (!c) {
152251877Speter            /* Bad headers? */
153251877Speter            return SERF_ERROR_BAD_HTTP_RESPONSE;
154251877Speter        }
155251877Speter
156251877Speter        /* Skip over initial ':' */
157251877Speter        c++;
158251877Speter
159251877Speter        /* And skip all whitespaces. */
160251877Speter        for(; c < ctx->linebuf.line + ctx->linebuf.used; c++)
161251877Speter        {
162251877Speter            if (!apr_isspace(*c))
163251877Speter            {
164251877Speter              break;
165251877Speter            }
166251877Speter        }
167251877Speter
168251877Speter        /* Always copy the headers (from the linebuf into new mem). */
169251877Speter        /* ### we should be able to optimize some mem copies */
170251877Speter        serf_bucket_headers_setx(
171251877Speter            ctx->headers,
172251877Speter            ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
173251877Speter            c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
174251877Speter    }
175251877Speter
176251877Speter    return status;
177251877Speter}
178251877Speter
179251877Speter/* Perform one iteration of the state machine.
180251877Speter *
181251877Speter * Will return when one the following conditions occurred:
182251877Speter *  1) a state change
183251877Speter *  2) an error
184251877Speter *  3) the stream is not ready or at EOF
185251877Speter *  4) APR_SUCCESS, meaning the machine can be run again immediately
186251877Speter */
187251877Speterstatic apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
188251877Speter{
189251877Speter    apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
190251877Speter
191251877Speter    switch (ctx->state) {
192251877Speter    case STATE_STATUS_LINE:
193251877Speter        /* RFC 2616 says that CRLF is the only line ending, but we can easily
194251877Speter         * accept any kind of line ending.
195251877Speter         */
196251877Speter        status = fetch_line(ctx, SERF_NEWLINE_ANY);
197251877Speter        if (SERF_BUCKET_READ_ERROR(status))
198251877Speter            return status;
199251877Speter
200251877Speter        if (ctx->linebuf.state == SERF_LINEBUF_READY) {
201251877Speter            /* The Status-Line is in the line buffer. Process it. */
202251877Speter            status = parse_status_line(ctx, bkt->allocator);
203251877Speter            if (status)
204251877Speter                return status;
205251877Speter
206251877Speter            /* Good times ahead: we're switching protocols! */
207251877Speter            if (ctx->sl.code == 101) {
208251877Speter                ctx->body =
209251877Speter                    serf_bucket_barrier_create(ctx->stream, bkt->allocator);
210251877Speter                ctx->state = STATE_DONE;
211251877Speter                break;
212251877Speter            }
213251877Speter
214251877Speter            /* Okay... move on to reading the headers. */
215251877Speter            ctx->state = STATE_HEADERS;
216251877Speter        }
217251877Speter        else {
218251877Speter            /* The connection closed before we could get the next
219251877Speter             * response.  Treat the request as lost so that our upper
220251877Speter             * end knows the server never tried to give us a response.
221251877Speter             */
222251877Speter            if (APR_STATUS_IS_EOF(status)) {
223251877Speter                return SERF_ERROR_REQUEST_LOST;
224251877Speter            }
225251877Speter        }
226251877Speter        break;
227251877Speter    case STATE_HEADERS:
228251877Speter        status = fetch_headers(bkt, ctx);
229251877Speter        if (SERF_BUCKET_READ_ERROR(status))
230251877Speter            return status;
231251877Speter
232251877Speter        /* If an empty line was read, then we hit the end of the headers.
233251877Speter         * Move on to the body.
234251877Speter         */
235251877Speter        if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
236251877Speter            const void *v;
237251877Speter
238251877Speter            /* Advance the state. */
239251877Speter            ctx->state = STATE_BODY;
240251877Speter
241251877Speter            ctx->body =
242251877Speter                serf_bucket_barrier_create(ctx->stream, bkt->allocator);
243251877Speter
244251877Speter            /* Are we C-L, chunked, or conn close? */
245251877Speter            v = serf_bucket_headers_get(ctx->headers, "Content-Length");
246251877Speter            if (v) {
247251877Speter                apr_uint64_t length;
248251877Speter                length = apr_strtoi64(v, NULL, 10);
249251877Speter                if (errno == ERANGE) {
250251877Speter                    return APR_FROM_OS_ERROR(ERANGE);
251251877Speter                }
252251877Speter                ctx->body = serf_bucket_response_body_create(
253251877Speter                              ctx->body, length, bkt->allocator);
254251877Speter            }
255251877Speter            else {
256251877Speter                v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
257251877Speter
258251877Speter                /* Need to handle multiple transfer-encoding. */
259251877Speter                if (v && strcasecmp("chunked", v) == 0) {
260251877Speter                    ctx->chunked = 1;
261251877Speter                    ctx->body = serf_bucket_dechunk_create(ctx->body,
262251877Speter                                                           bkt->allocator);
263251877Speter                }
264251877Speter
265251877Speter                if (!v && (ctx->sl.code == 204 || ctx->sl.code == 304)) {
266251877Speter                    ctx->state = STATE_DONE;
267251877Speter                }
268251877Speter            }
269251877Speter            v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
270251877Speter            if (v) {
271251877Speter                /* Need to handle multiple content-encoding. */
272251877Speter                if (v && strcasecmp("gzip", v) == 0) {
273251877Speter                    ctx->body =
274251877Speter                        serf_bucket_deflate_create(ctx->body, bkt->allocator,
275251877Speter                                                   SERF_DEFLATE_GZIP);
276251877Speter                }
277251877Speter                else if (v && strcasecmp("deflate", v) == 0) {
278251877Speter                    ctx->body =
279251877Speter                        serf_bucket_deflate_create(ctx->body, bkt->allocator,
280251877Speter                                                   SERF_DEFLATE_DEFLATE);
281251877Speter                }
282251877Speter            }
283251877Speter            /* If we're a HEAD request, we don't receive a body. */
284251877Speter            if (ctx->head_req) {
285251877Speter                ctx->state = STATE_DONE;
286251877Speter            }
287251877Speter        }
288251877Speter        break;
289251877Speter    case STATE_BODY:
290251877Speter        /* Don't do anything. */
291251877Speter        break;
292251877Speter    case STATE_TRAILERS:
293251877Speter        status = fetch_headers(bkt, ctx);
294251877Speter        if (SERF_BUCKET_READ_ERROR(status))
295251877Speter            return status;
296251877Speter
297251877Speter        /* If an empty line was read, then we're done. */
298251877Speter        if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
299251877Speter            ctx->state = STATE_DONE;
300251877Speter            return APR_EOF;
301251877Speter        }
302251877Speter        break;
303251877Speter    case STATE_DONE:
304251877Speter        return APR_EOF;
305251877Speter    default:
306251877Speter        /* Not reachable */
307251877Speter        return APR_EGENERAL;
308251877Speter    }
309251877Speter
310251877Speter    return status;
311251877Speter}
312251877Speter
313251877Speterstatic apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
314251877Speter{
315251877Speter    apr_status_t status;
316251877Speter
317251877Speter    /* Keep reading and moving through states if we aren't at the BODY */
318251877Speter    while (ctx->state != STATE_BODY) {
319251877Speter        status = run_machine(bkt, ctx);
320251877Speter
321251877Speter        /* Anything other than APR_SUCCESS means that we cannot immediately
322251877Speter         * read again (for now).
323251877Speter         */
324251877Speter        if (status)
325251877Speter            return status;
326251877Speter    }
327251877Speter    /* in STATE_BODY */
328251877Speter
329251877Speter    return APR_SUCCESS;
330251877Speter}
331251877Speter
332251877Speterapr_status_t serf_bucket_response_wait_for_headers(
333251877Speter    serf_bucket_t *bucket)
334251877Speter{
335251877Speter    response_context_t *ctx = bucket->data;
336251877Speter
337251877Speter    return wait_for_body(bucket, ctx);
338251877Speter}
339251877Speter
340251877Speterapr_status_t serf_bucket_response_status(
341251877Speter    serf_bucket_t *bkt,
342251877Speter    serf_status_line *sline)
343251877Speter{
344251877Speter    response_context_t *ctx = bkt->data;
345251877Speter    apr_status_t status;
346251877Speter
347251877Speter    if (ctx->state != STATE_STATUS_LINE) {
348251877Speter        /* We already read it and moved on. Just return it. */
349251877Speter        *sline = ctx->sl;
350251877Speter        return APR_SUCCESS;
351251877Speter    }
352251877Speter
353251877Speter    /* Running the state machine once will advance the machine, or state
354251877Speter     * that the stream isn't ready with enough data. There isn't ever a
355251877Speter     * need to run the machine more than once to try and satisfy this. We
356251877Speter     * have to look at the state to tell whether it advanced, though, as
357251877Speter     * it is quite possible to advance *and* to return APR_EAGAIN.
358251877Speter     */
359251877Speter    status = run_machine(bkt, ctx);
360251877Speter    if (ctx->state == STATE_HEADERS) {
361251877Speter        *sline = ctx->sl;
362251877Speter    }
363251877Speter    else {
364251877Speter        /* Indicate that we don't have the information yet. */
365251877Speter        sline->version = 0;
366251877Speter    }
367251877Speter
368251877Speter    return status;
369251877Speter}
370251877Speter
371251877Speterstatic apr_status_t serf_response_read(serf_bucket_t *bucket,
372251877Speter                                       apr_size_t requested,
373251877Speter                                       const char **data, apr_size_t *len)
374251877Speter{
375251877Speter    response_context_t *ctx = bucket->data;
376251877Speter    apr_status_t rv;
377251877Speter
378251877Speter    rv = wait_for_body(bucket, ctx);
379251877Speter    if (rv) {
380251877Speter        /* It's not possible to have read anything yet! */
381251877Speter        if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
382251877Speter            *len = 0;
383251877Speter        }
384251877Speter        return rv;
385251877Speter    }
386251877Speter
387251877Speter    rv = serf_bucket_read(ctx->body, requested, data, len);
388251877Speter    if (SERF_BUCKET_READ_ERROR(rv))
389251877Speter        return rv;
390251877Speter
391251877Speter    if (APR_STATUS_IS_EOF(rv)) {
392251877Speter        if (ctx->chunked) {
393251877Speter            ctx->state = STATE_TRAILERS;
394251877Speter            /* Mask the result. */
395251877Speter            rv = APR_SUCCESS;
396251877Speter        } else {
397251877Speter            ctx->state = STATE_DONE;
398251877Speter        }
399251877Speter    }
400251877Speter    return rv;
401251877Speter}
402251877Speter
403251877Speterstatic apr_status_t serf_response_readline(serf_bucket_t *bucket,
404251877Speter                                           int acceptable, int *found,
405251877Speter                                           const char **data, apr_size_t *len)
406251877Speter{
407251877Speter    response_context_t *ctx = bucket->data;
408251877Speter    apr_status_t rv;
409251877Speter
410251877Speter    rv = wait_for_body(bucket, ctx);
411251877Speter    if (rv) {
412251877Speter        return rv;
413251877Speter    }
414251877Speter
415251877Speter    /* Delegate to the stream bucket to do the readline. */
416251877Speter    return serf_bucket_readline(ctx->body, acceptable, found, data, len);
417251877Speter}
418251877Speter
419251877Speterapr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket)
420251877Speter{
421251877Speter    response_context_t *ctx = bucket->data;
422251877Speter    serf_bucket_t *bkt;
423251877Speter    char buf[256];
424251877Speter    int size;
425251877Speter
426251877Speter    serf_bucket_aggregate_become(bucket);
427251877Speter
428251877Speter    /* Add reconstructed status line. */
429251877Speter    size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ",
430251877Speter                        SERF_HTTP_VERSION_MAJOR(ctx->sl.version),
431251877Speter                        SERF_HTTP_VERSION_MINOR(ctx->sl.version),
432251877Speter                        ctx->sl.code);
433251877Speter    bkt = serf_bucket_simple_copy_create(buf, size,
434251877Speter                                         bucket->allocator);
435251877Speter    serf_bucket_aggregate_append(bucket, bkt);
436251877Speter    bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason),
437251877Speter                                         bucket->allocator);
438251877Speter    serf_bucket_aggregate_append(bucket, bkt);
439251877Speter    bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2,
440251877Speter                                        bucket->allocator);
441251877Speter    serf_bucket_aggregate_append(bucket, bkt);
442251877Speter
443251877Speter    /* Add headers and stream buckets in order. */
444251877Speter    serf_bucket_aggregate_append(bucket, ctx->headers);
445251877Speter    serf_bucket_aggregate_append(bucket, ctx->stream);
446251877Speter
447251877Speter    serf_bucket_mem_free(bucket->allocator, ctx);
448251877Speter
449251877Speter    return APR_SUCCESS;
450251877Speter}
451251877Speter
452251877Speter/* ### need to implement */
453251877Speter#define serf_response_peek NULL
454251877Speter
455251877Speterconst serf_bucket_type_t serf_bucket_type_response = {
456251877Speter    "RESPONSE",
457251877Speter    serf_response_read,
458251877Speter    serf_response_readline,
459251877Speter    serf_default_read_iovec,
460251877Speter    serf_default_read_for_sendfile,
461251877Speter    serf_default_read_bucket,
462251877Speter    serf_response_peek,
463251877Speter    serf_response_destroy_and_data,
464251877Speter};
465