limit_buckets.c revision 251886
1234285Sdim/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2234285Sdim *
3234285Sdim * Licensed under the Apache License, Version 2.0 (the "License");
4234285Sdim * you may not use this file except in compliance with the License.
5234285Sdim * You may obtain a copy of the License at
6234285Sdim *
7234285Sdim *     http://www.apache.org/licenses/LICENSE-2.0
8234285Sdim *
9234285Sdim * Unless required by applicable law or agreed to in writing, software
10234285Sdim * distributed under the License is distributed on an "AS IS" BASIS,
11234285Sdim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12234285Sdim * See the License for the specific language governing permissions and
13234285Sdim * limitations under the License.
14234285Sdim */
15234285Sdim
16234285Sdim#include <apr_pools.h>
17234285Sdim
18234285Sdim#include "serf.h"
19251662Sdim#include "serf_bucket_util.h"
20249423Sdim
21251662Sdim/* Older versions of APR do not have this macro.  */
22234285Sdim#ifdef APR_SIZE_MAX
23234285Sdim#define REQUESTED_MAX APR_SIZE_MAX
24234285Sdim#else
25234285Sdim#define REQUESTED_MAX (~((apr_size_t)0))
26234285Sdim#endif
27234285Sdim
28234285Sdim
29263508Sdimtypedef struct {
30234285Sdim    serf_bucket_t *stream;
31263508Sdim    apr_uint64_t remaining;
32251662Sdim} limit_context_t;
33251662Sdim
34234285Sdim
35234285Sdimserf_bucket_t *serf_bucket_limit_create(
36234285Sdim    serf_bucket_t *stream, apr_uint64_t len, serf_bucket_alloc_t *allocator)
37234285Sdim{
38234285Sdim    limit_context_t *ctx;
39234285Sdim
40234285Sdim    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
41234285Sdim    ctx->stream = stream;
42234285Sdim    ctx->remaining = len;
43234285Sdim
44234285Sdim    return serf_bucket_create(&serf_bucket_type_limit, allocator, ctx);
45234285Sdim}
46234285Sdim
47234285Sdimstatic apr_status_t serf_limit_read(serf_bucket_t *bucket,
48234285Sdim                                    apr_size_t requested,
49234285Sdim                                    const char **data, apr_size_t *len)
50234285Sdim{
51234285Sdim    limit_context_t *ctx = bucket->data;
52234285Sdim    apr_status_t status;
53234285Sdim
54234285Sdim    if (!ctx->remaining) {
55234285Sdim        *len = 0;
56234285Sdim        return APR_EOF;
57234285Sdim    }
58234285Sdim
59234285Sdim    if (requested == SERF_READ_ALL_AVAIL || requested > ctx->remaining) {
60234285Sdim        if (ctx->remaining <= REQUESTED_MAX) {
61234285Sdim            requested = (apr_size_t) ctx->remaining;
62234285Sdim        } else {
63234285Sdim            requested = REQUESTED_MAX;
64234285Sdim        }
65234285Sdim    }
66234285Sdim
67234285Sdim    status = serf_bucket_read(ctx->stream, requested, data, len);
68234285Sdim
69234285Sdim    if (!SERF_BUCKET_READ_ERROR(status)) {
70234285Sdim        ctx->remaining -= *len;
71234285Sdim    }
72249423Sdim
73249423Sdim    /* If we have met our limit and don't have a status, return EOF. */
74249423Sdim    if (!ctx->remaining && !status) {
75249423Sdim        status = APR_EOF;
76234285Sdim    }
77234285Sdim
78234285Sdim    return status;
79234285Sdim}
80234285Sdim
81234285Sdimstatic apr_status_t serf_limit_readline(serf_bucket_t *bucket,
82234285Sdim                                         int acceptable, int *found,
83234285Sdim                                         const char **data, apr_size_t *len)
84234285Sdim{
85234285Sdim    limit_context_t *ctx = bucket->data;
86234285Sdim    apr_status_t status;
87234285Sdim
88234285Sdim    if (!ctx->remaining) {
89234285Sdim        *len = 0;
90234285Sdim        return APR_EOF;
91234285Sdim    }
92234285Sdim
93234285Sdim    status = serf_bucket_readline(ctx->stream, acceptable, found, data, len);
94234285Sdim
95234285Sdim    if (!SERF_BUCKET_READ_ERROR(status)) {
96234285Sdim        ctx->remaining -= *len;
97234285Sdim    }
98234285Sdim
99234285Sdim    /* If we have met our limit and don't have a status, return EOF. */
100234285Sdim    if (!ctx->remaining && !status) {
101234285Sdim        status = APR_EOF;
102234285Sdim    }
103234285Sdim
104234285Sdim    return status;
105234285Sdim}
106234285Sdim
107234285Sdimstatic apr_status_t serf_limit_peek(serf_bucket_t *bucket,
108234285Sdim                                     const char **data,
109234285Sdim                                     apr_size_t *len)
110234285Sdim{
111234285Sdim    limit_context_t *ctx = bucket->data;
112234285Sdim
113234285Sdim    return serf_bucket_peek(ctx->stream, data, len);
114234285Sdim}
115234285Sdim
116234285Sdimstatic void serf_limit_destroy(serf_bucket_t *bucket)
117251662Sdim{
118234285Sdim    limit_context_t *ctx = bucket->data;
119234285Sdim
120234285Sdim    serf_bucket_destroy(ctx->stream);
121234285Sdim
122234285Sdim    serf_default_destroy_and_data(bucket);
123239462Sdim}
124234285Sdim
125234285Sdimconst serf_bucket_type_t serf_bucket_type_limit = {
126234285Sdim    "LIMIT",
127234285Sdim    serf_limit_read,
128234285Sdim    serf_limit_readline,
129234285Sdim    serf_default_read_iovec,
130234285Sdim    serf_default_read_for_sendfile,
131234285Sdim    serf_default_read_bucket,
132234285Sdim    serf_limit_peek,
133234285Sdim    serf_limit_destroy,
134251662Sdim};
135251662Sdim