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 46262339Speter/* Returns 1 if according to RFC2626 this response can have a body, 0 if it 47262339Speter must not have a body. */ 48262339Speterstatic int expect_body(response_context_t *ctx) 49262339Speter{ 50262339Speter if (ctx->head_req) 51262339Speter return 0; 52251877Speter 53262339Speter /* 100 Continue and 101 Switching Protocols */ 54262339Speter if (ctx->sl.code >= 100 && ctx->sl.code < 200) 55262339Speter return 0; 56262339Speter 57262339Speter /* 204 No Content */ 58262339Speter if (ctx->sl.code == 204) 59262339Speter return 0; 60262339Speter 61262339Speter /* 205? */ 62262339Speter 63262339Speter /* 304 Not Modified */ 64262339Speter if (ctx->sl.code == 304) 65262339Speter return 0; 66262339Speter 67262339Speter return 1; 68262339Speter} 69262339Speter 70251877Speterserf_bucket_t *serf_bucket_response_create( 71251877Speter serf_bucket_t *stream, 72251877Speter serf_bucket_alloc_t *allocator) 73251877Speter{ 74251877Speter response_context_t *ctx; 75251877Speter 76251877Speter ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); 77251877Speter ctx->stream = stream; 78251877Speter ctx->body = NULL; 79251877Speter ctx->headers = serf_bucket_headers_create(allocator); 80251877Speter ctx->state = STATE_STATUS_LINE; 81251877Speter ctx->chunked = 0; 82251877Speter ctx->head_req = 0; 83251877Speter 84251877Speter serf_linebuf_init(&ctx->linebuf); 85251877Speter 86251877Speter return serf_bucket_create(&serf_bucket_type_response, allocator, ctx); 87251877Speter} 88251877Speter 89251877Spetervoid serf_bucket_response_set_head( 90251877Speter serf_bucket_t *bucket) 91251877Speter{ 92251877Speter response_context_t *ctx = bucket->data; 93251877Speter 94251877Speter ctx->head_req = 1; 95251877Speter} 96251877Speter 97251877Speterserf_bucket_t *serf_bucket_response_get_headers( 98251877Speter serf_bucket_t *bucket) 99251877Speter{ 100251877Speter return ((response_context_t *)bucket->data)->headers; 101251877Speter} 102251877Speter 103251877Speter 104251877Speterstatic void serf_response_destroy_and_data(serf_bucket_t *bucket) 105251877Speter{ 106251877Speter response_context_t *ctx = bucket->data; 107251877Speter 108251877Speter if (ctx->state != STATE_STATUS_LINE) { 109251877Speter serf_bucket_mem_free(bucket->allocator, (void*)ctx->sl.reason); 110251877Speter } 111251877Speter 112251877Speter serf_bucket_destroy(ctx->stream); 113251877Speter if (ctx->body != NULL) 114251877Speter serf_bucket_destroy(ctx->body); 115251877Speter serf_bucket_destroy(ctx->headers); 116251877Speter 117251877Speter serf_default_destroy_and_data(bucket); 118251877Speter} 119251877Speter 120251877Speterstatic apr_status_t fetch_line(response_context_t *ctx, int acceptable) 121251877Speter{ 122251877Speter return serf_linebuf_fetch(&ctx->linebuf, ctx->stream, acceptable); 123251877Speter} 124251877Speter 125251877Speterstatic apr_status_t parse_status_line(response_context_t *ctx, 126251877Speter serf_bucket_alloc_t *allocator) 127251877Speter{ 128251877Speter int res; 129251877Speter char *reason; /* ### stupid APR interface makes this non-const */ 130251877Speter 131251877Speter /* ctx->linebuf.line should be of form: HTTP/1.1 200 OK */ 132251877Speter res = apr_date_checkmask(ctx->linebuf.line, "HTTP/#.# ###*"); 133251877Speter if (!res) { 134251877Speter /* Not an HTTP response? Well, at least we won't understand it. */ 135251877Speter return SERF_ERROR_BAD_HTTP_RESPONSE; 136251877Speter } 137251877Speter 138251877Speter ctx->sl.version = SERF_HTTP_VERSION(ctx->linebuf.line[5] - '0', 139251877Speter ctx->linebuf.line[7] - '0'); 140251877Speter ctx->sl.code = apr_strtoi64(ctx->linebuf.line + 8, &reason, 10); 141251877Speter 142251877Speter /* Skip leading spaces for the reason string. */ 143251877Speter if (apr_isspace(*reason)) { 144251877Speter reason++; 145251877Speter } 146251877Speter 147251877Speter /* Copy the reason value out of the line buffer. */ 148251877Speter ctx->sl.reason = serf_bstrmemdup(allocator, reason, 149251877Speter ctx->linebuf.used 150251877Speter - (reason - ctx->linebuf.line)); 151251877Speter 152251877Speter return APR_SUCCESS; 153251877Speter} 154251877Speter 155251877Speter/* This code should be replaced with header buckets. */ 156251877Speterstatic apr_status_t fetch_headers(serf_bucket_t *bkt, response_context_t *ctx) 157251877Speter{ 158251877Speter apr_status_t status; 159251877Speter 160251877Speter /* RFC 2616 says that CRLF is the only line ending, but we can easily 161251877Speter * accept any kind of line ending. 162251877Speter */ 163251877Speter status = fetch_line(ctx, SERF_NEWLINE_ANY); 164251877Speter if (SERF_BUCKET_READ_ERROR(status)) { 165251877Speter return status; 166251877Speter } 167251877Speter /* Something was read. Process it. */ 168251877Speter 169251877Speter if (ctx->linebuf.state == SERF_LINEBUF_READY && ctx->linebuf.used) { 170251877Speter const char *end_key; 171251877Speter const char *c; 172251877Speter 173251877Speter end_key = c = memchr(ctx->linebuf.line, ':', ctx->linebuf.used); 174251877Speter if (!c) { 175251877Speter /* Bad headers? */ 176251877Speter return SERF_ERROR_BAD_HTTP_RESPONSE; 177251877Speter } 178251877Speter 179251877Speter /* Skip over initial ':' */ 180251877Speter c++; 181251877Speter 182251877Speter /* And skip all whitespaces. */ 183251877Speter for(; c < ctx->linebuf.line + ctx->linebuf.used; c++) 184251877Speter { 185251877Speter if (!apr_isspace(*c)) 186251877Speter { 187251877Speter break; 188251877Speter } 189251877Speter } 190251877Speter 191251877Speter /* Always copy the headers (from the linebuf into new mem). */ 192251877Speter /* ### we should be able to optimize some mem copies */ 193251877Speter serf_bucket_headers_setx( 194251877Speter ctx->headers, 195251877Speter ctx->linebuf.line, end_key - ctx->linebuf.line, 1, 196251877Speter c, ctx->linebuf.line + ctx->linebuf.used - c, 1); 197251877Speter } 198251877Speter 199251877Speter return status; 200251877Speter} 201251877Speter 202251877Speter/* Perform one iteration of the state machine. 203251877Speter * 204251877Speter * Will return when one the following conditions occurred: 205251877Speter * 1) a state change 206251877Speter * 2) an error 207251877Speter * 3) the stream is not ready or at EOF 208251877Speter * 4) APR_SUCCESS, meaning the machine can be run again immediately 209251877Speter */ 210251877Speterstatic apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx) 211251877Speter{ 212251877Speter apr_status_t status = APR_SUCCESS; /* initialize to avoid gcc warnings */ 213251877Speter 214251877Speter switch (ctx->state) { 215251877Speter case STATE_STATUS_LINE: 216251877Speter /* RFC 2616 says that CRLF is the only line ending, but we can easily 217251877Speter * accept any kind of line ending. 218251877Speter */ 219251877Speter status = fetch_line(ctx, SERF_NEWLINE_ANY); 220251877Speter if (SERF_BUCKET_READ_ERROR(status)) 221251877Speter return status; 222251877Speter 223251877Speter if (ctx->linebuf.state == SERF_LINEBUF_READY) { 224251877Speter /* The Status-Line is in the line buffer. Process it. */ 225251877Speter status = parse_status_line(ctx, bkt->allocator); 226251877Speter if (status) 227251877Speter return status; 228251877Speter 229251877Speter /* Good times ahead: we're switching protocols! */ 230251877Speter if (ctx->sl.code == 101) { 231251877Speter ctx->body = 232251877Speter serf_bucket_barrier_create(ctx->stream, bkt->allocator); 233251877Speter ctx->state = STATE_DONE; 234251877Speter break; 235251877Speter } 236251877Speter 237251877Speter /* Okay... move on to reading the headers. */ 238251877Speter ctx->state = STATE_HEADERS; 239251877Speter } 240251877Speter else { 241251877Speter /* The connection closed before we could get the next 242251877Speter * response. Treat the request as lost so that our upper 243251877Speter * end knows the server never tried to give us a response. 244251877Speter */ 245251877Speter if (APR_STATUS_IS_EOF(status)) { 246251877Speter return SERF_ERROR_REQUEST_LOST; 247251877Speter } 248251877Speter } 249251877Speter break; 250251877Speter case STATE_HEADERS: 251251877Speter status = fetch_headers(bkt, ctx); 252251877Speter if (SERF_BUCKET_READ_ERROR(status)) 253251877Speter return status; 254251877Speter 255251877Speter /* If an empty line was read, then we hit the end of the headers. 256251877Speter * Move on to the body. 257251877Speter */ 258251877Speter if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) { 259251877Speter const void *v; 260251877Speter 261251877Speter /* Advance the state. */ 262251877Speter ctx->state = STATE_BODY; 263251877Speter 264262339Speter /* If this is a response to a HEAD request, or code == 1xx,204 or304 265262339Speter then we don't receive a real body. */ 266262339Speter if (!expect_body(ctx)) { 267262339Speter ctx->body = serf_bucket_simple_create(NULL, 0, NULL, NULL, 268262339Speter bkt->allocator); 269262339Speter ctx->state = STATE_BODY; 270262339Speter break; 271262339Speter } 272262339Speter 273251877Speter ctx->body = 274251877Speter serf_bucket_barrier_create(ctx->stream, bkt->allocator); 275251877Speter 276251877Speter /* Are we C-L, chunked, or conn close? */ 277251877Speter v = serf_bucket_headers_get(ctx->headers, "Content-Length"); 278251877Speter if (v) { 279251877Speter apr_uint64_t length; 280251877Speter length = apr_strtoi64(v, NULL, 10); 281251877Speter if (errno == ERANGE) { 282251877Speter return APR_FROM_OS_ERROR(ERANGE); 283251877Speter } 284251877Speter ctx->body = serf_bucket_response_body_create( 285251877Speter ctx->body, length, bkt->allocator); 286251877Speter } 287251877Speter else { 288251877Speter v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding"); 289251877Speter 290251877Speter /* Need to handle multiple transfer-encoding. */ 291251877Speter if (v && strcasecmp("chunked", v) == 0) { 292251877Speter ctx->chunked = 1; 293251877Speter ctx->body = serf_bucket_dechunk_create(ctx->body, 294251877Speter bkt->allocator); 295251877Speter } 296251877Speter } 297251877Speter v = serf_bucket_headers_get(ctx->headers, "Content-Encoding"); 298251877Speter if (v) { 299251877Speter /* Need to handle multiple content-encoding. */ 300251877Speter if (v && strcasecmp("gzip", v) == 0) { 301251877Speter ctx->body = 302251877Speter serf_bucket_deflate_create(ctx->body, bkt->allocator, 303251877Speter SERF_DEFLATE_GZIP); 304251877Speter } 305251877Speter else if (v && strcasecmp("deflate", v) == 0) { 306251877Speter ctx->body = 307251877Speter serf_bucket_deflate_create(ctx->body, bkt->allocator, 308251877Speter SERF_DEFLATE_DEFLATE); 309251877Speter } 310251877Speter } 311251877Speter } 312251877Speter break; 313251877Speter case STATE_BODY: 314251877Speter /* Don't do anything. */ 315251877Speter break; 316251877Speter case STATE_TRAILERS: 317251877Speter status = fetch_headers(bkt, ctx); 318251877Speter if (SERF_BUCKET_READ_ERROR(status)) 319251877Speter return status; 320251877Speter 321251877Speter /* If an empty line was read, then we're done. */ 322251877Speter if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) { 323251877Speter ctx->state = STATE_DONE; 324251877Speter return APR_EOF; 325251877Speter } 326251877Speter break; 327251877Speter case STATE_DONE: 328251877Speter return APR_EOF; 329251877Speter default: 330251877Speter /* Not reachable */ 331251877Speter return APR_EGENERAL; 332251877Speter } 333251877Speter 334251877Speter return status; 335251877Speter} 336251877Speter 337251877Speterstatic apr_status_t wait_for_body(serf_bucket_t *bkt, response_context_t *ctx) 338251877Speter{ 339251877Speter apr_status_t status; 340251877Speter 341251877Speter /* Keep reading and moving through states if we aren't at the BODY */ 342251877Speter while (ctx->state != STATE_BODY) { 343251877Speter status = run_machine(bkt, ctx); 344251877Speter 345251877Speter /* Anything other than APR_SUCCESS means that we cannot immediately 346251877Speter * read again (for now). 347251877Speter */ 348251877Speter if (status) 349251877Speter return status; 350251877Speter } 351251877Speter /* in STATE_BODY */ 352251877Speter 353251877Speter return APR_SUCCESS; 354251877Speter} 355251877Speter 356251877Speterapr_status_t serf_bucket_response_wait_for_headers( 357251877Speter serf_bucket_t *bucket) 358251877Speter{ 359251877Speter response_context_t *ctx = bucket->data; 360251877Speter 361251877Speter return wait_for_body(bucket, ctx); 362251877Speter} 363251877Speter 364251877Speterapr_status_t serf_bucket_response_status( 365251877Speter serf_bucket_t *bkt, 366251877Speter serf_status_line *sline) 367251877Speter{ 368251877Speter response_context_t *ctx = bkt->data; 369251877Speter apr_status_t status; 370251877Speter 371251877Speter if (ctx->state != STATE_STATUS_LINE) { 372251877Speter /* We already read it and moved on. Just return it. */ 373251877Speter *sline = ctx->sl; 374251877Speter return APR_SUCCESS; 375251877Speter } 376251877Speter 377251877Speter /* Running the state machine once will advance the machine, or state 378251877Speter * that the stream isn't ready with enough data. There isn't ever a 379251877Speter * need to run the machine more than once to try and satisfy this. We 380251877Speter * have to look at the state to tell whether it advanced, though, as 381251877Speter * it is quite possible to advance *and* to return APR_EAGAIN. 382251877Speter */ 383251877Speter status = run_machine(bkt, ctx); 384251877Speter if (ctx->state == STATE_HEADERS) { 385251877Speter *sline = ctx->sl; 386251877Speter } 387251877Speter else { 388251877Speter /* Indicate that we don't have the information yet. */ 389251877Speter sline->version = 0; 390251877Speter } 391251877Speter 392251877Speter return status; 393251877Speter} 394251877Speter 395251877Speterstatic apr_status_t serf_response_read(serf_bucket_t *bucket, 396251877Speter apr_size_t requested, 397251877Speter const char **data, apr_size_t *len) 398251877Speter{ 399251877Speter response_context_t *ctx = bucket->data; 400251877Speter apr_status_t rv; 401251877Speter 402251877Speter rv = wait_for_body(bucket, ctx); 403251877Speter if (rv) { 404251877Speter /* It's not possible to have read anything yet! */ 405251877Speter if (APR_STATUS_IS_EOF(rv) || APR_STATUS_IS_EAGAIN(rv)) { 406251877Speter *len = 0; 407251877Speter } 408251877Speter return rv; 409251877Speter } 410251877Speter 411251877Speter rv = serf_bucket_read(ctx->body, requested, data, len); 412251877Speter if (SERF_BUCKET_READ_ERROR(rv)) 413251877Speter return rv; 414251877Speter 415251877Speter if (APR_STATUS_IS_EOF(rv)) { 416251877Speter if (ctx->chunked) { 417251877Speter ctx->state = STATE_TRAILERS; 418251877Speter /* Mask the result. */ 419251877Speter rv = APR_SUCCESS; 420251877Speter } else { 421251877Speter ctx->state = STATE_DONE; 422251877Speter } 423251877Speter } 424251877Speter return rv; 425251877Speter} 426251877Speter 427251877Speterstatic apr_status_t serf_response_readline(serf_bucket_t *bucket, 428251877Speter int acceptable, int *found, 429251877Speter const char **data, apr_size_t *len) 430251877Speter{ 431251877Speter response_context_t *ctx = bucket->data; 432251877Speter apr_status_t rv; 433251877Speter 434251877Speter rv = wait_for_body(bucket, ctx); 435251877Speter if (rv) { 436251877Speter return rv; 437251877Speter } 438251877Speter 439251877Speter /* Delegate to the stream bucket to do the readline. */ 440251877Speter return serf_bucket_readline(ctx->body, acceptable, found, data, len); 441251877Speter} 442251877Speter 443251877Speterapr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket) 444251877Speter{ 445251877Speter response_context_t *ctx = bucket->data; 446251877Speter serf_bucket_t *bkt; 447251877Speter char buf[256]; 448251877Speter int size; 449251877Speter 450251877Speter serf_bucket_aggregate_become(bucket); 451251877Speter 452251877Speter /* Add reconstructed status line. */ 453251877Speter size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ", 454251877Speter SERF_HTTP_VERSION_MAJOR(ctx->sl.version), 455251877Speter SERF_HTTP_VERSION_MINOR(ctx->sl.version), 456251877Speter ctx->sl.code); 457251877Speter bkt = serf_bucket_simple_copy_create(buf, size, 458251877Speter bucket->allocator); 459251877Speter serf_bucket_aggregate_append(bucket, bkt); 460251877Speter bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason), 461251877Speter bucket->allocator); 462251877Speter serf_bucket_aggregate_append(bucket, bkt); 463251877Speter bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2, 464251877Speter bucket->allocator); 465251877Speter serf_bucket_aggregate_append(bucket, bkt); 466251877Speter 467251877Speter /* Add headers and stream buckets in order. */ 468251877Speter serf_bucket_aggregate_append(bucket, ctx->headers); 469251877Speter serf_bucket_aggregate_append(bucket, ctx->stream); 470251877Speter 471251877Speter serf_bucket_mem_free(bucket->allocator, ctx); 472251877Speter 473251877Speter return APR_SUCCESS; 474251877Speter} 475251877Speter 476251877Speter/* ### need to implement */ 477251877Speter#define serf_response_peek NULL 478251877Speter 479251877Speterconst serf_bucket_type_t serf_bucket_type_response = { 480251877Speter "RESPONSE", 481251877Speter serf_response_read, 482251877Speter serf_response_readline, 483251877Speter serf_default_read_iovec, 484251877Speter serf_default_read_for_sendfile, 485251877Speter serf_default_read_bucket, 486251877Speter serf_response_peek, 487251877Speter serf_response_destroy_and_data, 488251877Speter}; 489