Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.19.2 ]​[ nginx-1.18.0 ]​

0001 
0002 /*
0003  * Copyright (C) Maxim Dounin
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     ngx_uint_t                         max_cached;
0015     ngx_uint_t                         requests;
0016     ngx_msec_t                         timeout;
0017 
0018     ngx_queue_t                        cache;
0019     ngx_queue_t                        free;
0020 
0021     ngx_http_upstream_init_pt          original_init_upstream;
0022     ngx_http_upstream_init_peer_pt     original_init_peer;
0023 
0024 } ngx_http_upstream_keepalive_srv_conf_t;
0025 
0026 
0027 typedef struct {
0028     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0029 
0030     ngx_queue_t                        queue;
0031     ngx_connection_t                  *connection;
0032 
0033     socklen_t                          socklen;
0034     ngx_sockaddr_t                     sockaddr;
0035 
0036 } ngx_http_upstream_keepalive_cache_t;
0037 
0038 
0039 typedef struct {
0040     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0041 
0042     ngx_http_upstream_t               *upstream;
0043 
0044     void                              *data;
0045 
0046     ngx_event_get_peer_pt              original_get_peer;
0047     ngx_event_free_peer_pt             original_free_peer;
0048 
0049 #if (NGX_HTTP_SSL)
0050     ngx_event_set_peer_session_pt      original_set_session;
0051     ngx_event_save_peer_session_pt     original_save_session;
0052 #endif
0053 
0054 } ngx_http_upstream_keepalive_peer_data_t;
0055 
0056 
0057 static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
0058     ngx_http_upstream_srv_conf_t *us);
0059 static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
0060     void *data);
0061 static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
0062     void *data, ngx_uint_t state);
0063 
0064 static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
0065 static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
0066 static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
0067 
0068 #if (NGX_HTTP_SSL)
0069 static ngx_int_t ngx_http_upstream_keepalive_set_session(
0070     ngx_peer_connection_t *pc, void *data);
0071 static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
0072     void *data);
0073 #endif
0074 
0075 static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
0076 static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
0077     void *conf);
0078 
0079 
0080 static ngx_command_t  ngx_http_upstream_keepalive_commands[] = {
0081 
0082     { ngx_string("keepalive"),
0083       NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
0084       ngx_http_upstream_keepalive,
0085       NGX_HTTP_SRV_CONF_OFFSET,
0086       0,
0087       NULL },
0088 
0089     { ngx_string("keepalive_timeout"),
0090       NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
0091       ngx_conf_set_msec_slot,
0092       NGX_HTTP_SRV_CONF_OFFSET,
0093       offsetof(ngx_http_upstream_keepalive_srv_conf_t, timeout),
0094       NULL },
0095 
0096     { ngx_string("keepalive_requests"),
0097       NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
0098       ngx_conf_set_num_slot,
0099       NGX_HTTP_SRV_CONF_OFFSET,
0100       offsetof(ngx_http_upstream_keepalive_srv_conf_t, requests),
0101       NULL },
0102 
0103       ngx_null_command
0104 };
0105 
0106 
0107 static ngx_http_module_t  ngx_http_upstream_keepalive_module_ctx = {
0108     NULL,                                  /* preconfiguration */
0109     NULL,                                  /* postconfiguration */
0110 
0111     NULL,                                  /* create main configuration */
0112     NULL,                                  /* init main configuration */
0113 
0114     ngx_http_upstream_keepalive_create_conf, /* create server configuration */
0115     NULL,                                  /* merge server configuration */
0116 
0117     NULL,                                  /* create location configuration */
0118     NULL                                   /* merge location configuration */
0119 };
0120 
0121 
0122 ngx_module_t  ngx_http_upstream_keepalive_module = {
0123     NGX_MODULE_V1,
0124     &ngx_http_upstream_keepalive_module_ctx, /* module context */
0125     ngx_http_upstream_keepalive_commands,    /* module directives */
0126     NGX_HTTP_MODULE,                       /* module type */
0127     NULL,                                  /* init master */
0128     NULL,                                  /* init module */
0129     NULL,                                  /* init process */
0130     NULL,                                  /* init thread */
0131     NULL,                                  /* exit thread */
0132     NULL,                                  /* exit process */
0133     NULL,                                  /* exit master */
0134     NGX_MODULE_V1_PADDING
0135 };
0136 
0137 
0138 static ngx_int_t
0139 ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
0140     ngx_http_upstream_srv_conf_t *us)
0141 {
0142     ngx_uint_t                               i;
0143     ngx_http_upstream_keepalive_srv_conf_t  *kcf;
0144     ngx_http_upstream_keepalive_cache_t     *cached;
0145 
0146     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
0147                    "init keepalive");
0148 
0149     kcf = ngx_http_conf_upstream_srv_conf(us,
0150                                           ngx_http_upstream_keepalive_module);
0151 
0152     ngx_conf_init_msec_value(kcf->timeout, 60000);
0153     ngx_conf_init_uint_value(kcf->requests, 100);
0154 
0155     if (kcf->original_init_upstream(cf, us) != NGX_OK) {
0156         return NGX_ERROR;
0157     }
0158 
0159     kcf->original_init_peer = us->peer.init;
0160 
0161     us->peer.init = ngx_http_upstream_init_keepalive_peer;
0162 
0163     /* allocate cache items and add to free queue */
0164 
0165     cached = ngx_pcalloc(cf->pool,
0166                 sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
0167     if (cached == NULL) {
0168         return NGX_ERROR;
0169     }
0170 
0171     ngx_queue_init(&kcf->cache);
0172     ngx_queue_init(&kcf->free);
0173 
0174     for (i = 0; i < kcf->max_cached; i++) {
0175         ngx_queue_insert_head(&kcf->free, &cached[i].queue);
0176         cached[i].conf = kcf;
0177     }
0178 
0179     return NGX_OK;
0180 }
0181 
0182 
0183 static ngx_int_t
0184 ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
0185     ngx_http_upstream_srv_conf_t *us)
0186 {
0187     ngx_http_upstream_keepalive_peer_data_t  *kp;
0188     ngx_http_upstream_keepalive_srv_conf_t   *kcf;
0189 
0190     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0191                    "init keepalive peer");
0192 
0193     kcf = ngx_http_conf_upstream_srv_conf(us,
0194                                           ngx_http_upstream_keepalive_module);
0195 
0196     kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
0197     if (kp == NULL) {
0198         return NGX_ERROR;
0199     }
0200 
0201     if (kcf->original_init_peer(r, us) != NGX_OK) {
0202         return NGX_ERROR;
0203     }
0204 
0205     kp->conf = kcf;
0206     kp->upstream = r->upstream;
0207     kp->data = r->upstream->peer.data;
0208     kp->original_get_peer = r->upstream->peer.get;
0209     kp->original_free_peer = r->upstream->peer.free;
0210 
0211     r->upstream->peer.data = kp;
0212     r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
0213     r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
0214 
0215 #if (NGX_HTTP_SSL)
0216     kp->original_set_session = r->upstream->peer.set_session;
0217     kp->original_save_session = r->upstream->peer.save_session;
0218     r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
0219     r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
0220 #endif
0221 
0222     return NGX_OK;
0223 }
0224 
0225 
0226 static ngx_int_t
0227 ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
0228 {
0229     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0230     ngx_http_upstream_keepalive_cache_t      *item;
0231 
0232     ngx_int_t          rc;
0233     ngx_queue_t       *q, *cache;
0234     ngx_connection_t  *c;
0235 
0236     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0237                    "get keepalive peer");
0238 
0239     /* ask balancer */
0240 
0241     rc = kp->original_get_peer(pc, kp->data);
0242 
0243     if (rc != NGX_OK) {
0244         return rc;
0245     }
0246 
0247     /* search cache for suitable connection */
0248 
0249     cache = &kp->conf->cache;
0250 
0251     for (q = ngx_queue_head(cache);
0252          q != ngx_queue_sentinel(cache);
0253          q = ngx_queue_next(q))
0254     {
0255         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
0256         c = item->connection;
0257 
0258         if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
0259                          item->socklen, pc->socklen)
0260             == 0)
0261         {
0262             ngx_queue_remove(q);
0263             ngx_queue_insert_head(&kp->conf->free, q);
0264 
0265             goto found;
0266         }
0267     }
0268 
0269     return NGX_OK;
0270 
0271 found:
0272 
0273     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0274                    "get keepalive peer: using connection %p", c);
0275 
0276     c->idle = 0;
0277     c->sent = 0;
0278     c->data = NULL;
0279     c->log = pc->log;
0280     c->read->log = pc->log;
0281     c->write->log = pc->log;
0282     c->pool->log = pc->log;
0283 
0284     if (c->read->timer_set) {
0285         ngx_del_timer(c->read);
0286     }
0287 
0288     pc->connection = c;
0289     pc->cached = 1;
0290 
0291     return NGX_DONE;
0292 }
0293 
0294 
0295 static void
0296 ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
0297     ngx_uint_t state)
0298 {
0299     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0300     ngx_http_upstream_keepalive_cache_t      *item;
0301 
0302     ngx_queue_t          *q;
0303     ngx_connection_t     *c;
0304     ngx_http_upstream_t  *u;
0305 
0306     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0307                    "free keepalive peer");
0308 
0309     /* cache valid connections */
0310 
0311     u = kp->upstream;
0312     c = pc->connection;
0313 
0314     if (state & NGX_PEER_FAILED
0315         || c == NULL
0316         || c->read->eof
0317         || c->read->error
0318         || c->read->timedout
0319         || c->write->error
0320         || c->write->timedout)
0321     {
0322         goto invalid;
0323     }
0324 
0325     if (c->requests >= kp->conf->requests) {
0326         goto invalid;
0327     }
0328 
0329     if (!u->keepalive) {
0330         goto invalid;
0331     }
0332 
0333     if (!u->request_body_sent) {
0334         goto invalid;
0335     }
0336 
0337     if (ngx_terminate || ngx_exiting) {
0338         goto invalid;
0339     }
0340 
0341     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
0342         goto invalid;
0343     }
0344 
0345     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0346                    "free keepalive peer: saving connection %p", c);
0347 
0348     if (ngx_queue_empty(&kp->conf->free)) {
0349 
0350         q = ngx_queue_last(&kp->conf->cache);
0351         ngx_queue_remove(q);
0352 
0353         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
0354 
0355         ngx_http_upstream_keepalive_close(item->connection);
0356 
0357     } else {
0358         q = ngx_queue_head(&kp->conf->free);
0359         ngx_queue_remove(q);
0360 
0361         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
0362     }
0363 
0364     ngx_queue_insert_head(&kp->conf->cache, q);
0365 
0366     item->connection = c;
0367 
0368     pc->connection = NULL;
0369 
0370     c->read->delayed = 0;
0371     ngx_add_timer(c->read, kp->conf->timeout);
0372 
0373     if (c->write->timer_set) {
0374         ngx_del_timer(c->write);
0375     }
0376 
0377     c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
0378     c->read->handler = ngx_http_upstream_keepalive_close_handler;
0379 
0380     c->data = item;
0381     c->idle = 1;
0382     c->log = ngx_cycle->log;
0383     c->read->log = ngx_cycle->log;
0384     c->write->log = ngx_cycle->log;
0385     c->pool->log = ngx_cycle->log;
0386 
0387     item->socklen = pc->socklen;
0388     ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
0389 
0390     if (c->read->ready) {
0391         ngx_http_upstream_keepalive_close_handler(c->read);
0392     }
0393 
0394 invalid:
0395 
0396     kp->original_free_peer(pc, kp->data, state);
0397 }
0398 
0399 
0400 static void
0401 ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
0402 {
0403     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
0404                    "keepalive dummy handler");
0405 }
0406 
0407 
0408 static void
0409 ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
0410 {
0411     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0412     ngx_http_upstream_keepalive_cache_t     *item;
0413 
0414     int                n;
0415     char               buf[1];
0416     ngx_connection_t  *c;
0417 
0418     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
0419                    "keepalive close handler");
0420 
0421     c = ev->data;
0422 
0423     if (c->close || c->read->timedout) {
0424         goto close;
0425     }
0426 
0427     n = recv(c->fd, buf, 1, MSG_PEEK);
0428 
0429     if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
0430         ev->ready = 0;
0431 
0432         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
0433             goto close;
0434         }
0435 
0436         return;
0437     }
0438 
0439 close:
0440 
0441     item = c->data;
0442     conf = item->conf;
0443 
0444     ngx_http_upstream_keepalive_close(c);
0445 
0446     ngx_queue_remove(&item->queue);
0447     ngx_queue_insert_head(&conf->free, &item->queue);
0448 }
0449 
0450 
0451 static void
0452 ngx_http_upstream_keepalive_close(ngx_connection_t *c)
0453 {
0454 
0455 #if (NGX_HTTP_SSL)
0456 
0457     if (c->ssl) {
0458         c->ssl->no_wait_shutdown = 1;
0459         c->ssl->no_send_shutdown = 1;
0460 
0461         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
0462             c->ssl->handler = ngx_http_upstream_keepalive_close;
0463             return;
0464         }
0465     }
0466 
0467 #endif
0468 
0469     ngx_destroy_pool(c->pool);
0470     ngx_close_connection(c);
0471 }
0472 
0473 
0474 #if (NGX_HTTP_SSL)
0475 
0476 static ngx_int_t
0477 ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
0478 {
0479     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0480 
0481     return kp->original_set_session(pc, kp->data);
0482 }
0483 
0484 
0485 static void
0486 ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
0487 {
0488     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0489 
0490     kp->original_save_session(pc, kp->data);
0491     return;
0492 }
0493 
0494 #endif
0495 
0496 
0497 static void *
0498 ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
0499 {
0500     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0501 
0502     conf = ngx_pcalloc(cf->pool,
0503                        sizeof(ngx_http_upstream_keepalive_srv_conf_t));
0504     if (conf == NULL) {
0505         return NULL;
0506     }
0507 
0508     /*
0509      * set by ngx_pcalloc():
0510      *
0511      *     conf->original_init_upstream = NULL;
0512      *     conf->original_init_peer = NULL;
0513      *     conf->max_cached = 0;
0514      */
0515 
0516     conf->timeout = NGX_CONF_UNSET_MSEC;
0517     conf->requests = NGX_CONF_UNSET_UINT;
0518 
0519     return conf;
0520 }
0521 
0522 
0523 static char *
0524 ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0525 {
0526     ngx_http_upstream_srv_conf_t            *uscf;
0527     ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;
0528 
0529     ngx_int_t    n;
0530     ngx_str_t   *value;
0531 
0532     if (kcf->max_cached) {
0533         return "is duplicate";
0534     }
0535 
0536     /* read options */
0537 
0538     value = cf->args->elts;
0539 
0540     n = ngx_atoi(value[1].data, value[1].len);
0541 
0542     if (n == NGX_ERROR || n == 0) {
0543         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0544                            "invalid value \"%V\" in \"%V\" directive",
0545                            &value[1], &cmd->name);
0546         return NGX_CONF_ERROR;
0547     }
0548 
0549     kcf->max_cached = n;
0550 
0551     /* init upstream handler */
0552 
0553     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
0554 
0555     kcf->original_init_upstream = uscf->peer.init_upstream
0556                                   ? uscf->peer.init_upstream
0557                                   : ngx_http_upstream_init_round_robin;
0558 
0559     uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
0560 
0561     return NGX_CONF_OK;
0562 }