chunk_buckets.c revision 251877
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_pools.h> 17251877Speter#include <apr_strings.h> 18251877Speter 19251877Speter#include "serf.h" 20251877Speter#include "serf_bucket_util.h" 21251877Speter 22251877Speter 23251877Spetertypedef struct { 24251877Speter enum { 25251877Speter STATE_FETCH, 26251877Speter STATE_CHUNK, 27251877Speter STATE_EOF 28251877Speter } state; 29251877Speter 30251877Speter apr_status_t last_status; 31251877Speter 32251877Speter serf_bucket_t *chunk; 33251877Speter serf_bucket_t *stream; 34251877Speter 35251877Speter char chunk_hdr[20]; 36251877Speter} chunk_context_t; 37251877Speter 38251877Speter 39251877Speterserf_bucket_t *serf_bucket_chunk_create( 40251877Speter serf_bucket_t *stream, serf_bucket_alloc_t *allocator) 41251877Speter{ 42251877Speter chunk_context_t *ctx; 43251877Speter 44251877Speter ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); 45251877Speter ctx->state = STATE_FETCH; 46251877Speter ctx->chunk = serf_bucket_aggregate_create(allocator); 47251877Speter ctx->stream = stream; 48251877Speter 49251877Speter return serf_bucket_create(&serf_bucket_type_chunk, allocator, ctx); 50251877Speter} 51251877Speter 52251877Speter#define CRLF "\r\n" 53251877Speter 54251877Speterstatic apr_status_t create_chunk(serf_bucket_t *bucket) 55251877Speter{ 56251877Speter chunk_context_t *ctx = bucket->data; 57251877Speter serf_bucket_t *simple_bkt; 58251877Speter apr_size_t chunk_len; 59251877Speter apr_size_t stream_len; 60251877Speter struct iovec vecs[66]; /* 64 + chunk trailer + EOF trailer = 66 */ 61251877Speter int vecs_read; 62251877Speter int i; 63251877Speter 64251877Speter if (ctx->state != STATE_FETCH) { 65251877Speter return APR_SUCCESS; 66251877Speter } 67251877Speter 68251877Speter ctx->last_status = 69251877Speter serf_bucket_read_iovec(ctx->stream, SERF_READ_ALL_AVAIL, 70251877Speter 64, vecs, &vecs_read); 71251877Speter 72251877Speter if (SERF_BUCKET_READ_ERROR(ctx->last_status)) { 73251877Speter /* Uh-oh. */ 74251877Speter return ctx->last_status; 75251877Speter } 76251877Speter 77251877Speter /* Count the length of the data we read. */ 78251877Speter stream_len = 0; 79251877Speter for (i = 0; i < vecs_read; i++) { 80251877Speter stream_len += vecs[i].iov_len; 81251877Speter } 82251877Speter 83251877Speter /* assert: stream_len in hex < sizeof(ctx->chunk_hdr) */ 84251877Speter 85251877Speter /* Inserting a 0 byte chunk indicates a terminator, which already happens 86251877Speter * during the EOF handler below. Adding another one here will cause the 87251877Speter * EOF chunk to be interpreted by the server as a new request. So, 88251877Speter * we'll only do this if we have something to write. 89251877Speter */ 90251877Speter if (stream_len) { 91251877Speter /* Build the chunk header. */ 92251877Speter chunk_len = apr_snprintf(ctx->chunk_hdr, sizeof(ctx->chunk_hdr), 93251877Speter "%" APR_UINT64_T_HEX_FMT CRLF, 94251877Speter (apr_uint64_t)stream_len); 95251877Speter 96251877Speter /* Create a copy of the chunk header so we can have multiple chunks 97251877Speter * in the pipeline at the same time. 98251877Speter */ 99251877Speter simple_bkt = serf_bucket_simple_copy_create(ctx->chunk_hdr, chunk_len, 100251877Speter bucket->allocator); 101251877Speter serf_bucket_aggregate_append(ctx->chunk, simple_bkt); 102251877Speter 103251877Speter /* Insert the chunk footer. */ 104251877Speter vecs[vecs_read].iov_base = CRLF; 105251877Speter vecs[vecs_read++].iov_len = sizeof(CRLF) - 1; 106251877Speter } 107251877Speter 108251877Speter /* We've reached the end of the line for the stream. */ 109251877Speter if (APR_STATUS_IS_EOF(ctx->last_status)) { 110251877Speter /* Insert the chunk footer. */ 111251877Speter vecs[vecs_read].iov_base = "0" CRLF CRLF; 112251877Speter vecs[vecs_read++].iov_len = sizeof("0" CRLF CRLF) - 1; 113251877Speter 114251877Speter ctx->state = STATE_EOF; 115251877Speter } 116251877Speter else { 117251877Speter /* Okay, we can return data. */ 118251877Speter ctx->state = STATE_CHUNK; 119251877Speter } 120251877Speter 121251877Speter serf_bucket_aggregate_append_iovec(ctx->chunk, vecs, vecs_read); 122251877Speter 123251877Speter return APR_SUCCESS; 124251877Speter} 125251877Speter 126251877Speterstatic apr_status_t serf_chunk_read(serf_bucket_t *bucket, 127251877Speter apr_size_t requested, 128251877Speter const char **data, apr_size_t *len) 129251877Speter{ 130251877Speter chunk_context_t *ctx = bucket->data; 131251877Speter apr_status_t status; 132251877Speter 133251877Speter /* Before proceeding, we need to fetch some data from the stream. */ 134251877Speter if (ctx->state == STATE_FETCH) { 135251877Speter status = create_chunk(bucket); 136251877Speter if (status) { 137251877Speter return status; 138251877Speter } 139251877Speter } 140251877Speter 141251877Speter status = serf_bucket_read(ctx->chunk, requested, data, len); 142251877Speter 143251877Speter /* Mask EOF from aggregate bucket. */ 144251877Speter if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) { 145251877Speter status = ctx->last_status; 146251877Speter ctx->state = STATE_FETCH; 147251877Speter } 148251877Speter 149251877Speter return status; 150251877Speter} 151251877Speter 152251877Speterstatic apr_status_t serf_chunk_readline(serf_bucket_t *bucket, 153251877Speter int acceptable, int *found, 154251877Speter const char **data, apr_size_t *len) 155251877Speter{ 156251877Speter chunk_context_t *ctx = bucket->data; 157251877Speter apr_status_t status; 158251877Speter 159251877Speter status = serf_bucket_readline(ctx->chunk, acceptable, found, data, len); 160251877Speter 161251877Speter /* Mask EOF from aggregate bucket. */ 162251877Speter if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) { 163251877Speter status = APR_EAGAIN; 164251877Speter ctx->state = STATE_FETCH; 165251877Speter } 166251877Speter 167251877Speter return status; 168251877Speter} 169251877Speter 170251877Speterstatic apr_status_t serf_chunk_read_iovec(serf_bucket_t *bucket, 171251877Speter apr_size_t requested, 172251877Speter int vecs_size, 173251877Speter struct iovec *vecs, 174251877Speter int *vecs_used) 175251877Speter{ 176251877Speter chunk_context_t *ctx = bucket->data; 177251877Speter apr_status_t status; 178251877Speter 179251877Speter /* Before proceeding, we need to fetch some data from the stream. */ 180251877Speter if (ctx->state == STATE_FETCH) { 181251877Speter status = create_chunk(bucket); 182251877Speter if (status) { 183251877Speter return status; 184251877Speter } 185251877Speter } 186251877Speter 187251877Speter status = serf_bucket_read_iovec(ctx->chunk, requested, vecs_size, vecs, 188251877Speter vecs_used); 189251877Speter 190251877Speter /* Mask EOF from aggregate bucket. */ 191251877Speter if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) { 192251877Speter status = ctx->last_status; 193251877Speter ctx->state = STATE_FETCH; 194251877Speter } 195251877Speter 196251877Speter return status; 197251877Speter} 198251877Speter 199251877Speterstatic apr_status_t serf_chunk_peek(serf_bucket_t *bucket, 200251877Speter const char **data, 201251877Speter apr_size_t *len) 202251877Speter{ 203251877Speter chunk_context_t *ctx = bucket->data; 204251877Speter apr_status_t status; 205251877Speter 206251877Speter status = serf_bucket_peek(ctx->chunk, data, len); 207251877Speter 208251877Speter /* Mask EOF from aggregate bucket. */ 209251877Speter if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) { 210251877Speter status = APR_EAGAIN; 211251877Speter } 212251877Speter 213251877Speter return status; 214251877Speter} 215251877Speter 216251877Speterstatic void serf_chunk_destroy(serf_bucket_t *bucket) 217251877Speter{ 218251877Speter chunk_context_t *ctx = bucket->data; 219251877Speter 220251877Speter serf_bucket_destroy(ctx->stream); 221251877Speter serf_bucket_destroy(ctx->chunk); 222251877Speter 223251877Speter serf_default_destroy_and_data(bucket); 224251877Speter} 225251877Speter 226251877Speterconst serf_bucket_type_t serf_bucket_type_chunk = { 227251877Speter "CHUNK", 228251877Speter serf_chunk_read, 229251877Speter serf_chunk_readline, 230251877Speter serf_chunk_read_iovec, 231251877Speter serf_default_read_for_sendfile, 232251877Speter serf_default_read_bucket, 233251877Speter serf_chunk_peek, 234251877Speter serf_chunk_destroy, 235251877Speter}; 236