1/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2 *
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
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
14 */
15
16#include <apr_pools.h>
17
18#include "serf.h"
19#include "serf_bucket_util.h"
20
21/* Older versions of APR do not have this macro.  */
22#ifdef APR_SIZE_MAX
23#define REQUESTED_MAX APR_SIZE_MAX
24#else
25#define REQUESTED_MAX (~((apr_size_t)0))
26#endif
27
28
29typedef struct {
30    serf_bucket_t *stream;
31    apr_uint64_t remaining;
32} body_context_t;
33
34serf_bucket_t *serf_bucket_response_body_create(
35    serf_bucket_t *stream, apr_uint64_t len, serf_bucket_alloc_t *allocator)
36{
37    body_context_t *ctx;
38
39    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
40    ctx->stream = stream;
41    ctx->remaining = len;
42
43    return serf_bucket_create(&serf_bucket_type_response_body, allocator, ctx);
44}
45
46static apr_status_t serf_response_body_read(serf_bucket_t *bucket,
47                                            apr_size_t requested,
48                                            const char **data,
49                                            apr_size_t *len)
50{
51    body_context_t *ctx = bucket->data;
52    apr_status_t status;
53
54    if (!ctx->remaining) {
55        *len = 0;
56        return APR_EOF;
57    }
58
59    if (requested == SERF_READ_ALL_AVAIL || requested > ctx->remaining) {
60        if (ctx->remaining <= REQUESTED_MAX) {
61            requested = (apr_size_t) ctx->remaining;
62        } else {
63            requested = REQUESTED_MAX;
64        }
65    }
66
67    status = serf_bucket_read(ctx->stream, requested, data, len);
68
69    if (!SERF_BUCKET_READ_ERROR(status)) {
70        ctx->remaining -= *len;
71    }
72
73    if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) {
74        /* The server sent less data than expected. */
75        status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE;
76    }
77
78    return status;
79}
80
81static apr_status_t serf_response_body_readline(serf_bucket_t *bucket,
82                                                int acceptable, int *found,
83                                                const char **data,
84                                                apr_size_t *len)
85{
86    body_context_t *ctx = bucket->data;
87    apr_status_t status;
88
89    if (!ctx->remaining) {
90        *len = 0;
91        return APR_EOF;
92    }
93
94    status = serf_bucket_readline(ctx->stream, acceptable, found, data, len);
95
96    if (!SERF_BUCKET_READ_ERROR(status)) {
97        ctx->remaining -= *len;
98    }
99
100    if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) {
101        /* The server sent less data than expected. */
102        status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE;
103    }
104
105    return status;
106}
107
108static apr_status_t serf_response_body_peek(serf_bucket_t *bucket,
109                                            const char **data,
110                                            apr_size_t *len)
111{
112    body_context_t *ctx = bucket->data;
113
114    return serf_bucket_peek(ctx->stream, data, len);
115}
116
117static void serf_response_body_destroy(serf_bucket_t *bucket)
118{
119    body_context_t *ctx = bucket->data;
120
121    serf_bucket_destroy(ctx->stream);
122
123    serf_default_destroy_and_data(bucket);
124}
125
126const serf_bucket_type_t serf_bucket_type_response_body = {
127    "RESPONSE_BODY",
128    serf_response_body_read,
129    serf_response_body_readline,
130    serf_default_read_iovec,
131    serf_default_read_for_sendfile,
132    serf_default_read_bucket,
133    serf_response_body_peek,
134    serf_response_body_destroy,
135};
136