1 /* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <apr_strings.h>
21 #include "serf_bucket_util.h"
22 #include "serf_private.h"
25 serf_bucket_t *stream;
26 serf_bucket_t *body; /* Pointer to the stream wrapping the body. */
27 serf_bucket_t *headers; /* holds parsed headers */
30 STATE_STATUS_LINE, /* reading status line */
31 STATE_HEADERS, /* reading headers */
32 STATE_BODY, /* reading body */
33 STATE_TRAILERS, /* reading trailers */
34 STATE_DONE /* we've sent EOF */
37 /* Buffer for accumulating a line from the response. */
38 serf_linebuf_t linebuf;
42 int chunked; /* Do we need to read trailers? */
43 int head_req; /* Was this a HEAD request? */
46 /* Returns 1 if according to RFC2626 this response can have a body, 0 if it
47 must not have a body. */
48 static int expect_body(response_context_t *ctx)
53 /* 100 Continue and 101 Switching Protocols */
54 if (ctx->sl.code >= 100 && ctx->sl.code < 200)
58 if (ctx->sl.code == 204)
63 /* 304 Not Modified */
64 if (ctx->sl.code == 304)
70 serf_bucket_t *serf_bucket_response_create(
71 serf_bucket_t *stream,
72 serf_bucket_alloc_t *allocator)
74 response_context_t *ctx;
76 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
79 ctx->headers = serf_bucket_headers_create(allocator);
80 ctx->state = STATE_STATUS_LINE;
84 serf_linebuf_init(&ctx->linebuf);
86 return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
89 void serf_bucket_response_set_head(
90 serf_bucket_t *bucket)
92 response_context_t *ctx = bucket->data;
97 serf_bucket_t *serf_bucket_response_get_headers(
98 serf_bucket_t *bucket)
100 return ((response_context_t *)bucket->data)->headers;
104 static void serf_response_destroy_and_data(serf_bucket_t *bucket)
106 response_context_t *ctx = bucket->data;
108 if (ctx->state != STATE_STATUS_LINE) {
109 serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
112 serf_bucket_destroy(ctx->stream);
113 if (ctx->body != NULL)
114 serf_bucket_destroy(ctx->body);
115 serf_bucket_destroy(ctx->headers);
117 serf_default_destroy_and_data(bucket);
120 static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
122 return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
125 static apr_status_t parse_status_line(response_context_t *ctx,
126 serf_bucket_alloc_t *allocator)
129 char *reason; /* ### stupid APR interface makes this non-const */
131 /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */
132 res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
134 /* Not an HTTP response? Well, at least we won't understand it. */
135 return SERF_ERROR_BAD_HTTP_RESPONSE;
138 ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
139 ctx->linebuf.line[7] - '0');
140 ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
142 /* Skip leading spaces for the reason string. */
143 if (apr_isspace(*reason)) {
147 /* Copy the reason value out of the line buffer. */
148 ctx->sl.reason = serf_bstrmemdup(allocator, reason,
150 - (reason - ctx->linebuf.line));
155 /* This code should be replaced with header buckets. */
156 static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
160 /* RFC 2616 says that CRLF is the only line ending, but we can easily
161 * accept any kind of line ending.
163 status = fetch_line(ctx, SERF_NEWLINE_ANY);
164 if (SERF_BUCKET_READ_ERROR(status)) {
167 /* Something was read. Process it. */
169 if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
173 end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
176 return SERF_ERROR_BAD_HTTP_RESPONSE;
179 /* Skip over initial ':' */
182 /* And skip all whitespaces. */
183 for(; c < ctx->linebuf.line + ctx->linebuf.used; c++)
185 if (!apr_isspace(*c))
191 /* Always copy the headers (from the linebuf into new mem). */
192 /* ### we should be able to optimize some mem copies */
193 serf_bucket_headers_setx(
195 ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
196 c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
202 /* Perform one iteration of the state machine.
204 * Will return when one the following conditions occurred:
207 * 3) the stream is not ready or at EOF
208 * 4) APR_SUCCESS, meaning the machine can be run again immediately
210 static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
212 apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
214 switch (ctx->state) {
215 case STATE_STATUS_LINE:
216 /* RFC 2616 says that CRLF is the only line ending, but we can easily
217 * accept any kind of line ending.
219 status = fetch_line(ctx, SERF_NEWLINE_ANY);
220 if (SERF_BUCKET_READ_ERROR(status))
223 if (ctx->linebuf.state == SERF_LINEBUF_READY) {
224 /* The Status-Line is in the line buffer. Process it. */
225 status = parse_status_line(ctx, bkt->allocator);
229 /* Good times ahead: we're switching protocols! */
230 if (ctx->sl.code == 101) {
232 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
233 ctx->state = STATE_DONE;
237 /* Okay... move on to reading the headers. */
238 ctx->state = STATE_HEADERS;
241 /* The connection closed before we could get the next
242 * response. Treat the request as lost so that our upper
243 * end knows the server never tried to give us a response.
245 if (APR_STATUS_IS_EOF(status)) {
246 return SERF_ERROR_REQUEST_LOST;
251 status = fetch_headers(bkt, ctx);
252 if (SERF_BUCKET_READ_ERROR(status))
255 /* If an empty line was read, then we hit the end of the headers.
256 * Move on to the body.
258 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
261 /* Advance the state. */
262 ctx->state = STATE_BODY;
264 /* If this is a response to a HEAD request, or code == 1xx,204 or304
265 then we don't receive a real body. */
266 if (!expect_body(ctx)) {
267 ctx->body = serf_bucket_simple_create(NULL, 0, NULL, NULL,
269 ctx->state = STATE_BODY;
274 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
276 /* Are we C-L, chunked, or conn close? */
277 v = serf_bucket_headers_get(ctx->headers, "Content-Length");
280 length = apr_strtoi64(v, NULL, 10);
281 if (errno == ERANGE) {
282 return APR_FROM_OS_ERROR(ERANGE);
284 ctx->body = serf_bucket_response_body_create(
285 ctx->body, length, bkt->allocator);
288 v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
290 /* Need to handle multiple transfer-encoding. */
291 if (v && strcasecmp("chunked", v) == 0) {
293 ctx->body = serf_bucket_dechunk_create(ctx->body,
297 v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
299 /* Need to handle multiple content-encoding. */
300 if (v && strcasecmp("gzip", v) == 0) {
302 serf_bucket_deflate_create(ctx->body, bkt->allocator,
305 else if (v && strcasecmp("deflate", v) == 0) {
307 serf_bucket_deflate_create(ctx->body, bkt->allocator,
308 SERF_DEFLATE_DEFLATE);
314 /* Don't do anything. */
317 status = fetch_headers(bkt, ctx);
318 if (SERF_BUCKET_READ_ERROR(status))
321 /* If an empty line was read, then we're done. */
322 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
323 ctx->state = STATE_DONE;
337 static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
341 /* Keep reading and moving through states if we aren't at the BODY */
342 while (ctx->state != STATE_BODY) {
343 status = run_machine(bkt, ctx);
345 /* Anything other than APR_SUCCESS means that we cannot immediately
346 * read again (for now).
356 apr_status_t serf_bucket_response_wait_for_headers(
357 serf_bucket_t *bucket)
359 response_context_t *ctx = bucket->data;
361 return wait_for_body(bucket, ctx);
364 apr_status_t serf_bucket_response_status(
366 serf_status_line *sline)
368 response_context_t *ctx = bkt->data;
371 if (ctx->state != STATE_STATUS_LINE) {
372 /* We already read it and moved on. Just return it. */
377 /* Running the state machine once will advance the machine, or state
378 * that the stream isn't ready with enough data. There isn't ever a
379 * need to run the machine more than once to try and satisfy this. We
380 * have to look at the state to tell whether it advanced, though, as
381 * it is quite possible to advance *and* to return APR_EAGAIN.
383 status = run_machine(bkt, ctx);
384 if (ctx->state == STATE_HEADERS) {
388 /* Indicate that we don't have the information yet. */
395 static apr_status_t serf_response_read(serf_bucket_t *bucket,
396 apr_size_t requested,
397 const char **data, apr_size_t *len)
399 response_context_t *ctx = bucket->data;
402 rv = wait_for_body(bucket, ctx);
404 /* It's not possible to have read anything yet! */
405 if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
411 rv = serf_bucket_read(ctx->body, requested, data, len);
412 if (SERF_BUCKET_READ_ERROR(rv))
415 if (APR_STATUS_IS_EOF(rv)) {
417 ctx->state = STATE_TRAILERS;
418 /* Mask the result. */
421 ctx->state = STATE_DONE;
427 static apr_status_t serf_response_readline(serf_bucket_t *bucket,
428 int acceptable, int *found,
429 const char **data, apr_size_t *len)
431 response_context_t *ctx = bucket->data;
434 rv = wait_for_body(bucket, ctx);
439 /* Delegate to the stream bucket to do the readline. */
440 return serf_bucket_readline(ctx->body, acceptable, found, data, len);
443 apr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket)
445 response_context_t *ctx = bucket->data;
450 serf_bucket_aggregate_become(bucket);
452 /* Add reconstructed status line. */
453 size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ",
454 SERF_HTTP_VERSION_MAJOR(ctx->sl.version),
455 SERF_HTTP_VERSION_MINOR(ctx->sl.version),
457 bkt = serf_bucket_simple_copy_create(buf, size,
459 serf_bucket_aggregate_append(bucket, bkt);
460 bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason),
462 serf_bucket_aggregate_append(bucket, bkt);
463 bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2,
465 serf_bucket_aggregate_append(bucket, bkt);
467 /* Add headers and stream buckets in order. */
468 serf_bucket_aggregate_append(bucket, ctx->headers);
469 serf_bucket_aggregate_append(bucket, ctx->stream);
471 serf_bucket_mem_free(bucket->allocator, ctx);
476 /* ### need to implement */
477 #define serf_response_peek NULL
479 const serf_bucket_type_t serf_bucket_type_response = {
482 serf_response_readline,
483 serf_default_read_iovec,
484 serf_default_read_for_sendfile,
485 serf_default_read_bucket,
487 serf_response_destroy_and_data,