1
1
#include "ngx_http_rate_limit_handler.h"
2
+ #include "ngx_http_rate_limit_reply.h"
2
3
#include "ngx_http_rate_limit_upstream.h"
3
4
#include "ngx_http_rate_limit_util.h"
4
5
@@ -11,23 +12,22 @@ static void ngx_http_rate_limit_abort_request(ngx_http_request_t *r);
11
12
static void ngx_http_rate_limit_finalize_request (ngx_http_request_t * r ,
12
13
ngx_int_t rc );
13
14
14
-
15
15
static ngx_str_t x_limit_header = ngx_string ("X-RateLimit-Limit" );
16
16
static ngx_str_t x_remaining_header = ngx_string ("X-RateLimit-Remaining" );
17
17
static ngx_str_t x_reset_header = ngx_string ("X-RateLimit-Reset" );
18
18
static ngx_str_t x_retry_after_header = ngx_string ("Retry-After" );
19
19
20
-
21
20
ngx_int_t
22
21
ngx_http_rate_limit_handler (ngx_http_request_t * r )
23
22
{
24
23
ngx_http_upstream_t * u ;
25
24
ngx_http_rate_limit_ctx_t * ctx ;
26
25
ngx_http_rate_limit_loc_conf_t * rlcf ;
27
- size_t len ;
26
+ size_t len ;
28
27
u_char * p , * n ;
29
- ngx_str_t target ;
30
- ngx_url_t url ;
28
+ ngx_uint_t status ;
29
+ ngx_str_t target ;
30
+ ngx_url_t url ;
31
31
32
32
rlcf = ngx_http_get_module_loc_conf (r , ngx_http_rate_limit_module );
33
33
@@ -42,22 +42,23 @@ ngx_http_rate_limit_handler(ngx_http_request_t *r)
42
42
return NGX_AGAIN ;
43
43
}
44
44
45
+ status = r -> upstream -> state -> status ;
46
+
45
47
/* Return appropriate status */
46
48
47
- if (ctx -> status == NGX_HTTP_TOO_MANY_REQUESTS ) {
49
+ if (status == NGX_HTTP_TOO_MANY_REQUESTS ) {
48
50
ngx_log_error (rlcf -> limit_log_level , r -> connection -> log , 0 ,
49
51
"rate limit exceeded for key \"%V\"" , & ctx -> key );
50
52
51
53
return rlcf -> status_code ;
52
54
}
53
55
54
- if (ctx -> status >= NGX_HTTP_OK
55
- && ctx -> status < NGX_HTTP_SPECIAL_RESPONSE ) {
56
+ if (status == NGX_HTTP_OK ) {
56
57
return NGX_OK ;
57
58
}
58
59
59
60
ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
60
- "rate limit unexpected status: %ui" , ctx -> status );
61
+ "rate limit unexpected status: %ui" , status );
61
62
62
63
return NGX_HTTP_INTERNAL_SERVER_ERROR ;
63
64
}
@@ -99,6 +100,8 @@ ngx_http_rate_limit_handler(ngx_http_request_t *r)
99
100
return NGX_HTTP_INTERNAL_SERVER_ERROR ;
100
101
}
101
102
103
+ ctx -> request = r ;
104
+
102
105
if (ngx_http_upstream_create (r ) != NGX_OK ) {
103
106
return NGX_HTTP_INTERNAL_SERVER_ERROR ;
104
107
}
@@ -108,7 +111,8 @@ ngx_http_rate_limit_handler(ngx_http_request_t *r)
108
111
if (rlcf -> complex_target ) {
109
112
/* Variables used in the rate_limit_pass directive */
110
113
111
- if (ngx_http_complex_value (r , rlcf -> complex_target , & target ) != NGX_OK ) {
114
+ if (ngx_http_complex_value (r , rlcf -> complex_target , & target ) !=
115
+ NGX_OK ) {
112
116
return NGX_ERROR ;
113
117
}
114
118
@@ -145,12 +149,9 @@ ngx_http_rate_limit_handler(ngx_http_request_t *r)
145
149
146
150
ngx_http_set_ctx (r , ctx , ngx_http_rate_limit_module );
147
151
148
- /* We bypass the upstream input filter mechanism in
149
- * ngx_http_rate_limit_rev_handler */
150
-
151
152
u -> input_filter_init = ngx_http_rate_limit_filter_init ;
152
153
u -> input_filter = ngx_http_rate_limit_filter ;
153
- u -> input_filter_ctx = r ;
154
+ u -> input_filter_ctx = ctx ;
154
155
155
156
r -> main -> count ++ ;
156
157
@@ -163,11 +164,10 @@ ngx_http_rate_limit_handler(ngx_http_request_t *r)
163
164
return NGX_AGAIN ;
164
165
}
165
166
166
-
167
167
static ngx_int_t
168
168
ngx_http_rate_limit_create_request (ngx_http_request_t * r )
169
169
{
170
- ngx_int_t rc ;
170
+ ngx_int_t rc ;
171
171
ngx_buf_t * b ;
172
172
ngx_chain_t * cl ;
173
173
@@ -194,7 +194,6 @@ ngx_http_rate_limit_create_request(ngx_http_request_t *r)
194
194
return NGX_OK ;
195
195
}
196
196
197
-
198
197
static ngx_int_t
199
198
ngx_http_rate_limit_reinit_request (ngx_http_request_t * r )
200
199
{
@@ -208,17 +207,14 @@ ngx_http_rate_limit_reinit_request(ngx_http_request_t *r)
208
207
return NGX_OK ;
209
208
}
210
209
211
-
212
210
static ngx_int_t
213
211
ngx_http_rate_limit_process_header (ngx_http_request_t * r )
214
212
{
215
- ngx_http_upstream_t * u ;
216
- ngx_http_rate_limit_ctx_t * ctx ;
217
- ngx_http_rate_limit_loc_conf_t * rlcf ;
218
- ngx_buf_t * b ;
219
- ngx_str_t buf ;
220
- u_char * line , * crnl , * arg ;
221
- ngx_uint_t lnum ;
213
+ ngx_http_upstream_t * u ;
214
+ ngx_http_rate_limit_ctx_t * ctx ;
215
+ ngx_buf_t * b ;
216
+ u_char chr ;
217
+ ngx_str_t buf ;
222
218
223
219
u = r -> upstream ;
224
220
b = & u -> buffer ;
@@ -232,114 +228,41 @@ ngx_http_rate_limit_process_header(ngx_http_request_t *r)
232
228
return NGX_ERROR ;
233
229
}
234
230
235
- rlcf = ngx_http_get_module_loc_conf (r , ngx_http_rate_limit_module );
231
+ /* the first char is the response header */
232
+ chr = * b -> pos ;
236
233
237
- lnum = 0 ;
234
+ /* we are always expecting a multi bulk reply */
235
+ if (chr != '*' ) {
236
+ buf .data = b -> pos ;
237
+ buf .len = b -> last - b -> pos ;
238
238
239
- for (line = b -> pos ; (crnl = (u_char * ) ngx_strstr (line , "\r\n" )) != NULL ; line = crnl + 2 ) {
240
- if (++ lnum == 1 ) {
241
- /* the first char is the response header
242
- * the second char is number of return args */
243
- if (* line != '*' && * (line + 1 ) != '5' ) {
244
- buf .data = b -> pos ;
245
- buf .len = b -> last - b -> pos ;
246
-
247
- ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
248
- "rate limit: redis sent invalid header: \"%V\"" , & buf );
249
-
250
- return NGX_HTTP_UPSTREAM_INVALID_HEADER ;
251
- }
252
-
253
- continue ;
254
- }
255
- if ((arg = (u_char * ) ngx_strchr (line , ':' )) == NULL || ++ arg > crnl ) {
256
- /* does not start with colon? */
257
-
258
- buf .data = b -> pos ;
259
- buf .len = b -> last - b -> pos ;
260
-
261
- ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
262
- "rate limit: redis sent invalid response: \"%V\"" , & buf );
263
-
264
- return NGX_HTTP_UPSTREAM_INVALID_HEADER ;
265
- }
239
+ ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
240
+ "rate limit: redis sent invalid response: \"%V\"" , & buf );
266
241
267
- if (lnum == 2 ) {
268
- /* 0 indicates the action is allowed
269
- * 1 indicates that the action was limited/blocked */
270
- if (* arg == '0' ) {
271
- ctx -> status = NGX_HTTP_OK ;
272
- } else {
273
- ctx -> status = NGX_HTTP_TOO_MANY_REQUESTS ;
274
- }
275
- } else if (ctx -> status == NGX_HTTP_TOO_MANY_REQUESTS || rlcf -> enable_headers ) {
276
- buf .data = arg ;
277
- buf .len = crnl - arg ;
278
-
279
- switch (lnum ) {
280
- case 3 :
281
- /* X-RateLimit-Limit HTTP header */
282
- (void ) ngx_set_custom_header (r , & x_limit_header , & buf );
283
- break ;
284
- case 4 :
285
- /* X-RateLimit-Remaining HTTP header */
286
- (void ) ngx_set_custom_header (r , & x_remaining_header , & buf );
287
- break ;
288
- case 5 :
289
- /* The number of seconds until the user should retry,
290
- * and always -1 if the action was allowed. */
291
- if (* arg != '-' ) {
292
- (void ) ngx_set_custom_header (r , & x_retry_after_header , & buf );
293
- }
294
- break ;
295
- case 6 :
296
- /* X-RateLimit-Reset header */
297
- (void ) ngx_set_custom_header (r , & x_reset_header , & buf );
298
- break ;
299
- default :
300
- buf .data = arg ;
301
- buf .len = b -> last - arg ;
302
-
303
- ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
304
- "rate limit: redis sent extra bytes: \"%V\"" , & buf );
305
-
306
- return NGX_HTTP_UPSTREAM_INVALID_HEADER ;
307
- }
308
- }
242
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER ;
309
243
}
310
244
245
+ ++ b -> pos ;
246
+
311
247
u -> state -> status = NGX_HTTP_OK ;
312
248
313
249
return NGX_OK ;
314
250
}
315
251
316
-
317
252
static ngx_int_t
318
253
ngx_http_rate_limit_filter_init (void * data )
319
254
{
320
- ngx_http_request_t * r = data ;
321
-
322
- ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
323
- "rate_limit: ngx_http_rate_limit_filter_init should not"
324
- " be called by the upstream" );
325
-
326
- return NGX_ERROR ;
255
+ return NGX_OK ;
327
256
}
328
257
329
-
330
258
static ngx_int_t
331
259
ngx_http_rate_limit_filter (void * data , ssize_t bytes )
332
260
{
333
- ngx_http_request_t * r = data ;
334
-
335
- ngx_log_error (NGX_LOG_ERR , r -> connection -> log , 0 ,
336
- "rate_limit: ngx_http_rate_limit_filter should not"
337
- " be called by the upstream" );
261
+ ngx_http_rate_limit_ctx_t * ctx = data ;
338
262
339
- return NGX_ERROR ;
263
+ return ngx_http_rate_limit_process_reply ( ctx , bytes ) ;
340
264
}
341
265
342
-
343
266
static void
344
267
ngx_http_rate_limit_abort_request (ngx_http_request_t * r )
345
268
{
@@ -348,23 +271,38 @@ ngx_http_rate_limit_abort_request(ngx_http_request_t *r)
348
271
return ;
349
272
}
350
273
351
-
352
274
static void
353
275
ngx_http_rate_limit_finalize_request (ngx_http_request_t * r , ngx_int_t rc )
354
276
{
355
- ngx_http_rate_limit_ctx_t * ctx ;
277
+ ngx_http_rate_limit_ctx_t * ctx ;
278
+ ngx_http_rate_limit_loc_conf_t * rlcf ;
356
279
357
280
ngx_log_debug0 (NGX_LOG_DEBUG_HTTP , r -> connection -> log , 0 ,
358
281
"finalize http rate limit request" );
359
282
360
283
ctx = ngx_http_get_module_ctx (r , ngx_http_rate_limit_module );
361
- if (ctx != NULL ) {
362
- ctx -> done = 1 ;
363
- }
364
284
365
- if (rc >= NGX_HTTP_SPECIAL_RESPONSE ) {
366
- r -> headers_out .status = rc ;
285
+ rlcf = ngx_http_get_module_loc_conf (r , ngx_http_rate_limit_module );
286
+
287
+ if (r -> upstream -> state -> status == NGX_HTTP_TOO_MANY_REQUESTS ||
288
+ rlcf -> enable_headers ) {
289
+ /* X-RateLimit-Limit HTTP header */
290
+ (void ) ngx_set_custom_header (r , & x_limit_header , ctx -> limit );
291
+
292
+ /* X-RateLimit-Remaining HTTP header */
293
+ (void ) ngx_set_custom_header (r , & x_remaining_header , ctx -> remaining );
294
+
295
+ /* Retry-After */
296
+ if (r -> upstream -> state -> status == NGX_HTTP_TOO_MANY_REQUESTS ) {
297
+ (void ) ngx_set_custom_header (r , & x_retry_after_header ,
298
+ ctx -> retry_after );
299
+ }
300
+
301
+ /* X-RateLimit-Reset */
302
+ (void ) ngx_set_custom_header (r , & x_reset_header , ctx -> reset );
367
303
}
368
304
369
- return ;
305
+ if (ctx != NULL ) {
306
+ ctx -> done = 1 ;
307
+ }
370
308
}
0 commit comments