Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.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->log = pc->log;
0279     c->read->log = pc->log;
0280     c->write->log = pc->log;
0281     c->pool->log = pc->log;
0282 
0283     if (c->read->timer_set) {
0284         ngx_del_timer(c->read);
0285     }
0286 
0287     pc->connection = c;
0288     pc->cached = 1;
0289 
0290     return NGX_DONE;
0291 }
0292 
0293 
0294 static void
0295 ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
0296     ngx_uint_t state)
0297 {
0298     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0299     ngx_http_upstream_keepalive_cache_t      *item;
0300 
0301     ngx_queue_t          *q;
0302     ngx_connection_t     *c;
0303     ngx_http_upstream_t  *u;
0304 
0305     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0306                    "free keepalive peer");
0307 
0308     /* cache valid connections */
0309 
0310     u = kp->upstream;
0311     c = pc->connection;
0312 
0313     if (state & NGX_PEER_FAILED
0314         || c == NULL
0315         || c->read->eof
0316         || c->read->error
0317         || c->read->timedout
0318         || c->write->error
0319         || c->write->timedout)
0320     {
0321         goto invalid;
0322     }
0323 
0324     if (c->requests >= kp->conf->requests) {
0325         goto invalid;
0326     }
0327 
0328     if (!u->keepalive) {
0329         goto invalid;
0330     }
0331 
0332     if (!u->request_body_sent) {
0333         goto invalid;
0334     }
0335 
0336     if (ngx_terminate || ngx_exiting) {
0337         goto invalid;
0338     }
0339 
0340     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
0341         goto invalid;
0342     }
0343 
0344     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
0345                    "free keepalive peer: saving connection %p", c);
0346 
0347     if (ngx_queue_empty(&kp->conf->free)) {
0348 
0349         q = ngx_queue_last(&kp->conf->cache);
0350         ngx_queue_remove(q);
0351 
0352         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
0353 
0354         ngx_http_upstream_keepalive_close(item->connection);
0355 
0356     } else {
0357         q = ngx_queue_head(&kp->conf->free);
0358         ngx_queue_remove(q);
0359 
0360         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
0361     }
0362 
0363     ngx_queue_insert_head(&kp->conf->cache, q);
0364 
0365     item->connection = c;
0366 
0367     pc->connection = NULL;
0368 
0369     c->read->delayed = 0;
0370     ngx_add_timer(c->read, kp->conf->timeout);
0371 
0372     if (c->write->timer_set) {
0373         ngx_del_timer(c->write);
0374     }
0375 
0376     c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
0377     c->read->handler = ngx_http_upstream_keepalive_close_handler;
0378 
0379     c->data = item;
0380     c->idle = 1;
0381     c->log = ngx_cycle->log;
0382     c->read->log = ngx_cycle->log;
0383     c->write->log = ngx_cycle->log;
0384     c->pool->log = ngx_cycle->log;
0385 
0386     item->socklen = pc->socklen;
0387     ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
0388 
0389     if (c->read->ready) {
0390         ngx_http_upstream_keepalive_close_handler(c->read);
0391     }
0392 
0393 invalid:
0394 
0395     kp->original_free_peer(pc, kp->data, state);
0396 }
0397 
0398 
0399 static void
0400 ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
0401 {
0402     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
0403                    "keepalive dummy handler");
0404 }
0405 
0406 
0407 static void
0408 ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
0409 {
0410     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0411     ngx_http_upstream_keepalive_cache_t     *item;
0412 
0413     int                n;
0414     char               buf[1];
0415     ngx_connection_t  *c;
0416 
0417     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
0418                    "keepalive close handler");
0419 
0420     c = ev->data;
0421 
0422     if (c->close || c->read->timedout) {
0423         goto close;
0424     }
0425 
0426     n = recv(c->fd, buf, 1, MSG_PEEK);
0427 
0428     if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
0429         ev->ready = 0;
0430 
0431         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
0432             goto close;
0433         }
0434 
0435         return;
0436     }
0437 
0438 close:
0439 
0440     item = c->data;
0441     conf = item->conf;
0442 
0443     ngx_http_upstream_keepalive_close(c);
0444 
0445     ngx_queue_remove(&item->queue);
0446     ngx_queue_insert_head(&conf->free, &item->queue);
0447 }
0448 
0449 
0450 static void
0451 ngx_http_upstream_keepalive_close(ngx_connection_t *c)
0452 {
0453 
0454 #if (NGX_HTTP_SSL)
0455 
0456     if (c->ssl) {
0457         c->ssl->no_wait_shutdown = 1;
0458         c->ssl->no_send_shutdown = 1;
0459 
0460         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
0461             c->ssl->handler = ngx_http_upstream_keepalive_close;
0462             return;
0463         }
0464     }
0465 
0466 #endif
0467 
0468     ngx_destroy_pool(c->pool);
0469     ngx_close_connection(c);
0470 }
0471 
0472 
0473 #if (NGX_HTTP_SSL)
0474 
0475 static ngx_int_t
0476 ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
0477 {
0478     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0479 
0480     return kp->original_set_session(pc, kp->data);
0481 }
0482 
0483 
0484 static void
0485 ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
0486 {
0487     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
0488 
0489     kp->original_save_session(pc, kp->data);
0490     return;
0491 }
0492 
0493 #endif
0494 
0495 
0496 static void *
0497 ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
0498 {
0499     ngx_http_upstream_keepalive_srv_conf_t  *conf;
0500 
0501     conf = ngx_pcalloc(cf->pool,
0502                        sizeof(ngx_http_upstream_keepalive_srv_conf_t));
0503     if (conf == NULL) {
0504         return NULL;
0505     }
0506 
0507     /*
0508      * set by ngx_pcalloc():
0509      *
0510      *     conf->original_init_upstream = NULL;
0511      *     conf->original_init_peer = NULL;
0512      *     conf->max_cached = 0;
0513      */
0514 
0515     conf->timeout = NGX_CONF_UNSET_MSEC;
0516     conf->requests = NGX_CONF_UNSET_UINT;
0517 
0518     return conf;
0519 }
0520 
0521 
0522 static char *
0523 ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
0524 {
0525     ngx_http_upstream_srv_conf_t            *uscf;
0526     ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;
0527 
0528     ngx_int_t    n;
0529     ngx_str_t   *value;
0530 
0531     if (kcf->max_cached) {
0532         return "is duplicate";
0533     }
0534 
0535     /* read options */
0536 
0537     value = cf->args->elts;
0538 
0539     n = ngx_atoi(value[1].data, value[1].len);
0540 
0541     if (n == NGX_ERROR || n == 0) {
0542         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
0543                            "invalid value \"%V\" in \"%V\" directive",
0544                            &value[1], &cmd->name);
0545         return NGX_CONF_ERROR;
0546     }
0547 
0548     kcf->max_cached = n;
0549 
0550     /* init upstream handler */
0551 
0552     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
0553 
0554     kcf->original_init_upstream = uscf->peer.init_upstream
0555                                   ? uscf->peer.init_upstream
0556                                   : ngx_http_upstream_init_round_robin;
0557 
0558     uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
0559 
0560     return NGX_CONF_OK;
0561 }