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