Apache HTTPD
mod_ratelimit.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "httpd.h"
18#include "http_config.h"
19#include "http_log.h"
20#include "util_filter.h"
21
22#include "mod_ratelimit.h"
23
24#define RATE_LIMIT_FILTER_NAME "RATE_LIMIT"
25#define RATE_INTERVAL_MS (200)
26
32
43
44#if defined(RLFDEBUG)
46{
48 int i = 0;
49
50 for (e = APR_BRIGADE_FIRST(bb);
51 e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e), i++) {
53 "brigade: [%d] %s", i, e->type->name);
54
55 }
56}
57#endif /* RLFDEBUG */
58
59static apr_status_t
61{
63 rl_ctx_t *ctx = f->ctx;
64 apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc;
65
66 /* Set up our rl_ctx_t on first use */
67 if (ctx == NULL) {
68 const char *rl = NULL;
69 int ratelimit;
70 int burst = 0;
71
72 /* no subrequests. */
73 if (f->r->main != NULL) {
75 return ap_pass_brigade(f->next, bb);
76 }
77
78 /* Configuration: rate limit */
79 rl = apr_table_get(f->r->subprocess_env, "rate-limit");
80
81 if (rl == NULL) {
83 return ap_pass_brigade(f->next, bb);
84 }
85
86 /* rl is in kilo bytes / second */
87 ratelimit = atoi(rl) * 1024;
88 if (ratelimit <= 0) {
89 /* remove ourselves */
91 APLOGNO(03488) "rl: disabling: rate-limit = %s (too high?)", rl);
93 return ap_pass_brigade(f->next, bb);
94 }
95
96 /* Configuration: optional initial burst */
97 rl = apr_table_get(f->r->subprocess_env, "rate-initial-burst");
98 if (rl != NULL) {
99 burst = atoi(rl) * 1024;
100 if (burst <= 0) {
102 APLOGNO(03489) "rl: disabling burst: rate-initial-burst = %s (too high?)", rl);
103 burst = 0;
104 }
105 }
106
107 /* Set up our context */
108 ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t));
109 f->ctx = ctx;
110 ctx->state = RATE_LIMIT;
111 ctx->speed = ratelimit;
112 ctx->burst = burst;
113 ctx->do_sleep = 0;
114
115 /* calculate how many bytes / interval we want to send */
116 /* speed is bytes / second, so, how many (speed / 1000 % interval) */
117 ctx->chunk_size = (ctx->speed / (1000 / RATE_INTERVAL_MS));
118 ctx->tmpbb = apr_brigade_create(f->r->pool, ba);
119 ctx->holdingbb = apr_brigade_create(f->r->pool, ba);
120 }
121 else {
122 APR_BRIGADE_PREPEND(bb, ctx->holdingbb);
123 }
124
125 while (!APR_BRIGADE_EMPTY(bb)) {
126 apr_bucket *e;
127
128 if (ctx->state == RATE_FULLSPEED) {
129 /* Find where we 'stop' going full speed. */
130 for (e = APR_BRIGADE_FIRST(bb);
132 if (AP_RL_BUCKET_IS_END(e)) {
133 apr_brigade_split_ex(bb, e, ctx->holdingbb);
134 ctx->state = RATE_LIMIT;
135 break;
136 }
137 }
138
141 rv = ap_pass_brigade(f->next, bb);
143
144 if (rv != APR_SUCCESS) {
146 "rl: full speed brigade pass failed.");
147 return rv;
148 }
149 }
150 else {
151 for (e = APR_BRIGADE_FIRST(bb);
154 apr_brigade_split_ex(bb, e, ctx->holdingbb);
155 ctx->state = RATE_FULLSPEED;
156 break;
157 }
158 }
159
160 while (!APR_BRIGADE_EMPTY(bb)) {
161 apr_off_t len = ctx->chunk_size + ctx->burst;
162
163 APR_BRIGADE_CONCAT(ctx->tmpbb, bb);
164
165 /*
166 * Pull next chunk of data; the initial amount is our
167 * burst allotment (if any) plus a chunk. All subsequent
168 * iterations are just chunks with whatever remaining
169 * burst amounts we have left (in case not done in the
170 * first bucket).
171 */
172 rv = apr_brigade_partition(ctx->tmpbb, len, &e);
173 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
175 "rl: partition failed.");
176 return rv;
177 }
178 /* Send next metadata now if any */
179 while (e != APR_BRIGADE_SENTINEL(ctx->tmpbb)
182 }
183 if (e != APR_BRIGADE_SENTINEL(ctx->tmpbb)) {
184 apr_brigade_split_ex(ctx->tmpbb, e, bb);
185 }
186 else {
187 apr_brigade_length(ctx->tmpbb, 1, &len);
188 }
189
190 /*
191 * Adjust the burst amount depending on how much
192 * we've done up to now.
193 */
194 if (ctx->burst) {
196 APLOGNO(03485) "rl: burst %d; len %"APR_OFF_T_FMT, ctx->burst, len);
197 if (len < ctx->burst) {
198 ctx->burst -= len;
199 }
200 else {
201 ctx->burst = 0;
202 }
203 }
204
205 e = APR_BRIGADE_LAST(ctx->tmpbb);
206 if (APR_BUCKET_IS_EOS(e)) {
208 }
209 else if (!APR_BUCKET_IS_FLUSH(e)) {
210 if (APR_BRIGADE_EMPTY(bb)) {
211 /* Wait for more (or next call) */
212 break;
213 }
216 }
217
218#if defined(RLFDEBUG)
219 brigade_dump(f->r, ctx->tmpbb);
220 brigade_dump(f->r, bb);
221#endif /* RLFDEBUG */
222
223 if (ctx->do_sleep) {
225 }
226 else {
227 ctx->do_sleep = 1;
228 }
229
230 rv = ap_pass_brigade(f->next, ctx->tmpbb);
231 apr_brigade_cleanup(ctx->tmpbb);
232
233 if (rv != APR_SUCCESS) {
234 /* Most often, user disconnects from stream */
236 "rl: brigade pass failed.");
237 return rv;
238 }
239 }
240 }
241
242 if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) {
243 /* Any rate-limited data in tmpbb is sent unlimited along
244 * with the rest.
245 */
246 APR_BRIGADE_CONCAT(bb, ctx->tmpbb);
247 APR_BRIGADE_CONCAT(bb, ctx->holdingbb);
248 }
249 }
250
251#if defined(RLFDEBUG)
252 brigade_dump(f->r, ctx->tmpbb);
253#endif /* RLFDEBUG */
254
255 /* Save remaining tmpbb with the correct lifetime for the next call */
256 return ap_save_brigade(f, &ctx->holdingbb, &ctx->tmpbb, f->r->pool);
257}
258
259
260static apr_status_t
263{
264 *str = NULL;
265 *len = 0;
266 return APR_SUCCESS;
267}
268
271{
272 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
273
275 b->free = apr_bucket_free;
276 b->list = list;
277 b->length = 0;
278 b->start = 0;
279 b->data = NULL;
280 b->type = &ap_rl_bucket_type_end;
281
282 return b;
283}
284
287{
288 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
289
291 b->free = apr_bucket_free;
292 b->list = list;
293 b->length = 0;
294 b->start = 0;
295 b->data = NULL;
296 b->type = &ap_rl_bucket_type_start;
297
298 return b;
299}
300
301
302
311
312
321
322
323
324
326{
327 /* run after mod_deflate etc etc, but not at connection level, ie, mod_ssl. */
330}
331
334 NULL, /* create per-directory config structure */
335 NULL, /* merge per-directory config structures */
336 NULL, /* create per-server config structure */
337 NULL, /* merge per-server config structures */
338 NULL, /* command apr_table_t */
340};
const char apr_size_t len
Definition ap_regex.h:187
#define AP_DECLARE_MODULE(foo)
request_rec * r
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
apr_status_t ap_save_brigade(ap_filter_t *f, apr_bucket_brigade **save_to, apr_bucket_brigade **b, apr_pool_t *p)
ap_filter_rec_t * ap_register_output_filter(const char *name, ap_out_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
void ap_remove_output_filter(ap_filter_t *f)
@ AP_FTYPE_CONNECTION
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
#define APR_INCOMPLETE
Definition apr_errno.h:452
#define APR_BUCKET_INIT(e)
apr_file_t * f
#define APR_BUCKET_IS_FLUSH(e)
#define APR_BRIGADE_LAST(b)
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_PREPEND(a, b)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define APR_BUCKET_NEXT(e)
apr_read_type_e
Definition apr_buckets.h:57
apr_bucket * e
#define APR_BRIGADE_CONCAT(a, b)
#define APR_BRIGADE_EMPTY(b)
#define APR_BRIGADE_SENTINEL(b)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define STANDARD20_MODULE_STUFF
apr_size_t size
const apr_array_header_t * list
Definition apr_cstr.h:105
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_pool_t * b
Definition apr_pools.h:529
Apache Configuration.
Apache Logging library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
const apr_bucket_type_t ap_rl_bucket_type_end
#define RATE_INTERVAL_MS
#define RATE_LIMIT_FILTER_NAME
apr_bucket * ap_rl_end_create(apr_bucket_alloc_t *list)
static void register_hooks(apr_pool_t *p)
const apr_bucket_type_t ap_rl_bucket_type_start
rl_state_e
@ RATE_LIMIT
@ RATE_FULLSPEED
static apr_status_t rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *bb)
static apr_status_t rl_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block)
apr_bucket * ap_rl_start_create(apr_bucket_alloc_t *list)
#define AP_RL_BUCKET_IS_END(e)
#define AP_RL_BUCKET_IS_START(e)
#define AP_RL_DECLARE(type)
#define AP_RL_DECLARE_DATA
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
The representation of a filter chain.
const char * name
const apr_bucket_type_t * type
apr_pool_t * pool
A structure that represents the current request.
Definition httpd.h:845
rl_state_e state
apr_bucket_brigade * holdingbb
apr_bucket_brigade * tmpbb
#define str
Apache filter library.