src/http/modules/ngx_http_upstream_keepalive_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Maxim Dounin
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     ngx_uint_t                         max_cached;

  10.     ngx_queue_t                        cache;
  11.     ngx_queue_t                        free;

  12.     ngx_http_upstream_init_pt          original_init_upstream;
  13.     ngx_http_upstream_init_peer_pt     original_init_peer;

  14. } ngx_http_upstream_keepalive_srv_conf_t;


  15. typedef struct {
  16.     ngx_http_upstream_keepalive_srv_conf_t  *conf;

  17.     ngx_http_upstream_t               *upstream;

  18.     void                              *data;

  19.     ngx_event_get_peer_pt              original_get_peer;
  20.     ngx_event_free_peer_pt             original_free_peer;

  21. #if (NGX_HTTP_SSL)
  22.     ngx_event_set_peer_session_pt      original_set_session;
  23.     ngx_event_save_peer_session_pt     original_save_session;
  24. #endif

  25. } ngx_http_upstream_keepalive_peer_data_t;


  26. typedef struct {
  27.     ngx_http_upstream_keepalive_srv_conf_t  *conf;

  28.     ngx_queue_t                        queue;
  29.     ngx_connection_t                  *connection;

  30.     socklen_t                          socklen;
  31.     u_char                             sockaddr[NGX_SOCKADDRLEN];

  32. } ngx_http_upstream_keepalive_cache_t;


  33. static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
  34.     ngx_http_upstream_srv_conf_t *us);
  35. static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
  36.     void *data);
  37. static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
  38.     void *data, ngx_uint_t state);

  39. static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
  40. static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
  41. static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);


  42. #if (NGX_HTTP_SSL)
  43. static ngx_int_t ngx_http_upstream_keepalive_set_session(
  44.     ngx_peer_connection_t *pc, void *data);
  45. static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
  46.     void *data);
  47. #endif

  48. static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
  49. static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
  50.     void *conf);


  51. static ngx_command_t  ngx_http_upstream_keepalive_commands[] = {

  52.     { ngx_string("keepalive"),
  53.       NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
  54.       ngx_http_upstream_keepalive,
  55.       NGX_HTTP_SRV_CONF_OFFSET,
  56.       0,
  57.       NULL },

  58.       ngx_null_command
  59. };


  60. static ngx_http_module_t  ngx_http_upstream_keepalive_module_ctx = {
  61.     NULL,                                  /* preconfiguration */
  62.     NULL,                                  /* postconfiguration */

  63.     NULL,                                  /* create main configuration */
  64.     NULL,                                  /* init main configuration */

  65.     ngx_http_upstream_keepalive_create_conf, /* create server configuration */
  66.     NULL,                                  /* merge server configuration */

  67.     NULL,                                  /* create location configuration */
  68.     NULL                                   /* merge location configuration */
  69. };


  70. ngx_module_t  ngx_http_upstream_keepalive_module = {
  71.     NGX_MODULE_V1,
  72.     &ngx_http_upstream_keepalive_module_ctx, /* module context */
  73.     ngx_http_upstream_keepalive_commands,    /* module directives */
  74.     NGX_HTTP_MODULE,                       /* module type */
  75.     NULL,                                  /* init master */
  76.     NULL,                                  /* init module */
  77.     NULL,                                  /* init process */
  78.     NULL,                                  /* init thread */
  79.     NULL,                                  /* exit thread */
  80.     NULL,                                  /* exit process */
  81.     NULL,                                  /* exit master */
  82.     NGX_MODULE_V1_PADDING
  83. };


  84. static ngx_int_t
  85. ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
  86.     ngx_http_upstream_srv_conf_t *us)
  87. {
  88.     ngx_uint_t                               i;
  89.     ngx_http_upstream_keepalive_srv_conf_t  *kcf;
  90.     ngx_http_upstream_keepalive_cache_t     *cached;

  91.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
  92.                    "init keepalive");

  93.     kcf = ngx_http_conf_upstream_srv_conf(us,
  94.                                           ngx_http_upstream_keepalive_module);

  95.     if (kcf->original_init_upstream(cf, us) != NGX_OK) {
  96.         return NGX_ERROR;
  97.     }

  98.     kcf->original_init_peer = us->peer.init;

  99.     us->peer.init = ngx_http_upstream_init_keepalive_peer;

  100.     /* allocate cache items and add to free queue */

  101.     cached = ngx_pcalloc(cf->pool,
  102.                 sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
  103.     if (cached == NULL) {
  104.         return NGX_ERROR;
  105.     }

  106.     ngx_queue_init(&kcf->cache);
  107.     ngx_queue_init(&kcf->free);

  108.     for (i = 0; i < kcf->max_cached; i++) {
  109.         ngx_queue_insert_head(&kcf->free, &cached[i].queue);
  110.         cached[i].conf = kcf;
  111.     }

  112.     return NGX_OK;
  113. }


  114. static ngx_int_t
  115. ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
  116.     ngx_http_upstream_srv_conf_t *us)
  117. {
  118.     ngx_http_upstream_keepalive_peer_data_t  *kp;
  119.     ngx_http_upstream_keepalive_srv_conf_t   *kcf;

  120.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  121.                    "init keepalive peer");

  122.     kcf = ngx_http_conf_upstream_srv_conf(us,
  123.                                           ngx_http_upstream_keepalive_module);

  124.     kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
  125.     if (kp == NULL) {
  126.         return NGX_ERROR;
  127.     }

  128.     if (kcf->original_init_peer(r, us) != NGX_OK) {
  129.         return NGX_ERROR;
  130.     }

  131.     kp->conf = kcf;
  132.     kp->upstream = r->upstream;
  133.     kp->data = r->upstream->peer.data;
  134.     kp->original_get_peer = r->upstream->peer.get;
  135.     kp->original_free_peer = r->upstream->peer.free;

  136.     r->upstream->peer.data = kp;
  137.     r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
  138.     r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;

  139. #if (NGX_HTTP_SSL)
  140.     kp->original_set_session = r->upstream->peer.set_session;
  141.     kp->original_save_session = r->upstream->peer.save_session;
  142.     r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
  143.     r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
  144. #endif

  145.     return NGX_OK;
  146. }


  147. static ngx_int_t
  148. ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
  149. {
  150.     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
  151.     ngx_http_upstream_keepalive_cache_t      *item;

  152.     ngx_int_t          rc;
  153.     ngx_queue_t       *q, *cache;
  154.     ngx_connection_t  *c;

  155.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  156.                    "get keepalive peer");

  157.     /* ask balancer */

  158.     rc = kp->original_get_peer(pc, kp->data);

  159.     if (rc != NGX_OK) {
  160.         return rc;
  161.     }

  162.     /* search cache for suitable connection */

  163.     cache = &kp->conf->cache;

  164.     for (q = ngx_queue_head(cache);
  165.          q != ngx_queue_sentinel(cache);
  166.          q = ngx_queue_next(q))
  167.     {
  168.         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
  169.         c = item->connection;

  170.         if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
  171.                          item->socklen, pc->socklen)
  172.             == 0)
  173.         {
  174.             ngx_queue_remove(q);
  175.             ngx_queue_insert_head(&kp->conf->free, q);

  176.             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  177.                            "get keepalive peer: using connection %p", c);

  178.             c->idle = 0;
  179.             c->sent = 0;
  180.             c->log = pc->log;
  181.             c->read->log = pc->log;
  182.             c->write->log = pc->log;
  183.             c->pool->log = pc->log;

  184.             pc->connection = c;
  185.             pc->cached = 1;

  186.             return NGX_DONE;
  187.         }
  188.     }

  189.     return NGX_OK;
  190. }


  191. static void
  192. ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
  193.     ngx_uint_t state)
  194. {
  195.     ngx_http_upstream_keepalive_peer_data_t  *kp = data;
  196.     ngx_http_upstream_keepalive_cache_t      *item;

  197.     ngx_queue_t          *q;
  198.     ngx_connection_t     *c;
  199.     ngx_http_upstream_t  *u;

  200.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  201.                    "free keepalive peer");

  202.     /* cache valid connections */

  203.     u = kp->upstream;
  204.     c = pc->connection;

  205.     if (state & NGX_PEER_FAILED
  206.         || c == NULL
  207.         || c->read->eof
  208.         || c->read->error
  209.         || c->read->timedout
  210.         || c->write->error
  211.         || c->write->timedout)
  212.     {
  213.         goto invalid;
  214.     }

  215.     if (!u->keepalive) {
  216.         goto invalid;
  217.     }

  218.     if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  219.         goto invalid;
  220.     }

  221.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  222.                    "free keepalive peer: saving connection %p", c);

  223.     if (ngx_queue_empty(&kp->conf->free)) {

  224.         q = ngx_queue_last(&kp->conf->cache);
  225.         ngx_queue_remove(q);

  226.         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

  227.         ngx_http_upstream_keepalive_close(item->connection);

  228.     } else {
  229.         q = ngx_queue_head(&kp->conf->free);
  230.         ngx_queue_remove(q);

  231.         item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
  232.     }

  233.     item->connection = c;
  234.     ngx_queue_insert_head(&kp->conf->cache, q);

  235.     pc->connection = NULL;

  236.     if (c->read->timer_set) {
  237.         ngx_del_timer(c->read);
  238.     }
  239.     if (c->write->timer_set) {
  240.         ngx_del_timer(c->write);
  241.     }

  242.     c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
  243.     c->read->handler = ngx_http_upstream_keepalive_close_handler;

  244.     c->data = item;
  245.     c->idle = 1;
  246.     c->log = ngx_cycle->log;
  247.     c->read->log = ngx_cycle->log;
  248.     c->write->log = ngx_cycle->log;
  249.     c->pool->log = ngx_cycle->log;

  250.     item->socklen = pc->socklen;
  251.     ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);

  252.     if (c->read->ready) {
  253.         ngx_http_upstream_keepalive_close_handler(c->read);
  254.     }

  255. invalid:

  256.     kp->original_free_peer(pc, kp->data, state);
  257. }


  258. static void
  259. ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
  260. {
  261.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
  262.                    "keepalive dummy handler");
  263. }


  264. static void
  265. ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
  266. {
  267.     ngx_http_upstream_keepalive_srv_conf_t  *conf;
  268.     ngx_http_upstream_keepalive_cache_t     *item;

  269.     int                n;
  270.     char               buf[1];
  271.     ngx_connection_t  *c;

  272.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
  273.                    "keepalive close handler");

  274.     c = ev->data;

  275.     if (c->close) {
  276.         goto close;
  277.     }

  278.     n = recv(c->fd, buf, 1, MSG_PEEK);

  279.     if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
  280.         /* stale event */

  281.         if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
  282.             goto close;
  283.         }

  284.         return;
  285.     }

  286. close:

  287.     item = c->data;
  288.     conf = item->conf;

  289.     ngx_http_upstream_keepalive_close(c);

  290.     ngx_queue_remove(&item->queue);
  291.     ngx_queue_insert_head(&conf->free, &item->queue);
  292. }


  293. static void
  294. ngx_http_upstream_keepalive_close(ngx_connection_t *c)
  295. {

  296. #if (NGX_HTTP_SSL)

  297.     if (c->ssl) {
  298.         c->ssl->no_wait_shutdown = 1;
  299.         c->ssl->no_send_shutdown = 1;

  300.         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
  301.             c->ssl->handler = ngx_http_upstream_keepalive_close;
  302.             return;
  303.         }
  304.     }

  305. #endif

  306.     ngx_destroy_pool(c->pool);
  307.     ngx_close_connection(c);
  308. }


  309. #if (NGX_HTTP_SSL)

  310. static ngx_int_t
  311. ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
  312. {
  313.     ngx_http_upstream_keepalive_peer_data_t  *kp = data;

  314.     return kp->original_set_session(pc, kp->data);
  315. }


  316. static void
  317. ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
  318. {
  319.     ngx_http_upstream_keepalive_peer_data_t  *kp = data;

  320.     kp->original_save_session(pc, kp->data);
  321.     return;
  322. }

  323. #endif


  324. static void *
  325. ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
  326. {
  327.     ngx_http_upstream_keepalive_srv_conf_t  *conf;

  328.     conf = ngx_pcalloc(cf->pool,
  329.                        sizeof(ngx_http_upstream_keepalive_srv_conf_t));
  330.     if (conf == NULL) {
  331.         return NULL;
  332.     }

  333.     /*
  334.      * set by ngx_pcalloc():
  335.      *
  336.      *     conf->original_init_upstream = NULL;
  337.      *     conf->original_init_peer = NULL;
  338.      */

  339.     conf->max_cached = 1;

  340.     return conf;
  341. }


  342. static char *
  343. ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  344. {
  345.     ngx_http_upstream_srv_conf_t            *uscf;
  346.     ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;

  347.     ngx_int_t    n;
  348.     ngx_str_t   *value;

  349.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  350.     if (kcf->original_init_upstream) {
  351.         return "is duplicate";
  352.     }

  353.     kcf->original_init_upstream = uscf->peer.init_upstream
  354.                                   ? uscf->peer.init_upstream
  355.                                   : ngx_http_upstream_init_round_robin;

  356.     uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;

  357.     /* read options */

  358.     value = cf->args->elts;

  359.     n = ngx_atoi(value[1].data, value[1].len);

  360.     if (n == NGX_ERROR || n == 0) {
  361.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  362.                            "invalid value \"%V\" in \"%V\" directive",
  363.                            &value[1], &cmd->name);
  364.         return NGX_CONF_ERROR;
  365.     }

  366.     kcf->max_cached = n;

  367.     return NGX_CONF_OK;
  368. }