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