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? */
47 serf_bucket_t *serf_bucket_response_create(
48 serf_bucket_t *stream,
49 serf_bucket_alloc_t *allocator)
51 response_context_t *ctx;
53 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
56 ctx->headers = serf_bucket_headers_create(allocator);
57 ctx->state = STATE_STATUS_LINE;
61 serf_linebuf_init(&ctx->linebuf);
63 return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
66 void serf_bucket_response_set_head(
67 serf_bucket_t *bucket)
69 response_context_t *ctx = bucket->data;
74 serf_bucket_t *serf_bucket_response_get_headers(
75 serf_bucket_t *bucket)
77 return ((response_context_t *)bucket->data)->headers;
81 static void serf_response_destroy_and_data(serf_bucket_t *bucket)
83 response_context_t *ctx = bucket->data;
85 if (ctx->state != STATE_STATUS_LINE) {
86 serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
89 serf_bucket_destroy(ctx->stream);
90 if (ctx->body != NULL)
91 serf_bucket_destroy(ctx->body);
92 serf_bucket_destroy(ctx->headers);
94 serf_default_destroy_and_data(bucket);
97 static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
99 return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
102 static apr_status_t parse_status_line(response_context_t *ctx,
103 serf_bucket_alloc_t *allocator)
106 char *reason; /* ### stupid APR interface makes this non-const */
108 /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */
109 res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
111 /* Not an HTTP response? Well, at least we won't understand it. */
112 return SERF_ERROR_BAD_HTTP_RESPONSE;
115 ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0',
116 ctx->linebuf.line[7] - '0');
117 ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10);
119 /* Skip leading spaces for the reason string. */
120 if (apr_isspace(*reason)) {
124 /* Copy the reason value out of the line buffer. */
125 ctx->sl.reason = serf_bstrmemdup(allocator, reason,
127 - (reason - ctx->linebuf.line));
132 /* This code should be replaced with header buckets. */
133 static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
137 /* RFC 2616 says that CRLF is the only line ending, but we can easily
138 * accept any kind of line ending.
140 status = fetch_line(ctx, SERF_NEWLINE_ANY);
141 if (SERF_BUCKET_READ_ERROR(status)) {
144 /* Something was read. Process it. */
146 if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
150 end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
153 return SERF_ERROR_BAD_HTTP_RESPONSE;
156 /* Skip over initial ':' */
159 /* And skip all whitespaces. */
160 for(; c < ctx->linebuf.line + ctx->linebuf.used; c++)
162 if (!apr_isspace(*c))
168 /* Always copy the headers (from the linebuf into new mem). */
169 /* ### we should be able to optimize some mem copies */
170 serf_bucket_headers_setx(
172 ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
173 c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
179 /* Perform one iteration of the state machine.
181 * Will return when one the following conditions occurred:
184 * 3) the stream is not ready or at EOF
185 * 4) APR_SUCCESS, meaning the machine can be run again immediately
187 static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
189 apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
191 switch (ctx->state) {
192 case STATE_STATUS_LINE:
193 /* RFC 2616 says that CRLF is the only line ending, but we can easily
194 * accept any kind of line ending.
196 status = fetch_line(ctx, SERF_NEWLINE_ANY);
197 if (SERF_BUCKET_READ_ERROR(status))
200 if (ctx->linebuf.state == SERF_LINEBUF_READY) {
201 /* The Status-Line is in the line buffer. Process it. */
202 status = parse_status_line(ctx, bkt->allocator);
206 /* Good times ahead: we're switching protocols! */
207 if (ctx->sl.code == 101) {
209 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
210 ctx->state = STATE_DONE;
214 /* Okay... move on to reading the headers. */
215 ctx->state = STATE_HEADERS;
218 /* The connection closed before we could get the next
219 * response. Treat the request as lost so that our upper
220 * end knows the server never tried to give us a response.
222 if (APR_STATUS_IS_EOF(status)) {
223 return SERF_ERROR_REQUEST_LOST;
228 status = fetch_headers(bkt, ctx);
229 if (SERF_BUCKET_READ_ERROR(status))
232 /* If an empty line was read, then we hit the end of the headers.
233 * Move on to the body.
235 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
238 /* Advance the state. */
239 ctx->state = STATE_BODY;
242 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
244 /* Are we C-L, chunked, or conn close? */
245 v = serf_bucket_headers_get(ctx->headers, "Content-Length");
248 length = apr_strtoi64(v, NULL, 10);
249 if (errno == ERANGE) {
250 return APR_FROM_OS_ERROR(ERANGE);
252 ctx->body = serf_bucket_response_body_create(
253 ctx->body, length, bkt->allocator);
256 v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
258 /* Need to handle multiple transfer-encoding. */
259 if (v && strcasecmp("chunked", v) == 0) {
261 ctx->body = serf_bucket_dechunk_create(ctx->body,
265 if (!v && (ctx->sl.code == 204 || ctx->sl.code == 304)) {
266 ctx->state = STATE_DONE;
269 v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
271 /* Need to handle multiple content-encoding. */
272 if (v && strcasecmp("gzip", v) == 0) {
274 serf_bucket_deflate_create(ctx->body, bkt->allocator,
277 else if (v && strcasecmp("deflate", v) == 0) {
279 serf_bucket_deflate_create(ctx->body, bkt->allocator,
280 SERF_DEFLATE_DEFLATE);
283 /* If we're a HEAD request, we don't receive a body. */
285 ctx->state = STATE_DONE;
290 /* Don't do anything. */
293 status = fetch_headers(bkt, ctx);
294 if (SERF_BUCKET_READ_ERROR(status))
297 /* If an empty line was read, then we're done. */
298 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
299 ctx->state = STATE_DONE;
313 static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
317 /* Keep reading and moving through states if we aren't at the BODY */
318 while (ctx->state != STATE_BODY) {
319 status = run_machine(bkt, ctx);
321 /* Anything other than APR_SUCCESS means that we cannot immediately
322 * read again (for now).
332 apr_status_t serf_bucket_response_wait_for_headers(
333 serf_bucket_t *bucket)
335 response_context_t *ctx = bucket->data;
337 return wait_for_body(bucket, ctx);
340 apr_status_t serf_bucket_response_status(
342 serf_status_line *sline)
344 response_context_t *ctx = bkt->data;
347 if (ctx->state != STATE_STATUS_LINE) {
348 /* We already read it and moved on. Just return it. */
353 /* Running the state machine once will advance the machine, or state
354 * that the stream isn't ready with enough data. There isn't ever a
355 * need to run the machine more than once to try and satisfy this. We
356 * have to look at the state to tell whether it advanced, though, as
357 * it is quite possible to advance *and* to return APR_EAGAIN.
359 status = run_machine(bkt, ctx);
360 if (ctx->state == STATE_HEADERS) {
364 /* Indicate that we don't have the information yet. */
371 static apr_status_t serf_response_read(serf_bucket_t *bucket,
372 apr_size_t requested,
373 const char **data, apr_size_t *len)
375 response_context_t *ctx = bucket->data;
378 rv = wait_for_body(bucket, ctx);
380 /* It's not possible to have read anything yet! */
381 if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
387 rv = serf_bucket_read(ctx->body, requested, data, len);
388 if (SERF_BUCKET_READ_ERROR(rv))
391 if (APR_STATUS_IS_EOF(rv)) {
393 ctx->state = STATE_TRAILERS;
394 /* Mask the result. */
397 ctx->state = STATE_DONE;
403 static apr_status_t serf_response_readline(serf_bucket_t *bucket,
404 int acceptable, int *found,
405 const char **data, apr_size_t *len)
407 response_context_t *ctx = bucket->data;
410 rv = wait_for_body(bucket, ctx);
415 /* Delegate to the stream bucket to do the readline. */
416 return serf_bucket_readline(ctx->body, acceptable, found, data, len);
419 apr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket)
421 response_context_t *ctx = bucket->data;
426 serf_bucket_aggregate_become(bucket);
428 /* Add reconstructed status line. */
429 size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ",
430 SERF_HTTP_VERSION_MAJOR(ctx->sl.version),
431 SERF_HTTP_VERSION_MINOR(ctx->sl.version),
433 bkt = serf_bucket_simple_copy_create(buf, size,
435 serf_bucket_aggregate_append(bucket, bkt);
436 bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason),
438 serf_bucket_aggregate_append(bucket, bkt);
439 bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2,
441 serf_bucket_aggregate_append(bucket, bkt);
443 /* Add headers and stream buckets in order. */
444 serf_bucket_aggregate_append(bucket, ctx->headers);
445 serf_bucket_aggregate_append(bucket, ctx->stream);
447 serf_bucket_mem_free(bucket->allocator, ctx);
452 /* ### need to implement */
453 #define serf_response_peek NULL
455 const serf_bucket_type_t serf_bucket_type_response = {
458 serf_response_readline,
459 serf_default_read_iovec,
460 serf_default_read_for_sendfile,
461 serf_default_read_bucket,
463 serf_response_destroy_and_data,