src/http/modules/ngx_http_memcached_module.c - nginx source code

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_TAKE12,
  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_socket_keepalive"),
  53.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  54.       ngx_conf_set_flag_slot,
  55.       NGX_HTTP_LOC_CONF_OFFSET,
  56.       offsetof(ngx_http_memcached_loc_conf_t, upstream.socket_keepalive),
  57.       NULL },

  58.     { ngx_string("memcached_connect_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.connect_timeout),
  63.       NULL },

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

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

  76.     { ngx_string("memcached_read_timeout"),
  77.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  78.       ngx_conf_set_msec_slot,
  79.       NGX_HTTP_LOC_CONF_OFFSET,
  80.       offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
  81.       NULL },

  82.     { ngx_string("memcached_next_upstream"),
  83.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  84.       ngx_conf_set_bitmask_slot,
  85.       NGX_HTTP_LOC_CONF_OFFSET,
  86.       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
  87.       &ngx_http_memcached_next_upstream_masks },

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

  94.     { ngx_string("memcached_next_upstream_timeout"),
  95.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  96.       ngx_conf_set_msec_slot,
  97.       NGX_HTTP_LOC_CONF_OFFSET,
  98.       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),
  99.       NULL },

  100.     { ngx_string("memcached_gzip_flag"),
  101.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  102.       ngx_conf_set_num_slot,
  103.       NGX_HTTP_LOC_CONF_OFFSET,
  104.       offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
  105.       NULL },

  106.       ngx_null_command
  107. };


  108. static ngx_http_module_t  ngx_http_memcached_module_ctx = {
  109.     NULL,                                  /* preconfiguration */
  110.     NULL,                                  /* postconfiguration */

  111.     NULL,                                  /* create main configuration */
  112.     NULL,                                  /* init main configuration */

  113.     NULL,                                  /* create server configuration */
  114.     NULL,                                  /* merge server configuration */

  115.     ngx_http_memcached_create_loc_conf,    /* create location configuration */
  116.     ngx_http_memcached_merge_loc_conf      /* merge location configuration */
  117. };


  118. ngx_module_t  ngx_http_memcached_module = {
  119.     NGX_MODULE_V1,
  120.     &ngx_http_memcached_module_ctx,        /* module context */
  121.     ngx_http_memcached_commands,           /* module directives */
  122.     NGX_HTTP_MODULE,                       /* module type */
  123.     NULL,                                  /* init master */
  124.     NULL,                                  /* init module */
  125.     NULL,                                  /* init process */
  126.     NULL,                                  /* init thread */
  127.     NULL,                                  /* exit thread */
  128.     NULL,                                  /* exit process */
  129.     NULL,                                  /* exit master */
  130.     NGX_MODULE_V1_PADDING
  131. };


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


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


  135. static ngx_int_t
  136. ngx_http_memcached_handler(ngx_http_request_t *r)
  137. {
  138.     ngx_int_t                       rc;
  139.     ngx_http_upstream_t            *u;
  140.     ngx_http_memcached_ctx_t       *ctx;
  141.     ngx_http_memcached_loc_conf_t  *mlcf;

  142.     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
  143.         return NGX_HTTP_NOT_ALLOWED;
  144.     }

  145.     rc = ngx_http_discard_request_body(r);

  146.     if (rc != NGX_OK) {
  147.         return rc;
  148.     }

  149.     if (ngx_http_set_content_type(r) != NGX_OK) {
  150.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  151.     }

  152.     if (ngx_http_upstream_create(r) != NGX_OK) {
  153.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  154.     }

  155.     u = r->upstream;

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

  158.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

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

  160.     u->create_request = ngx_http_memcached_create_request;
  161.     u->reinit_request = ngx_http_memcached_reinit_request;
  162.     u->process_header = ngx_http_memcached_process_header;
  163.     u->abort_request = ngx_http_memcached_abort_request;
  164.     u->finalize_request = ngx_http_memcached_finalize_request;

  165.     ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
  166.     if (ctx == NULL) {
  167.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  168.     }

  169.     ctx->request = r;

  170.     ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);

  171.     u->input_filter_init = ngx_http_memcached_filter_init;
  172.     u->input_filter = ngx_http_memcached_filter;
  173.     u->input_filter_ctx = ctx;

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

  175.     ngx_http_upstream_init(r);

  176.     return NGX_DONE;
  177. }


  178. static ngx_int_t
  179. ngx_http_memcached_create_request(ngx_http_request_t *r)
  180. {
  181.     size_t                          len;
  182.     uintptr_t                       escape;
  183.     ngx_buf_t                      *b;
  184.     ngx_chain_t                    *cl;
  185.     ngx_http_memcached_ctx_t       *ctx;
  186.     ngx_http_variable_value_t      *vv;
  187.     ngx_http_memcached_loc_conf_t  *mlcf;

  188.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

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

  190.     if (vv == NULL || vv->not_found || vv->len == 0) {
  191.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  192.                       "the \"$memcached_key\" variable is not set");
  193.         return NGX_ERROR;
  194.     }

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

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

  197.     b = ngx_create_temp_buf(r->pool, len);
  198.     if (b == NULL) {
  199.         return NGX_ERROR;
  200.     }

  201.     cl = ngx_alloc_chain_link(r->pool);
  202.     if (cl == NULL) {
  203.         return NGX_ERROR;
  204.     }

  205.     cl->buf = b;
  206.     cl->next = NULL;

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

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

  209.     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);

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

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

  213.     } else {
  214.         b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
  215.                                             NGX_ESCAPE_MEMCACHED);
  216.     }

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

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

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

  221.     return NGX_OK;
  222. }


  223. static ngx_int_t
  224. ngx_http_memcached_reinit_request(ngx_http_request_t *r)
  225. {
  226.     return NGX_OK;
  227. }


  228. static ngx_int_t
  229. ngx_http_memcached_process_header(ngx_http_request_t *r)
  230. {
  231.     u_char                         *p, *start;
  232.     ngx_str_t                       line;
  233.     ngx_uint_t                      flags;
  234.     ngx_table_elt_t                *h;
  235.     ngx_http_upstream_t            *u;
  236.     ngx_http_memcached_ctx_t       *ctx;
  237.     ngx_http_memcached_loc_conf_t  *mlcf;

  238.     u = r->upstream;

  239.     for (p = u->buffer.pos; p < u->buffer.last; p++) {
  240.         if (*p == LF) {
  241.             goto found;
  242.         }
  243.     }

  244.     return NGX_AGAIN;

  245. found:

  246.     line.data = u->buffer.pos;
  247.     line.len = p - u->buffer.pos;

  248.     if (line.len == 0 || *(p - 1) != CR) {
  249.         goto no_valid;
  250.     }

  251.     *p = '\0';
  252.     line.len--;

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

  255.     p = u->buffer.pos;

  256.     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
  257.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);

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

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

  260.         if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
  261.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  262.                           "memcached sent invalid key in response \"%V\" "
  263.                           "for key \"%V\"",
  264.                           &line, &ctx->key);

  265.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  266.         }

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

  268.         if (*p++ != ' ') {
  269.             goto no_valid;
  270.         }

  271.         /* flags */

  272.         start = p;

  273.         while (*p) {
  274.             if (*p++ == ' ') {
  275.                 if (mlcf->gzip_flag) {
  276.                     goto flags;
  277.                 } else {
  278.                     goto length;
  279.                 }
  280.             }
  281.         }

  282.         goto no_valid;

  283.     flags:

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

  285.         if (flags == (ngx_uint_t) NGX_ERROR) {
  286.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  287.                           "memcached sent invalid flags in response \"%V\" "
  288.                           "for key \"%V\"",
  289.                           &line, &ctx->key);
  290.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  291.         }

  292.         if (flags & mlcf->gzip_flag) {
  293.             h = ngx_list_push(&r->headers_out.headers);
  294.             if (h == NULL) {
  295.                 return NGX_ERROR;
  296.             }

  297.             h->hash = 1;
  298.             h->next = NULL;
  299.             ngx_str_set(&h->key, "Content-Encoding");
  300.             ngx_str_set(&h->value, "gzip");
  301.             r->headers_out.content_encoding = h;
  302.         }

  303.     length:

  304.         start = p;
  305.         p = line.data + line.len;

  306.         u->headers_in.content_length_n = ngx_atoof(start, p - start);
  307.         if (u->headers_in.content_length_n == NGX_ERROR) {
  308.             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  309.                           "memcached sent invalid length in response \"%V\" "
  310.                           "for key \"%V\"",
  311.                           &line, &ctx->key);
  312.             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  313.         }

  314.         u->headers_in.status_n = 200;
  315.         u->state->status = 200;
  316.         u->buffer.pos = p + sizeof(CRLF) - 1;

  317.         return NGX_OK;
  318.     }

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

  322.         u->headers_in.content_length_n = 0;
  323.         u->headers_in.status_n = 404;
  324.         u->state->status = 404;
  325.         u->buffer.pos = p + sizeof("END" CRLF) - 1;
  326.         u->keepalive = 1;

  327.         return NGX_OK;
  328.     }

  329. no_valid:

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

  332.     return NGX_HTTP_UPSTREAM_INVALID_HEADER;
  333. }


  334. static ngx_int_t
  335. ngx_http_memcached_filter_init(void *data)
  336. {
  337.     ngx_http_memcached_ctx_t  *ctx = data;

  338.     ngx_http_upstream_t  *u;

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

  340.     if (u->headers_in.status_n != 404) {
  341.         u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
  342.         ctx->rest = NGX_HTTP_MEMCACHED_END;

  343.     } else {
  344.         u->length = 0;
  345.     }

  346.     return NGX_OK;
  347. }


  348. static ngx_int_t
  349. ngx_http_memcached_filter(void *data, ssize_t bytes)
  350. {
  351.     ngx_http_memcached_ctx_t  *ctx = data;

  352.     u_char               *last;
  353.     ngx_buf_t            *b;
  354.     ngx_chain_t          *cl, **ll;
  355.     ngx_http_upstream_t  *u;

  356.     u = ctx->request->upstream;
  357.     b = &u->buffer;

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

  359.         if (bytes > u->length
  360.             || ngx_strncmp(b->last,
  361.                    ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
  362.                    bytes)
  363.                != 0)
  364.         {
  365.             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
  366.                           "memcached sent invalid trailer");

  367.             u->length = 0;
  368.             ctx->rest = 0;

  369.             return NGX_OK;
  370.         }

  371.         u->length -= bytes;
  372.         ctx->rest -= bytes;

  373.         if (u->length == 0) {
  374.             u->keepalive = 1;
  375.         }

  376.         return NGX_OK;
  377.     }

  378.     for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
  379.         ll = &cl->next;
  380.     }

  381.     cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
  382.     if (cl == NULL) {
  383.         return NGX_ERROR;
  384.     }

  385.     cl->buf->flush = 1;
  386.     cl->buf->memory = 1;

  387.     *ll = cl;

  388.     last = b->last;
  389.     cl->buf->pos = last;
  390.     b->last += bytes;
  391.     cl->buf->last = b->last;
  392.     cl->buf->tag = u->output.tag;

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

  396.     if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
  397.         u->length -= bytes;
  398.         return NGX_OK;
  399.     }

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

  401.     if (bytes > u->length
  402.         || ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0)
  403.     {
  404.         ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
  405.                       "memcached sent invalid trailer");

  406.         b->last = last;
  407.         cl->buf->last = last;
  408.         u->length = 0;
  409.         ctx->rest = 0;

  410.         return NGX_OK;
  411.     }

  412.     ctx->rest -= b->last - last;
  413.     b->last = last;
  414.     cl->buf->last = last;
  415.     u->length = ctx->rest;

  416.     if (u->length == 0) {
  417.         u->keepalive = 1;
  418.     }

  419.     return NGX_OK;
  420. }


  421. static void
  422. ngx_http_memcached_abort_request(ngx_http_request_t *r)
  423. {
  424.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  425.                    "abort http memcached request");
  426.     return;
  427. }


  428. static void
  429. ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
  430. {
  431.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  432.                    "finalize http memcached request");
  433.     return;
  434. }


  435. static void *
  436. ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
  437. {
  438.     ngx_http_memcached_loc_conf_t  *conf;

  439.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
  440.     if (conf == NULL) {
  441.         return NULL;
  442.     }

  443.     /*
  444.      * set by ngx_pcalloc():
  445.      *
  446.      *     conf->upstream.bufs.num = 0;
  447.      *     conf->upstream.next_upstream = 0;
  448.      *     conf->upstream.temp_path = NULL;
  449.      */

  450.     conf->upstream.local = NGX_CONF_UNSET_PTR;
  451.     conf->upstream.socket_keepalive = NGX_CONF_UNSET;
  452.     conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
  453.     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
  454.     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
  455.     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
  456.     conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;

  457.     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

  458.     /* the hardcoded values */
  459.     conf->upstream.cyclic_temp_file = 0;
  460.     conf->upstream.buffering = 0;
  461.     conf->upstream.ignore_client_abort = 0;
  462.     conf->upstream.send_lowat = 0;
  463.     conf->upstream.bufs.num = 0;
  464.     conf->upstream.busy_buffers_size = 0;
  465.     conf->upstream.max_temp_file_size = 0;
  466.     conf->upstream.temp_file_write_size = 0;
  467.     conf->upstream.intercept_errors = 1;
  468.     conf->upstream.intercept_404 = 1;
  469.     conf->upstream.pass_request_headers = 0;
  470.     conf->upstream.pass_request_body = 0;
  471.     conf->upstream.force_ranges = 1;

  472.     conf->index = NGX_CONF_UNSET;
  473.     conf->gzip_flag = NGX_CONF_UNSET_UINT;

  474.     return conf;
  475. }


  476. static char *
  477. ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  478. {
  479.     ngx_http_memcached_loc_conf_t *prev = parent;
  480.     ngx_http_memcached_loc_conf_t *conf = child;

  481.     ngx_conf_merge_ptr_value(conf->upstream.local,
  482.                               prev->upstream.local, NULL);

  483.     ngx_conf_merge_value(conf->upstream.socket_keepalive,
  484.                               prev->upstream.socket_keepalive, 0);

  485.     ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
  486.                               prev->upstream.next_upstream_tries, 0);

  487.     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
  488.                               prev->upstream.connect_timeout, 60000);

  489.     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
  490.                               prev->upstream.send_timeout, 60000);

  491.     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
  492.                               prev->upstream.read_timeout, 60000);

  493.     ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
  494.                               prev->upstream.next_upstream_timeout, 0);

  495.     ngx_conf_merge_size_value(conf->upstream.buffer_size,
  496.                               prev->upstream.buffer_size,
  497.                               (size_t) ngx_pagesize);

  498.     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
  499.                               prev->upstream.next_upstream,
  500.                               (NGX_CONF_BITMASK_SET
  501.                                |NGX_HTTP_UPSTREAM_FT_ERROR
  502.                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

  503.     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
  504.         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
  505.                                        |NGX_HTTP_UPSTREAM_FT_OFF;
  506.     }

  507.     if (conf->upstream.upstream == NULL) {
  508.         conf->upstream.upstream = prev->upstream.upstream;
  509.     }

  510.     if (conf->index == NGX_CONF_UNSET) {
  511.         conf->index = prev->index;
  512.     }

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

  514.     return NGX_CONF_OK;
  515. }


  516. static char *
  517. ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  518. {
  519.     ngx_http_memcached_loc_conf_t *mlcf = conf;

  520.     ngx_str_t                 *value;
  521.     ngx_url_t                  u;
  522.     ngx_http_core_loc_conf_t  *clcf;

  523.     if (mlcf->upstream.upstream) {
  524.         return "is duplicate";
  525.     }

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

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

  528.     u.url = value[1];
  529.     u.no_resolve = 1;

  530.     mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
  531.     if (mlcf->upstream.upstream == NULL) {
  532.         return NGX_CONF_ERROR;
  533.     }

  534.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

  535.     clcf->handler = ngx_http_memcached_handler;

  536.     if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {
  537.         clcf->auto_redirect = 1;
  538.     }

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

  540.     if (mlcf->index == NGX_ERROR) {
  541.         return NGX_CONF_ERROR;
  542.     }

  543.     return NGX_CONF_OK;
  544. }