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.
19 #include <apr_strings.h>
23 #include "serf_bucket_util.h"
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 */
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 */
39 /* Buffer for accumulating a line from the response. */
40 serf_linebuf_t linebuf;
44 int chunked; /* Do we need to read trailers? */
45 int head_req; /* Was this a HEAD request? */
49 SERF_DECLARE(serf_bucket_t *) serf_bucket_response_create(
50 serf_bucket_t *stream,
51 serf_bucket_alloc_t *allocator)
53 response_context_t *ctx;
55 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
58 ctx->headers = serf_bucket_headers_create(allocator);
59 ctx->state = STATE_STATUS_LINE;
63 serf_linebuf_init(&ctx->linebuf);
65 return serf_bucket_create(&serf_bucket_type_response, allocator, ctx);
68 SERF_DECLARE(void) serf_bucket_response_set_head(
69 serf_bucket_t *bucket)
71 response_context_t *ctx = bucket->data;
76 SERF_DECLARE(serf_bucket_t *) serf_bucket_response_get_headers(
77 serf_bucket_t *bucket)
79 return ((response_context_t *)bucket->data)->headers;
83 static void serf_response_destroy_and_data(serf_bucket_t *bucket)
85 response_context_t *ctx = bucket->data;
87 if (ctx->state != STATE_STATUS_LINE) {
88 serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason);
91 serf_bucket_destroy(ctx->stream);
92 if (ctx->body != NULL)
93 serf_bucket_destroy(ctx->body);
94 serf_bucket_destroy(ctx->headers);
96 serf_default_destroy_and_data(bucket);
99 static apr_status_t fetch_line(response_context_t *ctx, int acceptable)
101 return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable);
104 static apr_status_t parse_status_line(response_context_t *ctx,
105 serf_bucket_alloc_t *allocator)
108 char *reason; /* ### stupid APR interface makes this non-const */
110 /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */
111 res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*");
113 /* Not an HTTP response? Well, at least we won't understand it. */
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);
121 /* Skip leading spaces for the reason string. */
122 if (apr_isspace(*reason)) {
126 /* Copy the reason value out of the line buffer. */
127 ctx->sl.reason = serf_bstrmemdup(allocator, reason,
129 - (reason - ctx->linebuf.line));
134 /* This code should be replaced with header buckets. */
135 static apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx)
139 /* RFC 2616 says that CRLF is the only line ending, but we can easily
140 * accept any kind of line ending.
142 status = fetch_line(ctx, SERF_NEWLINE_ANY);
143 if (SERF_BUCKET_READ_ERROR(status)) {
146 /* Something was read. Process it. */
148 if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) {
152 end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used);
158 /* Skip over initial : and spaces. */
159 while (apr_isspace(*++c))
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(
166 ctx->linebuf.line, end_key - ctx->linebuf.line, 1,
167 c, ctx->linebuf.line + ctx->linebuf.used - c, 1);
173 /* Perform one iteration of the state machine.
175 * Will return when one the following conditions occurred:
178 * 3) the stream is not ready or at EOF
179 * 4) APR_SUCCESS, meaning the machine can be run again immediately
181 static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx)
183 apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */
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.
190 status = fetch_line(ctx, SERF_NEWLINE_ANY);
191 if (SERF_BUCKET_READ_ERROR(status))
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);
200 /* Okay... move on to reading the headers. */
201 ctx->state = STATE_HEADERS;
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.
208 if (APR_STATUS_IS_EOF(status)) {
209 return SERF_ERROR_REQUEST_LOST;
214 status = fetch_headers(bkt, ctx);
215 if (SERF_BUCKET_READ_ERROR(status))
218 /* If an empty line was read, then we hit the end of the headers.
219 * Move on to the body.
221 if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) {
224 /* Advance the state. */
225 ctx->state = STATE_BODY;
228 serf_bucket_barrier_create(ctx->stream, bkt->allocator);
230 /* Are we C-L, chunked, or conn close? */
231 v = serf_bucket_headers_get(ctx->headers, "Content-Length");
234 length = apr_strtoi64(v, NULL, 10);
235 if (errno == ERANGE) {
236 return APR_FROM_OS_ERROR(ERANGE);
238 ctx->body = serf_bucket_limit_create(ctx->body, length,
242 v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding");
244 /* Need to handle multiple transfer-encoding. */
245 if (v && strcasecmp("chunked", v) == 0) {
247 ctx->body = serf_bucket_dechunk_create(ctx->body,
251 if (!v && (ctx->sl.code == 204 || ctx->sl.code == 304)) {
252 ctx->state = STATE_DONE;
255 v = serf_bucket_headers_get(ctx->headers, "Content-Encoding");
257 /* Need to handle multiple content-encoding. */
258 if (v && strcasecmp("gzip", v) == 0) {
260 serf_bucket_deflate_create(ctx->body, bkt->allocator,
263 else if (v && strcasecmp("deflate", v) == 0) {
265 serf_bucket_deflate_create(ctx->body, bkt->allocator,
266 SERF_DEFLATE_DEFLATE);
269 /* If we're a HEAD request, we don't receive a body. */
271 ctx->state = STATE_DONE;
276 /* Don't do anything. */
279 status = fetch_headers(bkt, ctx);
280 if (SERF_BUCKET_READ_ERROR(status))
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;
299 static apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx)
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);
307 /* Anything other than APR_SUCCESS means that we cannot immediately
308 * read again (for now).
318 SERF_DECLARE(apr_status_t) serf_bucket_response_wait_for_headers(
319 serf_bucket_t *bucket)
321 response_context_t *ctx = bucket->data;
323 return wait_for_body(bucket, ctx);
326 SERF_DECLARE(apr_status_t) serf_bucket_response_status(
328 serf_status_line *sline)
330 response_context_t *ctx = bkt->data;
333 if (ctx->state != STATE_STATUS_LINE) {
334 /* We already read it and moved on. Just return it. */
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.
345 status = run_machine(bkt, ctx);
346 if (ctx->state == STATE_HEADERS) {
350 /* Indicate that we don't have the information yet. */
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)
361 response_context_t *ctx = bucket->data;
364 rv = wait_for_body(bucket, ctx);
366 /* It's not possible to have read anything yet! */
367 if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) {
373 rv = serf_bucket_read(ctx->body, requested, data, len);
374 if (APR_STATUS_IS_EOF(rv)) {
376 ctx->state = STATE_TRAILERS;
377 /* Mask the result. */
381 ctx->state = STATE_DONE;
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)
391 response_context_t *ctx = bucket->data;
394 rv = wait_for_body(bucket, ctx);
399 /* Delegate to the stream bucket to do the readline. */
400 return serf_bucket_readline(ctx->body, acceptable, found, data, len);
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
408 SERF_DECLARE_DATA const serf_bucket_type_t serf_bucket_type_response = {
411 serf_response_readline,
412 serf_response_read_iovec,
413 serf_response_read_for_sendfile,
414 serf_default_read_bucket,
416 serf_response_destroy_and_data,