1
2 /*
3 * Copyright (C) Maxim Dounin
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11
12
13 typedef struct {
14 ngx_uint_t max_cached;
15 ngx_uint_t requests;
16 ngx_msec_t timeout;
17
18 ngx_queue_t cache;
19 ngx_queue_t free;
20
21 ngx_http_upstream_init_pt original_init_upstream;
22 ngx_http_upstream_init_peer_pt original_init_peer;
23
24 } ngx_http_upstream_keepalive_srv_conf_t;
25
26
27 typedef struct {
28 ngx_http_upstream_keepalive_srv_conf_t *conf;
29
30 ngx_queue_t queue;
31 ngx_connection_t *connection;
32
33 socklen_t socklen;
34 ngx_sockaddr_t sockaddr;
35
36 } ngx_http_upstream_keepalive_cache_t;
37
38
39 typedef struct {
40 ngx_http_upstream_keepalive_srv_conf_t *conf;
41
42 ngx_http_upstream_t *upstream;
43
44 void *data;
45
46 ngx_event_get_peer_pt original_get_peer;
47 ngx_event_free_peer_pt original_free_peer;
48
49 #if (NGX_HTTP_SSL)
50 ngx_event_set_peer_session_pt original_set_session;
51 ngx_event_save_peer_session_pt original_save_session;
52 #endif
53
54 } ngx_http_upstream_keepalive_peer_data_t;
55
56
57 static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
58 ngx_http_upstream_srv_conf_t *us);
59 static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
60 void *data);
61 static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
62 void *data, ngx_uint_t state);
63
64 static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
65 static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
66 static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
67
68 #if (NGX_HTTP_SSL)
69 static ngx_int_t ngx_http_upstream_keepalive_set_session(
70 ngx_peer_connection_t *pc, void *data);
71 static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
72 void *data);
73 #endif
74
75 static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
76 static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
77 void *conf);
78
79
80 static ngx_command_t ngx_http_upstream_keepalive_commands[] = {
81
82 { ngx_string("keepalive"),
83 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
84 ngx_http_upstream_keepalive,
85 NGX_HTTP_SRV_CONF_OFFSET,
86 0,
87 NULL },
88
89 { ngx_string("keepalive_timeout"),
90 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
91 ngx_conf_set_msec_slot,
92 NGX_HTTP_SRV_CONF_OFFSET,
93 offsetof(ngx_http_upstream_keepalive_srv_conf_t, timeout),
94 NULL },
95
96 { ngx_string("keepalive_requests"),
97 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
98 ngx_conf_set_num_slot,
99 NGX_HTTP_SRV_CONF_OFFSET,
100 offsetof(ngx_http_upstream_keepalive_srv_conf_t, requests),
101 NULL },
102
103 ngx_null_command
104 };
105
106
107 static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = {
108 NULL, /* preconfiguration */
109 NULL, /* postconfiguration */
110
111 NULL, /* create main configuration */
112 NULL, /* init main configuration */
113
114 ngx_http_upstream_keepalive_create_conf, /* create server configuration */
115 NULL, /* merge server configuration */
116
117 NULL, /* create location configuration */
118 NULL /* merge location configuration */
119 };
120
121
122 ngx_module_t ngx_http_upstream_keepalive_module = {
123 NGX_MODULE_V1,
124 &ngx_http_upstream_keepalive_module_ctx, /* module context */
125 ngx_http_upstream_keepalive_commands, /* module directives */
126 NGX_HTTP_MODULE, /* module type */
127 NULL, /* init master */
128 NULL, /* init module */
129 NULL, /* init process */
130 NULL, /* init thread */
131 NULL, /* exit thread */
132 NULL, /* exit process */
133 NULL, /* exit master */
134 NGX_MODULE_V1_PADDING
135 };
136
137
138 static ngx_int_t
ngx_http_upstream_init_keepalive(ngx_conf_t * cf,ngx_http_upstream_srv_conf_t * us)139 ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
140 ngx_http_upstream_srv_conf_t *us)
141 {
142 ngx_uint_t i;
143 ngx_http_upstream_keepalive_srv_conf_t *kcf;
144 ngx_http_upstream_keepalive_cache_t *cached;
145
146 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
147 "init keepalive");
148
149 kcf = ngx_http_conf_upstream_srv_conf(us,
150 ngx_http_upstream_keepalive_module);
151
152 ngx_conf_init_msec_value(kcf->timeout, 60000);
153 ngx_conf_init_uint_value(kcf->requests, 100);
154
155 if (kcf->original_init_upstream(cf, us) != NGX_OK) {
156 return NGX_ERROR;
157 }
158
159 kcf->original_init_peer = us->peer.init;
160
161 us->peer.init = ngx_http_upstream_init_keepalive_peer;
162
163 /* allocate cache items and add to free queue */
164
165 cached = ngx_pcalloc(cf->pool,
166 sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
167 if (cached == NULL) {
168 return NGX_ERROR;
169 }
170
171 ngx_queue_init(&kcf->cache);
172 ngx_queue_init(&kcf->free);
173
174 for (i = 0; i < kcf->max_cached; i++) {
175 ngx_queue_insert_head(&kcf->free, &cached[i].queue);
176 cached[i].conf = kcf;
177 }
178
179 return NGX_OK;
180 }
181
182
183 static ngx_int_t
ngx_http_upstream_init_keepalive_peer(ngx_http_request_t * r,ngx_http_upstream_srv_conf_t * us)184 ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
185 ngx_http_upstream_srv_conf_t *us)
186 {
187 ngx_http_upstream_keepalive_peer_data_t *kp;
188 ngx_http_upstream_keepalive_srv_conf_t *kcf;
189
190 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
191 "init keepalive peer");
192
193 kcf = ngx_http_conf_upstream_srv_conf(us,
194 ngx_http_upstream_keepalive_module);
195
196 kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
197 if (kp == NULL) {
198 return NGX_ERROR;
199 }
200
201 if (kcf->original_init_peer(r, us) != NGX_OK) {
202 return NGX_ERROR;
203 }
204
205 kp->conf = kcf;
206 kp->upstream = r->upstream;
207 kp->data = r->upstream->peer.data;
208 kp->original_get_peer = r->upstream->peer.get;
209 kp->original_free_peer = r->upstream->peer.free;
210
211 r->upstream->peer.data = kp;
212 r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
213 r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
214
215 #if (NGX_HTTP_SSL)
216 kp->original_set_session = r->upstream->peer.set_session;
217 kp->original_save_session = r->upstream->peer.save_session;
218 r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
219 r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
220 #endif
221
222 return NGX_OK;
223 }
224
225
226 static ngx_int_t
ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t * pc,void * data)227 ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
228 {
229 ngx_http_upstream_keepalive_peer_data_t *kp = data;
230 ngx_http_upstream_keepalive_cache_t *item;
231
232 ngx_int_t rc;
233 ngx_queue_t *q, *cache;
234 ngx_connection_t *c;
235
236 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
237 "get keepalive peer");
238
239 /* ask balancer */
240
241 rc = kp->original_get_peer(pc, kp->data);
242
243 if (rc != NGX_OK) {
244 return rc;
245 }
246
247 /* search cache for suitable connection */
248
249 cache = &kp->conf->cache;
250
251 for (q = ngx_queue_head(cache);
252 q != ngx_queue_sentinel(cache);
253 q = ngx_queue_next(q))
254 {
255 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
256 c = item->connection;
257
258 if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
259 item->socklen, pc->socklen)
260 == 0)
261 {
262 ngx_queue_remove(q);
263 ngx_queue_insert_head(&kp->conf->free, q);
264
265 goto found;
266 }
267 }
268
269 return NGX_OK;
270
271 found:
272
273 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
274 "get keepalive peer: using connection %p", c);
275
276 c->idle = 0;
277 c->sent = 0;
278 c->log = pc->log;
279 c->read->log = pc->log;
280 c->write->log = pc->log;
281 c->pool->log = pc->log;
282
283 if (c->read->timer_set) {
284 ngx_del_timer(c->read);
285 }
286
287 pc->connection = c;
288 pc->cached = 1;
289
290 return NGX_DONE;
291 }
292
293
294 static void
ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t * pc,void * data,ngx_uint_t state)295 ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
296 ngx_uint_t state)
297 {
298 ngx_http_upstream_keepalive_peer_data_t *kp = data;
299 ngx_http_upstream_keepalive_cache_t *item;
300
301 ngx_queue_t *q;
302 ngx_connection_t *c;
303 ngx_http_upstream_t *u;
304
305 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
306 "free keepalive peer");
307
308 /* cache valid connections */
309
310 u = kp->upstream;
311 c = pc->connection;
312
313 if (state & NGX_PEER_FAILED
314 || c == NULL
315 || c->read->eof
316 || c->read->error
317 || c->read->timedout
318 || c->write->error
319 || c->write->timedout)
320 {
321 goto invalid;
322 }
323
324 if (c->requests >= kp->conf->requests) {
325 goto invalid;
326 }
327
328 if (!u->keepalive) {
329 goto invalid;
330 }
331
332 if (!u->request_body_sent) {
333 goto invalid;
334 }
335
336 if (ngx_terminate || ngx_exiting) {
337 goto invalid;
338 }
339
340 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
341 goto invalid;
342 }
343
344 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
345 "free keepalive peer: saving connection %p", c);
346
347 if (ngx_queue_empty(&kp->conf->free)) {
348
349 q = ngx_queue_last(&kp->conf->cache);
350 ngx_queue_remove(q);
351
352 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
353
354 ngx_http_upstream_keepalive_close(item->connection);
355
356 } else {
357 q = ngx_queue_head(&kp->conf->free);
358 ngx_queue_remove(q);
359
360 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
361 }
362
363 ngx_queue_insert_head(&kp->conf->cache, q);
364
365 item->connection = c;
366
367 pc->connection = NULL;
368
369 c->read->delayed = 0;
370 ngx_add_timer(c->read, kp->conf->timeout);
371
372 if (c->write->timer_set) {
373 ngx_del_timer(c->write);
374 }
375
376 c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
377 c->read->handler = ngx_http_upstream_keepalive_close_handler;
378
379 c->data = item;
380 c->idle = 1;
381 c->log = ngx_cycle->log;
382 c->read->log = ngx_cycle->log;
383 c->write->log = ngx_cycle->log;
384 c->pool->log = ngx_cycle->log;
385
386 item->socklen = pc->socklen;
387 ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
388
389 if (c->read->ready) {
390 ngx_http_upstream_keepalive_close_handler(c->read);
391 }
392
393 invalid:
394
395 kp->original_free_peer(pc, kp->data, state);
396 }
397
398
399 static void
ngx_http_upstream_keepalive_dummy_handler(ngx_event_t * ev)400 ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
401 {
402 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
403 "keepalive dummy handler");
404 }
405
406
407 static void
ngx_http_upstream_keepalive_close_handler(ngx_event_t * ev)408 ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
409 {
410 ngx_http_upstream_keepalive_srv_conf_t *conf;
411 ngx_http_upstream_keepalive_cache_t *item;
412
413 int n;
414 char buf[1];
415 ngx_connection_t *c;
416
417 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
418 "keepalive close handler");
419
420 c = ev->data;
421
422 if (c->close || c->read->timedout) {
423 goto close;
424 }
425
426 n = recv(c->fd, buf, 1, MSG_PEEK);
427
428 if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
429 ev->ready = 0;
430
431 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
432 goto close;
433 }
434
435 return;
436 }
437
438 close:
439
440 item = c->data;
441 conf = item->conf;
442
443 ngx_http_upstream_keepalive_close(c);
444
445 ngx_queue_remove(&item->queue);
446 ngx_queue_insert_head(&conf->free, &item->queue);
447 }
448
449
450 static void
ngx_http_upstream_keepalive_close(ngx_connection_t * c)451 ngx_http_upstream_keepalive_close(ngx_connection_t *c)
452 {
453
454 #if (NGX_HTTP_SSL)
455
456 if (c->ssl) {
457 c->ssl->no_wait_shutdown = 1;
458 c->ssl->no_send_shutdown = 1;
459
460 if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
461 c->ssl->handler = ngx_http_upstream_keepalive_close;
462 return;
463 }
464 }
465
466 #endif
467
468 ngx_destroy_pool(c->pool);
469 ngx_close_connection(c);
470 }
471
472
473 #if (NGX_HTTP_SSL)
474
475 static ngx_int_t
ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t * pc,void * data)476 ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
477 {
478 ngx_http_upstream_keepalive_peer_data_t *kp = data;
479
480 return kp->original_set_session(pc, kp->data);
481 }
482
483
484 static void
ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t * pc,void * data)485 ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
486 {
487 ngx_http_upstream_keepalive_peer_data_t *kp = data;
488
489 kp->original_save_session(pc, kp->data);
490 return;
491 }
492
493 #endif
494
495
496 static void *
ngx_http_upstream_keepalive_create_conf(ngx_conf_t * cf)497 ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
498 {
499 ngx_http_upstream_keepalive_srv_conf_t *conf;
500
501 conf = ngx_pcalloc(cf->pool,
502 sizeof(ngx_http_upstream_keepalive_srv_conf_t));
503 if (conf == NULL) {
504 return NULL;
505 }
506
507 /*
508 * set by ngx_pcalloc():
509 *
510 * conf->original_init_upstream = NULL;
511 * conf->original_init_peer = NULL;
512 * conf->max_cached = 0;
513 */
514
515 conf->timeout = NGX_CONF_UNSET_MSEC;
516 conf->requests = NGX_CONF_UNSET_UINT;
517
518 return conf;
519 }
520
521
522 static char *
ngx_http_upstream_keepalive(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)523 ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
524 {
525 ngx_http_upstream_srv_conf_t *uscf;
526 ngx_http_upstream_keepalive_srv_conf_t *kcf = conf;
527
528 ngx_int_t n;
529 ngx_str_t *value;
530
531 if (kcf->max_cached) {
532 return "is duplicate";
533 }
534
535 /* read options */
536
537 value = cf->args->elts;
538
539 n = ngx_atoi(value[1].data, value[1].len);
540
541 if (n == NGX_ERROR || n == 0) {
542 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
543 "invalid value \"%V\" in \"%V\" directive",
544 &value[1], &cmd->name);
545 return NGX_CONF_ERROR;
546 }
547
548 kcf->max_cached = n;
549
550 /* init upstream handler */
551
552 uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
553
554 kcf->original_init_upstream = uscf->peer.init_upstream
555 ? uscf->peer.init_upstream
556 : ngx_http_upstream_init_round_robin;
557
558 uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
559
560 return NGX_CONF_OK;
561 }
562