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

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  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_http_upstream_conf_t   upstream;
  10.     ngx_int_t                  index;
  11.     ngx_uint_t                 gzip_flag;
  12. } ngx_http_memcached_loc_conf_t;


  13. typedef struct {
  14.     size_t                     rest;
  15.     ngx_http_request_t        *request;
  16.     ngx_str_t                  key;
  17. } ngx_http_memcached_ctx_t;


  18. static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
  19. static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
  20. static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
  21. static ngx_int_t ngx_http_memcached_filter_init(void *data);
  22. static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
  23. static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
  24. static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
  25.     ngx_int_t rc);

  26. static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
  27. static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
  28.     void *parent, void *child);

  29. static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
  30.     void *conf);


  31. static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {
  32.     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
  33.     { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
  34.     { ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
  35.     { ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
  36.     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
  37.     { ngx_null_string, 0 }
  38. };


  39. static ngx_command_t  ngx_http_memcached_commands[] = {

  40.     { ngx_string("memcached_pass"),
  41.       NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
  42.       ngx_http_memcached_pass,
  43.       NGX_HTTP_LOC_CONF_OFFSET,
  44.       0,
  45.       NULL },

  46.     { ngx_string("memcached_bind"),
  47.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  48.       ngx_http_upstream_bind_set_slot,
  49.       NGX_HTTP_LOC_CONF_OFFSET,
  50.       offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
  51.       NULL },

  52.     { ngx_string("memcached_connect_timeout"),
  53.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  54.       ngx_conf_set_msec_slot,
  55.       NGX_HTTP_LOC_CONF_OFFSET,
  56.       offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
  57.       NULL },

  58.     { ngx_string("memcached_send_timeout"),
  59.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  60.       ngx_conf_set_msec_slot,
  61.       NGX_HTTP_LOC_CONF_OFFSET,
  62.       offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
  63.       NULL },

  64.     { ngx_string("memcached_buffer_size"),
  65.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  66.       ngx_conf_set_size_slot,
  67.       NGX_HTTP_LOC_CONF_OFFSET,
  68.       offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
  69.       NULL },

  70.     { ngx_string("memcached_read_timeout"),
  71.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  72.       ngx_conf_set_msec_slot,
  73.       NGX_HTTP_LOC_CONF_OFFSET,
  74.       offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
  75.       NULL },

  76.     { ngx_string("memcached_next_upstream"),
  77.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  78.       ngx_conf_set_bitmask_slot,
  79.       NGX_HTTP_LOC_CONF_OFFSET,
  80.       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
  81.       &ngx_http_memcached_next_upstream_masks },

  82.     { ngx_string("memcached_next_upstream_tries"),
  83.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  84.       ngx_conf_set_num_slot,
  85.       NGX_HTTP_LOC_CONF_OFFSET,
  86.       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_tries),
  87.       NULL },

  88.     { ngx_string("memcached_next_upstream_timeout"),
  89.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  90.       ngx_conf_set_msec_slot,
  91.       NGX_HTTP_LOC_CONF_OFFSET,
  92.       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),
  93.       NULL },

  94.     { ngx_string("memcached_gzip_flag"),
  95.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  96.       ngx_conf_set_num_slot,
  97.       NGX_HTTP_LOC_CONF_OFFSET,
  98.       offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
  99.       NULL },

  100.       ngx_null_command
  101. };


  102. static ngx_http_module_t  ngx_http_memcached_module_ctx = {
  103.     NULL,                                  /* preconfiguration */
  104.     NULL,                                  /* postconfiguration */

  105.     NULL,                                  /* create main configuration */
  106.     NULL,                                  /* init main configuration */

  107.     NULL,                                  /* create server configuration */
  108.     NULL,                                  /* merge server configuration */

  109.     ngx_http_memcached_create_loc_conf,    /* create location configuration */
  110.     ngx_http_memcached_merge_loc_conf      /* merge location configuration */
  111. };


  112. ngx_module_t  ngx_http_memcached_module = {
  113.     NGX_MODULE_V1,
  114.     &ngx_http_memcached_module_ctx,        /* module context */
  115.     ngx_http_memcached_commands,           /* module directives */
  116.     NGX_HTTP_MODULE,                       /* module type */
  117.     NULL,                                  /* init master */
  118.     NULL,                                  /* init module */
  119.     NULL,                                  /* init process */
  120.     NULL,                                  /* init thread */
  121.     NULL,                                  /* exit thread */
  122.     NULL,                                  /* exit process */
  123.     NULL,                                  /* exit master */
  124.     NGX_MODULE_V1_PADDING
  125. };


  126. static ngx_str_t  ngx_http_memcached_key = ngx_string("memcached_key");


  127. #define NGX_HTTP_MEMCACHED_END   (sizeof(ngx_http_memcached_end) - 1)
  128. static u_char  ngx_http_memcached_end[] = CRLF "END" CRLF;


  129. static ngx_int_t
  130. ngx_http_memcached_handler(ngx_http_request_t *r)
  131. {
  132.     ngx_int_t                       rc;
  133.     ngx_http_upstream_t            *u;
  134.     ngx_http_memcached_ctx_t       *ctx;
  135.     ngx_http_memcached_loc_conf_t  *mlcf;

  136.     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
  137.         return NGX_HTTP_NOT_ALLOWED;
  138.     }

  139.     rc = ngx_http_discard_request_body(r);

  140.     if (rc != NGX_OK) {
  141.         return rc;
  142.     }

  143.     if (ngx_http_set_content_type(r) != NGX_OK) {
  144.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  145.     }

  146.     if (ngx_http_upstream_create(r) != NGX_OK) {
  147.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  148.     }

  149.     u = r->upstream;

  150.     ngx_str_set(&u->schema, "memcached://");
  151.     u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;

  152.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

  153.     u->conf = &mlcf->upstream;

  154.     u->create_request = ngx_http_memcached_create_request;
  155.     u->reinit_request = ngx_http_memcached_reinit_request;
  156.     u->process_header = ngx_http_memcached_process_header;
  157.     u->abort_request = ngx_http_memcached_abort_request;
  158.     u->finalize_request = ngx_http_memcached_finalize_request;

  159.     ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
  160.     if (ctx == NULL) {
  161.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  162.     }

  163.     ctx->request = r;

  164.     ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);

  165.     u->input_filter_init = ngx_http_memcached_filter_init;
  166.     u->input_filter = ngx_http_memcached_filter;
  167.     u->input_filter_ctx = ctx;

  168.     r->main->count++;

  169.     ngx_http_upstream_init(r);

  170.     return NGX_DONE;
  171. }


  172. static ngx_int_t
  173. ngx_http_memcached_create_request(ngx_http_request_t *r)
  174. {
  175.     size_t                          len;
  176.     uintptr_t                       escape;
  177.     ngx_buf_t                      *b;
  178.     ngx_chain_t                    *cl;
  179.     ngx_http_memcached_ctx_t       *ctx;
  180.     ngx_http_variable_value_t      *vv;
  181.     ngx_http_memcached_loc_conf_t  *mlcf;

  182.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

  183.     vv = ngx_http_get_indexed_variable(r, mlcf->index);

  184.     if (vv == NULL || vv->not_found || vv->len == 0) {
  185.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  186.                       "the \"$memcached_key\" variable is not set");
  187.         return NGX_ERROR;
  188.     }

  189.     escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);

  190.     len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;

  191.     b = ngx_create_temp_buf(r->pool, len);
  192.     if (b == NULL) {
  193.         return NGX_ERROR;
  194.     }

  195.     cl = ngx_alloc_chain_link(r->pool);
  196.     if (cl == NULL) {
  197.         return NGX_ERROR;
  198.     }

  199.     cl->buf = b;
  200.     cl->next = NULL;

  201.     r->upstream->request_bufs = cl;

  202.     *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';

  203.     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);

  204.     ctx->key.data = b->last;

  205.     if (escape == 0) {
  206.         b->last = ngx_copy(b->last, vv->data, vv->len);

  207.     } else {
  208.         b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
  209.                                             NGX_ESCAPE_MEMCACHED);
  210.     }

  211.     ctx->key.len = b->last - ctx->key.data;

  212.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  213.                    "http memcached request: \"%V\"", &ctx->key);

  214.     *b->last++ = CR; *b->last++ = LF;

  215.     return NGX_OK;
  216. }


  217. static ngx_int_t
  218. ngx_http_memcached_reinit_request(ngx_http_request_t *r)
  219. {
  220.     return NGX_OK;
  221. }


  222. static ngx_int_t
  223. ngx_http_memcached_process_header(ngx_http_request_t *r)
  224. {
  225.     u_char                         *p, *start;
  226.     ngx_str_t                       line;
  227.     ngx_uint_t                      flags;
  228.     ngx_table_elt_t                *h;
  229.     ngx_http_upstream_t            *u;
  230.     ngx_http_memcached_ctx_t       *ctx;
  231.     ngx_http_memcached_loc_conf_t  *mlcf;

  232.     u = r->upstream;

  233.     for (p = u->buffer.pos; p < u->buffer.last; p++) {
  234.         if (*p == LF) {
  235.             goto found;
  236.         }
  237.     }

  238.     return NGX_AGAIN;

  239. found:

  240.     line.data = u->buffer.pos;
  241.     line.len = p - u->buffer.pos;

  242.     if (line.len == 0 || *(p - 1) != CR) {
  243.         goto no_valid;
  244.     }

  245.     *p = '\0';
  246.     line.len--;

  247.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  248.                    "memcached: \"%V\"", &line);

  249.     p = u->buffer.pos;

  250.     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
  251.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

  252.     if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {

  253.         p += sizeof("VALUE ") - 1;

  254.         if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
  255.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  256.                           "memcached sent invalid key in response \"%V\" "
  257.                           "for key \"%V\"",
  258.                           &line, &ctx->key);

  259.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  260.         }

  261.         p += ctx->key.len;

  262.         if (*p++ != ' ') {
  263.             goto no_valid;
  264.         }

  265.         /* flags */

  266.         start = p;

  267.         while (*p) {
  268.             if (*p++ == ' ') {
  269.                 if (mlcf->gzip_flag) {
  270.                     goto flags;
  271.                 } else {
  272.                     goto length;
  273.                 }
  274.             }
  275.         }

  276.         goto no_valid;

  277.     flags:

  278.         flags = ngx_atoi(start, p - start - 1);

  279.         if (flags == (ngx_uint_t) NGX_ERROR) {
  280.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  281.                           "memcached sent invalid flags in response \"%V\" "
  282.                           "for key \"%V\"",
  283.                           &line, &ctx->key);
  284.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  285.         }

  286.         if (flags & mlcf->gzip_flag) {
  287.             h = ngx_list_push(&r->headers_out.headers);
  288.             if (h == NULL) {
  289.                 return NGX_ERROR;
  290.             }

  291.             h->hash = 1;
  292.             ngx_str_set(&h->key, "Content-Encoding");
  293.             ngx_str_set(&h->value, "gzip");
  294.             r->headers_out.content_encoding = h;
  295.         }

  296.     length:

  297.         start = p;
  298.         p = line.data + line.len;

  299.         u->headers_in.content_length_n = ngx_atoof(start, p - start);
  300.         if (u->headers_in.content_length_n == NGX_ERROR) {
  301.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  302.                           "memcached sent invalid length in response \"%V\" "
  303.                           "for key \"%V\"",
  304.                           &line, &ctx->key);
  305.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  306.         }

  307.         u->headers_in.status_n = 200;
  308.         u->state->status = 200;
  309.         u->buffer.pos = p + sizeof(CRLF) - 1;

  310.         return NGX_OK;
  311.     }

  312.     if (ngx_strcmp(p, "END\x0d") == 0) {
  313.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  314.                       "key: \"%V\" was not found by memcached", &ctx->key);

  315.         u->headers_in.content_length_n = 0;
  316.         u->headers_in.status_n = 404;
  317.         u->state->status = 404;
  318.         u->buffer.pos = p + sizeof("END" CRLF) - 1;
  319.         u->keepalive = 1;

  320.         return NGX_OK;
  321.     }

  322. no_valid:

  323.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  324.                   "memcached sent invalid response: \"%V\"", &line);

  325.     return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  326. }


  327. static ngx_int_t
  328. ngx_http_memcached_filter_init(void *data)
  329. {
  330.     ngx_http_memcached_ctx_t  *ctx = data;

  331.     ngx_http_upstream_t  *u;

  332.     u = ctx->request->upstream;

  333.     if (u->headers_in.status_n != 404) {
  334.         u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
  335.         ctx->rest = NGX_HTTP_MEMCACHED_END;

  336.     } else {
  337.         u->length = 0;
  338.     }

  339.     return NGX_OK;
  340. }


  341. static ngx_int_t
  342. ngx_http_memcached_filter(void *data, ssize_t bytes)
  343. {
  344.     ngx_http_memcached_ctx_t  *ctx = data;

  345.     u_char               *last;
  346.     ngx_buf_t            *b;
  347.     ngx_chain_t          *cl, **ll;
  348.     ngx_http_upstream_t  *u;

  349.     u = ctx->request->upstream;
  350.     b = &u->buffer;

  351.     if (u->length == (ssize_t) ctx->rest) {

  352.         if (ngx_strncmp(b->last,
  353.                    ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
  354.                    bytes)
  355.             != 0)
  356.         {
  357.             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
  358.                           "memcached sent invalid trailer");

  359.             u->length = 0;
  360.             ctx->rest = 0;

  361.             return NGX_OK;
  362.         }

  363.         u->length -= bytes;
  364.         ctx->rest -= bytes;

  365.         if (u->length == 0) {
  366.             u->keepalive = 1;
  367.         }

  368.         return NGX_OK;
  369.     }

  370.     for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
  371.         ll = &cl->next;
  372.     }

  373.     cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
  374.     if (cl == NULL) {
  375.         return NGX_ERROR;
  376.     }

  377.     cl->buf->flush = 1;
  378.     cl->buf->memory = 1;

  379.     *ll = cl;

  380.     last = b->last;
  381.     cl->buf->pos = last;
  382.     b->last += bytes;
  383.     cl->buf->last = b->last;
  384.     cl->buf->tag = u->output.tag;

  385.     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
  386.                    "memcached filter bytes:%z size:%z length:%z rest:%z",
  387.                    bytes, b->last - b->pos, u->length, ctx->rest);

  388.     if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
  389.         u->length -= bytes;
  390.         return NGX_OK;
  391.     }

  392.     last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);

  393.     if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
  394.         ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
  395.                       "memcached sent invalid trailer");

  396.         b->last = last;
  397.         cl->buf->last = last;
  398.         u->length = 0;
  399.         ctx->rest = 0;

  400.         return NGX_OK;
  401.     }

  402.     ctx->rest -= b->last - last;
  403.     b->last = last;
  404.     cl->buf->last = last;
  405.     u->length = ctx->rest;

  406.     if (u->length == 0) {
  407.         u->keepalive = 1;
  408.     }

  409.     return NGX_OK;
  410. }


  411. static void
  412. ngx_http_memcached_abort_request(ngx_http_request_t *r)
  413. {
  414.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  415.                    "abort http memcached request");
  416.     return;
  417. }


  418. static void
  419. ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
  420. {
  421.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  422.                    "finalize http memcached request");
  423.     return;
  424. }


  425. static void *
  426. ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
  427. {
  428.     ngx_http_memcached_loc_conf_t  *conf;

  429.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
  430.     if (conf == NULL) {
  431.         return NULL;
  432.     }

  433.     /*
  434.      * set by ngx_pcalloc():
  435.      *
  436.      *     conf->upstream.bufs.num = 0;
  437.      *     conf->upstream.next_upstream = 0;
  438.      *     conf->upstream.temp_path = NULL;
  439.      *     conf->upstream.uri = { 0, NULL };
  440.      *     conf->upstream.location = NULL;
  441.      */

  442.     conf->upstream.local = NGX_CONF_UNSET_PTR;
  443.     conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
  444.     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
  445.     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
  446.     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
  447.     conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

  448.     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

  449.     /* the hardcoded values */
  450.     conf->upstream.cyclic_temp_file = 0;
  451.     conf->upstream.buffering = 0;
  452.     conf->upstream.ignore_client_abort = 0;
  453.     conf->upstream.send_lowat = 0;
  454.     conf->upstream.bufs.num = 0;
  455.     conf->upstream.busy_buffers_size = 0;
  456.     conf->upstream.max_temp_file_size = 0;
  457.     conf->upstream.temp_file_write_size = 0;
  458.     conf->upstream.intercept_errors = 1;
  459.     conf->upstream.intercept_404 = 1;
  460.     conf->upstream.pass_request_headers = 0;
  461.     conf->upstream.pass_request_body = 0;

  462.     conf->index = NGX_CONF_UNSET;
  463.     conf->gzip_flag = NGX_CONF_UNSET_UINT;

  464.     return conf;
  465. }


  466. static char *
  467. ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  468. {
  469.     ngx_http_memcached_loc_conf_t *prev = parent;
  470.     ngx_http_memcached_loc_conf_t *conf = child;

  471.     ngx_conf_merge_ptr_value(conf->upstream.local,
  472.                               prev->upstream.local, NULL);

  473.     ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
  474.                               prev->upstream.next_upstream_tries, 0);

  475.     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
  476.                               prev->upstream.connect_timeout, 60000);

  477.     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
  478.                               prev->upstream.send_timeout, 60000);

  479.     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
  480.                               prev->upstream.read_timeout, 60000);

  481.     ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
  482.                               prev->upstream.next_upstream_timeout, 0);

  483.     ngx_conf_merge_size_value(conf->upstream.buffer_size,
  484.                               prev->upstream.buffer_size,
  485.                               (size_t) ngx_pagesize);

  486.     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
  487.                               prev->upstream.next_upstream,
  488.                               (NGX_CONF_BITMASK_SET
  489.                                |NGX_HTTP_UPSTREAM_FT_ERROR
  490.                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

  491.     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
  492.         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
  493.                                        |NGX_HTTP_UPSTREAM_FT_OFF;
  494.     }

  495.     if (conf->upstream.upstream == NULL) {
  496.         conf->upstream.upstream = prev->upstream.upstream;
  497.     }

  498.     if (conf->index == NGX_CONF_UNSET) {
  499.         conf->index = prev->index;
  500.     }

  501.     ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);

  502.     return NGX_CONF_OK;
  503. }


  504. static char *
  505. ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  506. {
  507.     ngx_http_memcached_loc_conf_t *mlcf = conf;

  508.     ngx_str_t                 *value;
  509.     ngx_url_t                  u;
  510.     ngx_http_core_loc_conf_t  *clcf;

  511.     if (mlcf->upstream.upstream) {
  512.         return "is duplicate";
  513.     }

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

  515.     ngx_memzero(&u, sizeof(ngx_url_t));

  516.     u.url = value[1];
  517.     u.no_resolve = 1;

  518.     mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
  519.     if (mlcf->upstream.upstream == NULL) {
  520.         return NGX_CONF_ERROR;
  521.     }

  522.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  523.     clcf->handler = ngx_http_memcached_handler;

  524.     if (clcf->name.data[clcf->name.len - 1] == '/') {
  525.         clcf->auto_redirect = 1;
  526.     }

  527.     mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);

  528.     if (mlcf->index == NGX_ERROR) {
  529.         return NGX_CONF_ERROR;
  530.     }

  531.     return NGX_CONF_OK;
  532. }