1/* ====================================================================
2 *    Licensed to the Apache Software Foundation (ASF) under one
3 *    or more contributor license agreements.  See the NOTICE file
4 *    distributed with this work for additional information
5 *    regarding copyright ownership.  The ASF licenses this file
6 *    to you under the Apache License, Version 2.0 (the
7 *    "License"); you may not use this file except in compliance
8 *    with the License.  You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *    Unless required by applicable law or agreed to in writing,
13 *    software distributed under the License is distributed on an
14 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 *    KIND, either express or implied.  See the License for the
16 *    specific language governing permissions and limitations
17 *    under the License.
18 * ====================================================================
19 */
20
21#include <apr_pools.h>
22
23#include "serf.h"
24#include "serf_bucket_util.h"
25
26/* Older versions of APR do not have this macro.  */
27#ifdef APR_SIZE_MAX
28#define REQUESTED_MAX APR_SIZE_MAX
29#else
30#define REQUESTED_MAX (~((apr_size_t)0))
31#endif
32
33
34typedef struct {
35    serf_bucket_t *stream;
36    apr_uint64_t remaining;
37} body_context_t;
38
39serf_bucket_t *serf_bucket_response_body_create(
40    serf_bucket_t *stream, apr_uint64_t len, serf_bucket_alloc_t *allocator)
41{
42    body_context_t *ctx;
43
44    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
45    ctx->stream = stream;
46    ctx->remaining = len;
47
48    return serf_bucket_create(&serf_bucket_type_response_body, allocator, ctx);
49}
50
51static apr_status_t serf_response_body_read(serf_bucket_t *bucket,
52                                            apr_size_t requested,
53                                            const char **data,
54                                            apr_size_t *len)
55{
56    body_context_t *ctx = bucket->data;
57    apr_status_t status;
58
59    if (!ctx->remaining) {
60        *len = 0;
61        return APR_EOF;
62    }
63
64    if (requested == SERF_READ_ALL_AVAIL || requested > ctx->remaining) {
65        if (ctx->remaining <= REQUESTED_MAX) {
66            requested = (apr_size_t) ctx->remaining;
67        } else {
68            requested = REQUESTED_MAX;
69        }
70    }
71
72    status = serf_bucket_read(ctx->stream, requested, data, len);
73
74    if (!SERF_BUCKET_READ_ERROR(status)) {
75        ctx->remaining -= *len;
76    }
77
78    if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) {
79        /* The server sent less data than expected. */
80        status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE;
81    }
82
83    return status;
84}
85
86static apr_status_t serf_response_body_readline(serf_bucket_t *bucket,
87                                                int acceptable, int *found,
88                                                const char **data,
89                                                apr_size_t *len)
90{
91    body_context_t *ctx = bucket->data;
92    apr_status_t status;
93
94    if (!ctx->remaining) {
95        *len = 0;
96        return APR_EOF;
97    }
98
99    status = serf_bucket_readline(ctx->stream, acceptable, found, data, len);
100
101    if (!SERF_BUCKET_READ_ERROR(status)) {
102        ctx->remaining -= *len;
103    }
104
105    if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) {
106        /* The server sent less data than expected. */
107        status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE;
108    }
109
110    return status;
111}
112
113static apr_status_t serf_response_body_peek(serf_bucket_t *bucket,
114                                            const char **data,
115                                            apr_size_t *len)
116{
117    body_context_t *ctx = bucket->data;
118
119    return serf_bucket_peek(ctx->stream, data, len);
120}
121
122static void serf_response_body_destroy(serf_bucket_t *bucket)
123{
124    body_context_t *ctx = bucket->data;
125
126    serf_bucket_destroy(ctx->stream);
127
128    serf_default_destroy_and_data(bucket);
129}
130
131const serf_bucket_type_t serf_bucket_type_response_body = {
132    "RESPONSE_BODY",
133    serf_response_body_read,
134    serf_response_body_readline,
135    serf_default_read_iovec,
136    serf_default_read_for_sendfile,
137    serf_default_read_bucket,
138    serf_response_body_peek,
139    serf_response_body_destroy,
140};
141