comparison ngx_http_delay_body_filter_module.c @ 1:7c2d64d9c656

Working delay body and tests. Depends on rb->filter_need_buffering and rb->buffered, as well as corresponding changes in the request body reading code to work. Might not be the best solution, as current body reading code relies on rb->buffered to be properly set at various stages. Notably, rb->buffered must be cleared when calling the next filter, since the save body filter relies on it. Possible future improvements: implement last buffer checking in the save body filter instead of checking for (rb->rest == 0 && !rb->buffered). The code uses its own event to implement delay timer. To remove the timer in case of abnormal request termination a cleanup handler is added. While in theory it is possible to use a timer on c->read instead, this implies additional changes to the request body reading code. Custom event was chosen to reduce complexity of changes needed.
author Maxim Dounin <mdounin@mdounin.ru>
date Fri, 20 Aug 2021 22:35:05 +0300
parents a386f95c5ae9
children b049c3a0543e
comparison
equal deleted inserted replaced
0:a386f95c5ae9 1:7c2d64d9c656
7 #include <ngx_core.h> 7 #include <ngx_core.h>
8 #include <ngx_http.h> 8 #include <ngx_http.h>
9 9
10 10
11 typedef struct { 11 typedef struct {
12 ngx_msec_t delay; 12 ngx_msec_t delay;
13 } ngx_http_delay_body_conf_t; 13 } ngx_http_delay_body_conf_t;
14 14
15 15
16 typedef struct {
17 ngx_event_t event;
18 ngx_chain_t *out;
19 ngx_uint_t buffered;
20 } ngx_http_delay_body_ctx_t;
21
22
23 static ngx_int_t ngx_http_delay_body_filter(ngx_http_request_t *r,
24 ngx_chain_t *in);
25 static void ngx_http_delay_body_cleanup(void *data);
26 static void ngx_http_delay_body_event_handler(ngx_event_t *ev);
16 static void *ngx_http_delay_body_create_conf(ngx_conf_t *cf); 27 static void *ngx_http_delay_body_create_conf(ngx_conf_t *cf);
17 static char *ngx_http_delay_body_merge_conf(ngx_conf_t *cf, void *parent, 28 static char *ngx_http_delay_body_merge_conf(ngx_conf_t *cf, void *parent,
18 void *child); 29 void *child);
19 static ngx_int_t ngx_http_delay_body_init(ngx_conf_t *cf); 30 static ngx_int_t ngx_http_delay_body_init(ngx_conf_t *cf);
20 31
67 78
68 79
69 static ngx_int_t 80 static ngx_int_t
70 ngx_http_delay_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 81 ngx_http_delay_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
71 { 82 {
72 ngx_chain_t *cl; 83 ngx_int_t rc;
84 ngx_chain_t *cl, *ln;
85 ngx_http_cleanup_t *cln;
86 ngx_http_delay_body_ctx_t *ctx;
73 ngx_http_delay_body_conf_t *conf; 87 ngx_http_delay_body_conf_t *conf;
74 88
75 conf = ngx_http_get_module_loc_conf(r, ngx_http_delay_body_filter_module); 89 conf = ngx_http_get_module_loc_conf(r, ngx_http_delay_body_filter_module);
76 90
77 if (!conf->delay) { 91 if (!conf->delay) {
79 } 93 }
80 94
81 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 95 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
82 "delay request body filter"); 96 "delay request body filter");
83 97
84 /* TODO: delay */ 98 ctx = ngx_http_get_module_ctx(r, ngx_http_delay_body_filter_module);
85 99
86 for (cl = in; cl; cl = cl->next) { 100 if (ctx == NULL) {
87 101 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_delay_body_ctx_t));
88 if (cl->buf->last_buf) { 102 if (ctx == NULL) {
89 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 103 return NGX_ERROR;
90 "delay body: last buf");
91 } 104 }
92 105
93 } 106 ngx_http_set_ctx(r, ctx, ngx_http_delay_body_filter_module);
94 107
95 return ngx_http_next_request_body_filter(r, in); 108 #if 1 /* XXX */
109 r->request_body->filter_need_buffering = 1;
110 #endif
111 }
112
113 if (ngx_chain_add_copy(r->pool, &ctx->out, in) != NGX_OK) {
114 return NGX_ERROR;
115 }
116
117 if (!ctx->event.timedout) {
118 if (!ctx->event.timer_set) {
119
120 /* cleanup to remove the timer in case of abnormal termination */
121
122 cln = ngx_http_cleanup_add(r, 0);
123 if (cln == NULL) {
124 return NGX_ERROR;
125 }
126
127 cln->handler = ngx_http_delay_body_cleanup;
128 cln->data = ctx;
129
130 /* add timer */
131
132 ctx->event.handler = ngx_http_delay_body_event_handler;
133 ctx->event.data = r;
134 ctx->event.log = r->connection->log;
135
136 ngx_add_timer(&ctx->event, conf->delay);
137
138 ctx->buffered = 1;
139 #if 1 /* XXX */
140 r->request_body->buffered++;
141 #endif
142 }
143
144 return ngx_http_next_request_body_filter(r, NULL);
145 }
146
147 if (ctx->buffered) {
148 ctx->buffered = 0;
149 #if 1 /* XXX */
150 r->request_body->buffered--;
151 #endif
152 }
153
154 rc = ngx_http_next_request_body_filter(r, ctx->out);
155
156 for (cl = ctx->out; cl; /* void */) {
157 ln = cl;
158 cl = cl->next;
159 ngx_free_chain(r->pool, ln);
160 }
161
162 ctx->out = NULL;
163
164 return rc;
165 }
166
167
168 static void
169 ngx_http_delay_body_cleanup(void *data)
170 {
171 ngx_http_delay_body_ctx_t *ctx = data;
172
173 if (ctx->event.timer_set) {
174 ngx_del_timer(&ctx->event);
175 }
176 }
177
178
179 static void
180 ngx_http_delay_body_event_handler(ngx_event_t *ev)
181 {
182 ngx_connection_t *c;
183 ngx_http_request_t *r;
184
185 r = ev->data;
186 c = r->connection;
187
188 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
189 "delay request body event");
190
191 ngx_post_event(c->read, &ngx_posted_events);
96 } 192 }
97 193
98 194
99 static void * 195 static void *
100 ngx_http_delay_body_create_conf(ngx_conf_t *cf) 196 ngx_http_delay_body_create_conf(ngx_conf_t *cf)