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.
16 #include <apr_pools.h>
17 #include <apr_strings.h>
20 #include "serf_bucket_util.h"
30 apr_status_t last_status;
33 serf_bucket_t *stream;
39 serf_bucket_t *serf_bucket_chunk_create(
40 serf_bucket_t *stream, serf_bucket_alloc_t *allocator)
44 ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
45 ctx->state = STATE_FETCH;
46 ctx->chunk = serf_bucket_aggregate_create(allocator);
49 return serf_bucket_create(&serf_bucket_type_chunk, allocator, ctx);
54 static apr_status_t create_chunk(serf_bucket_t *bucket)
56 chunk_context_t *ctx = bucket->data;
57 serf_bucket_t *simple_bkt;
59 apr_size_t stream_len;
60 struct iovec vecs[66]; /* 64 + chunk trailer + EOF trailer = 66 */
64 if (ctx->state != STATE_FETCH) {
69 serf_bucket_read_iovec(ctx->stream, SERF_READ_ALL_AVAIL,
70 64, vecs, &vecs_read);
72 if (SERF_BUCKET_READ_ERROR(ctx->last_status)) {
74 return ctx->last_status;
77 /* Count the length of the data we read. */
79 for (i = 0; i < vecs_read; i++) {
80 stream_len += vecs[i].iov_len;
83 /* assert: stream_len in hex < sizeof(ctx->chunk_hdr) */
85 /* Inserting a 0 byte chunk indicates a terminator, which already happens
86 * during the EOF handler below. Adding another one here will cause the
87 * EOF chunk to be interpreted by the server as a new request. So,
88 * we'll only do this if we have something to write.
91 /* Build the chunk header. */
92 chunk_len = apr_snprintf(ctx->chunk_hdr, sizeof(ctx->chunk_hdr),
93 "%" APR_UINT64_T_HEX_FMT CRLF,
94 (apr_uint64_t)stream_len);
96 /* Create a copy of the chunk header so we can have multiple chunks
97 * in the pipeline at the same time.
99 simple_bkt = serf_bucket_simple_copy_create(ctx->chunk_hdr, chunk_len,
101 serf_bucket_aggregate_append(ctx->chunk, simple_bkt);
103 /* Insert the chunk footer. */
104 vecs[vecs_read].iov_base = CRLF;
105 vecs[vecs_read++].iov_len = sizeof(CRLF) - 1;
108 /* We've reached the end of the line for the stream. */
109 if (APR_STATUS_IS_EOF(ctx->last_status)) {
110 /* Insert the chunk footer. */
111 vecs[vecs_read].iov_base = "0" CRLF CRLF;
112 vecs[vecs_read++].iov_len = sizeof("0" CRLF CRLF) - 1;
114 ctx->state = STATE_EOF;
117 /* Okay, we can return data. */
118 ctx->state = STATE_CHUNK;
121 serf_bucket_aggregate_append_iovec(ctx->chunk, vecs, vecs_read);
126 static apr_status_t serf_chunk_read(serf_bucket_t *bucket,
127 apr_size_t requested,
128 const char **data, apr_size_t *len)
130 chunk_context_t *ctx = bucket->data;
133 /* Before proceeding, we need to fetch some data from the stream. */
134 if (ctx->state == STATE_FETCH) {
135 status = create_chunk(bucket);
141 status = serf_bucket_read(ctx->chunk, requested, data, len);
143 /* Mask EOF from aggregate bucket. */
144 if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) {
145 status = ctx->last_status;
146 ctx->state = STATE_FETCH;
152 static apr_status_t serf_chunk_readline(serf_bucket_t *bucket,
153 int acceptable, int *found,
154 const char **data, apr_size_t *len)
156 chunk_context_t *ctx = bucket->data;
159 status = serf_bucket_readline(ctx->chunk, acceptable, found, data, len);
161 /* Mask EOF from aggregate bucket. */
162 if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) {
164 ctx->state = STATE_FETCH;
170 static apr_status_t serf_chunk_read_iovec(serf_bucket_t *bucket,
171 apr_size_t requested,
176 chunk_context_t *ctx = bucket->data;
179 /* Before proceeding, we need to fetch some data from the stream. */
180 if (ctx->state == STATE_FETCH) {
181 status = create_chunk(bucket);
187 status = serf_bucket_read_iovec(ctx->chunk, requested, vecs_size, vecs,
190 /* Mask EOF from aggregate bucket. */
191 if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) {
192 status = ctx->last_status;
193 ctx->state = STATE_FETCH;
199 static apr_status_t serf_chunk_peek(serf_bucket_t *bucket,
203 chunk_context_t *ctx = bucket->data;
206 status = serf_bucket_peek(ctx->chunk, data, len);
208 /* Mask EOF from aggregate bucket. */
209 if (APR_STATUS_IS_EOF(status) && ctx->state == STATE_CHUNK) {
216 static void serf_chunk_destroy(serf_bucket_t *bucket)
218 chunk_context_t *ctx = bucket->data;
220 serf_bucket_destroy(ctx->stream);
221 serf_bucket_destroy(ctx->chunk);
223 serf_default_destroy_and_data(bucket);
226 const serf_bucket_type_t serf_bucket_type_chunk = {
230 serf_chunk_read_iovec,
231 serf_default_read_for_sendfile,
232 serf_default_read_bucket,