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_strings.h> 17251877Speter 18251877Speter#include "serf.h" 19251877Speter#include "serf_bucket_util.h" 20251877Speter 21251877Spetertypedef struct { 22251877Speter serf_bucket_t *stream; 23251877Speter 24251877Speter enum { 25251877Speter STATE_SIZE, /* reading the chunk size */ 26251877Speter STATE_CHUNK, /* reading the chunk */ 27251877Speter STATE_TERM, /* reading the chunk terminator */ 28251877Speter STATE_DONE /* body is done; we've returned EOF */ 29251877Speter } state; 30251877Speter 31251877Speter /* Buffer for accumulating a chunk size. */ 32251877Speter serf_linebuf_t linebuf; 33251877Speter 34251877Speter /* How much of the chunk, or the terminator, do we have left to read? */ 35251877Speter apr_int64_t body_left; 36251877Speter 37251877Speter} dechunk_context_t; 38251877Speter 39251877Speter 40251877Speterserf_bucket_t *serf_bucket_dechunk_create( 41251877Speter serf_bucket_t *stream, 42251877Speter serf_bucket_alloc_t *allocator) 43251877Speter{ 44251877Speter dechunk_context_t *ctx; 45251877Speter 46251877Speter ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); 47251877Speter ctx->stream = stream; 48251877Speter ctx->state = STATE_SIZE; 49251877Speter 50251877Speter serf_linebuf_init(&ctx->linebuf); 51251877Speter 52251877Speter return serf_bucket_create(&serf_bucket_type_dechunk, allocator, ctx); 53251877Speter} 54251877Speter 55251877Speterstatic void serf_dechunk_destroy_and_data(serf_bucket_t *bucket) 56251877Speter{ 57251877Speter dechunk_context_t *ctx = bucket->data; 58251877Speter 59251877Speter serf_bucket_destroy(ctx->stream); 60251877Speter 61251877Speter serf_default_destroy_and_data(bucket); 62251877Speter} 63251877Speter 64251877Speterstatic apr_status_t serf_dechunk_read(serf_bucket_t *bucket, 65251877Speter apr_size_t requested, 66251877Speter const char **data, apr_size_t *len) 67251877Speter{ 68251877Speter dechunk_context_t *ctx = bucket->data; 69251877Speter apr_status_t status; 70251877Speter 71251877Speter while (1) { 72251877Speter switch (ctx->state) { 73251877Speter case STATE_SIZE: 74251877Speter 75251877Speter /* fetch a line terminated by CRLF */ 76251877Speter status = serf_linebuf_fetch(&ctx->linebuf, ctx->stream, 77251877Speter SERF_NEWLINE_CRLF); 78251877Speter if (SERF_BUCKET_READ_ERROR(status)) 79251877Speter return status; 80251877Speter 81251877Speter /* if a line was read, then parse it. */ 82251877Speter if (ctx->linebuf.state == SERF_LINEBUF_READY) { 83251877Speter /* NUL-terminate the line. if it filled the entire buffer, 84251877Speter then just assume the thing is too large. */ 85251877Speter if (ctx->linebuf.used == sizeof(ctx->linebuf.line)) 86251877Speter return APR_FROM_OS_ERROR(ERANGE); 87251877Speter ctx->linebuf.line[ctx->linebuf.used] = '\0'; 88251877Speter 89251877Speter /* convert from HEX digits. */ 90251877Speter ctx->body_left = apr_strtoi64(ctx->linebuf.line, NULL, 16); 91251877Speter if (errno == ERANGE) { 92251877Speter return APR_FROM_OS_ERROR(ERANGE); 93251877Speter } 94251877Speter 95251877Speter if (ctx->body_left == 0) { 96251877Speter /* Just read the last-chunk marker. We're DONE. */ 97251877Speter ctx->state = STATE_DONE; 98251877Speter status = APR_EOF; 99251877Speter } 100251877Speter else { 101251877Speter /* Got a size, so we'll start reading the chunk now. */ 102251877Speter ctx->state = STATE_CHUNK; 103251877Speter } 104251877Speter 105251877Speter /* If we can read more, then go do so. */ 106251877Speter if (!status) 107251877Speter continue; 108251877Speter } 109251877Speter /* assert: status != 0 */ 110251877Speter 111251877Speter /* Note that we didn't actually read anything, so our callers 112251877Speter * don't get confused. 113251877Speter */ 114251877Speter *len = 0; 115251877Speter 116251877Speter return status; 117251877Speter 118251877Speter case STATE_CHUNK: 119251877Speter 120251877Speter if (requested > ctx->body_left) { 121251877Speter requested = ctx->body_left; 122251877Speter } 123251877Speter 124251877Speter /* Delegate to the stream bucket to do the read. */ 125251877Speter status = serf_bucket_read(ctx->stream, requested, data, len); 126251877Speter if (SERF_BUCKET_READ_ERROR(status)) 127251877Speter return status; 128251877Speter 129251877Speter /* Some data was read, so decrement the amount left and see 130251877Speter * if we're done reading this chunk. 131251877Speter */ 132251877Speter ctx->body_left -= *len; 133251877Speter if (!ctx->body_left) { 134251877Speter ctx->state = STATE_TERM; 135251877Speter ctx->body_left = 2; /* CRLF */ 136251877Speter } 137251877Speter 138251877Speter /* We need more data but there is no more available. */ 139251877Speter if (ctx->body_left && APR_STATUS_IS_EOF(status)) { 140251877Speter return SERF_ERROR_TRUNCATED_HTTP_RESPONSE; 141251877Speter } 142251877Speter 143251877Speter /* Return the data we just read. */ 144251877Speter return status; 145251877Speter 146251877Speter case STATE_TERM: 147251877Speter /* Delegate to the stream bucket to do the read. */ 148251877Speter status = serf_bucket_read(ctx->stream, ctx->body_left, data, len); 149251877Speter if (SERF_BUCKET_READ_ERROR(status)) 150251877Speter return status; 151251877Speter 152251877Speter /* Some data was read, so decrement the amount left and see 153251877Speter * if we're done reading the chunk terminator. 154251877Speter */ 155251877Speter ctx->body_left -= *len; 156251877Speter 157251877Speter /* We need more data but there is no more available. */ 158251877Speter if (ctx->body_left && APR_STATUS_IS_EOF(status)) 159251877Speter return SERF_ERROR_TRUNCATED_HTTP_RESPONSE; 160251877Speter 161251877Speter if (!ctx->body_left) { 162251877Speter ctx->state = STATE_SIZE; 163251877Speter } 164251877Speter 165253895Speter /* Don't return the CR of CRLF to the caller! */ 166253895Speter *len = 0; 167253895Speter 168251877Speter if (status) 169251877Speter return status; 170251877Speter 171251877Speter break; 172251877Speter 173251877Speter case STATE_DONE: 174251877Speter /* Just keep returning EOF */ 175253895Speter *len = 0; 176251877Speter return APR_EOF; 177251877Speter 178251877Speter default: 179251877Speter /* Not reachable */ 180251877Speter return APR_EGENERAL; 181251877Speter } 182251877Speter } 183251877Speter /* NOTREACHED */ 184251877Speter} 185251877Speter 186251877Speter/* ### need to implement */ 187251877Speter#define serf_dechunk_readline NULL 188251877Speter#define serf_dechunk_peek NULL 189251877Speter 190251877Speterconst serf_bucket_type_t serf_bucket_type_dechunk = { 191251877Speter "DECHUNK", 192251877Speter serf_dechunk_read, 193251877Speter serf_dechunk_readline, 194251877Speter serf_default_read_iovec, 195251877Speter serf_default_read_for_sendfile, 196251877Speter serf_default_read_bucket, 197251877Speter serf_dechunk_peek, 198251877Speter serf_dechunk_destroy_and_data, 199251877Speter}; 200