1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 
13 typedef struct {
14     ngx_http_upstream_conf_t   upstream;
15     ngx_int_t                  index;
16     ngx_uint_t                 gzip_flag;
17 } ngx_http_memcached_loc_conf_t;
18 
19 
20 typedef struct {
21     size_t                     rest;
22     ngx_http_request_t        *request;
23     ngx_str_t                  key;
24 } ngx_http_memcached_ctx_t;
25 
26 
27 static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
28 static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
29 static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
30 static ngx_int_t ngx_http_memcached_filter_init(void *data);
31 static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
32 static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
33 static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
34     ngx_int_t rc);
35 
36 static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
37 static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
38     void *parent, void *child);
39 
40 static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
41     void *conf);
42 
43 
44 static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {
45     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
46     { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
47     { ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
48     { ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
49     { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
50     { ngx_null_string, 0 }
51 };
52 
53 
54 static ngx_command_t  ngx_http_memcached_commands[] = {
55 
56     { ngx_string("memcached_pass"),
57       NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
58       ngx_http_memcached_pass,
59       NGX_HTTP_LOC_CONF_OFFSET,
60       0,
61       NULL },
62 
63     { ngx_string("memcached_bind"),
64       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
65       ngx_http_upstream_bind_set_slot,
66       NGX_HTTP_LOC_CONF_OFFSET,
67       offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
68       NULL },
69 
70     { ngx_string("memcached_socket_keepalive"),
71       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
72       ngx_conf_set_flag_slot,
73       NGX_HTTP_LOC_CONF_OFFSET,
74       offsetof(ngx_http_memcached_loc_conf_t, upstream.socket_keepalive),
75       NULL },
76 
77     { ngx_string("memcached_connect_timeout"),
78       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
79       ngx_conf_set_msec_slot,
80       NGX_HTTP_LOC_CONF_OFFSET,
81       offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
82       NULL },
83 
84     { ngx_string("memcached_send_timeout"),
85       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
86       ngx_conf_set_msec_slot,
87       NGX_HTTP_LOC_CONF_OFFSET,
88       offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
89       NULL },
90 
91     { ngx_string("memcached_buffer_size"),
92       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
93       ngx_conf_set_size_slot,
94       NGX_HTTP_LOC_CONF_OFFSET,
95       offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
96       NULL },
97 
98     { ngx_string("memcached_read_timeout"),
99       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
100       ngx_conf_set_msec_slot,
101       NGX_HTTP_LOC_CONF_OFFSET,
102       offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
103       NULL },
104 
105     { ngx_string("memcached_next_upstream"),
106       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
107       ngx_conf_set_bitmask_slot,
108       NGX_HTTP_LOC_CONF_OFFSET,
109       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
110       &ngx_http_memcached_next_upstream_masks },
111 
112     { ngx_string("memcached_next_upstream_tries"),
113       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
114       ngx_conf_set_num_slot,
115       NGX_HTTP_LOC_CONF_OFFSET,
116       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_tries),
117       NULL },
118 
119     { ngx_string("memcached_next_upstream_timeout"),
120       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
121       ngx_conf_set_msec_slot,
122       NGX_HTTP_LOC_CONF_OFFSET,
123       offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),
124       NULL },
125 
126     { ngx_string("memcached_gzip_flag"),
127       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
128       ngx_conf_set_num_slot,
129       NGX_HTTP_LOC_CONF_OFFSET,
130       offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
131       NULL },
132 
133       ngx_null_command
134 };
135 
136 
137 static ngx_http_module_t  ngx_http_memcached_module_ctx = {
138     NULL,                                  /* preconfiguration */
139     NULL,                                  /* postconfiguration */
140 
141     NULL,                                  /* create main configuration */
142     NULL,                                  /* init main configuration */
143 
144     NULL,                                  /* create server configuration */
145     NULL,                                  /* merge server configuration */
146 
147     ngx_http_memcached_create_loc_conf,    /* create location configuration */
148     ngx_http_memcached_merge_loc_conf      /* merge location configuration */
149 };
150 
151 
152 ngx_module_t  ngx_http_memcached_module = {
153     NGX_MODULE_V1,
154     &ngx_http_memcached_module_ctx,        /* module context */
155     ngx_http_memcached_commands,           /* module directives */
156     NGX_HTTP_MODULE,                       /* module type */
157     NULL,                                  /* init master */
158     NULL,                                  /* init module */
159     NULL,                                  /* init process */
160     NULL,                                  /* init thread */
161     NULL,                                  /* exit thread */
162     NULL,                                  /* exit process */
163     NULL,                                  /* exit master */
164     NGX_MODULE_V1_PADDING
165 };
166 
167 
168 static ngx_str_t  ngx_http_memcached_key = ngx_string("memcached_key");
169 
170 
171 #define NGX_HTTP_MEMCACHED_END   (sizeof(ngx_http_memcached_end) - 1)
172 static u_char  ngx_http_memcached_end[] = CRLF "END" CRLF;
173 
174 
175 static ngx_int_t
ngx_http_memcached_handler(ngx_http_request_t * r)176 ngx_http_memcached_handler(ngx_http_request_t *r)
177 {
178     ngx_int_t                       rc;
179     ngx_http_upstream_t            *u;
180     ngx_http_memcached_ctx_t       *ctx;
181     ngx_http_memcached_loc_conf_t  *mlcf;
182 
183     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
184         return NGX_HTTP_NOT_ALLOWED;
185     }
186 
187     rc = ngx_http_discard_request_body(r);
188 
189     if (rc != NGX_OK) {
190         return rc;
191     }
192 
193     if (ngx_http_set_content_type(r) != NGX_OK) {
194         return NGX_HTTP_INTERNAL_SERVER_ERROR;
195     }
196 
197     if (ngx_http_upstream_create(r) != NGX_OK) {
198         return NGX_HTTP_INTERNAL_SERVER_ERROR;
199     }
200 
201     u = r->upstream;
202 
203     ngx_str_set(&u->schema, "memcached://");
204     u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
205 
206     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
207 
208     u->conf = &mlcf->upstream;
209 
210     u->create_request = ngx_http_memcached_create_request;
211     u->reinit_request = ngx_http_memcached_reinit_request;
212     u->process_header = ngx_http_memcached_process_header;
213     u->abort_request = ngx_http_memcached_abort_request;
214     u->finalize_request = ngx_http_memcached_finalize_request;
215 
216     ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
217     if (ctx == NULL) {
218         return NGX_HTTP_INTERNAL_SERVER_ERROR;
219     }
220 
221     ctx->request = r;
222 
223     ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
224 
225     u->input_filter_init = ngx_http_memcached_filter_init;
226     u->input_filter = ngx_http_memcached_filter;
227     u->input_filter_ctx = ctx;
228 
229     r->main->count++;
230 
231     ngx_http_upstream_init(r);
232 
233     return NGX_DONE;
234 }
235 
236 
237 static ngx_int_t
ngx_http_memcached_create_request(ngx_http_request_t * r)238 ngx_http_memcached_create_request(ngx_http_request_t *r)
239 {
240     size_t                          len;
241     uintptr_t                       escape;
242     ngx_buf_t                      *b;
243     ngx_chain_t                    *cl;
244     ngx_http_memcached_ctx_t       *ctx;
245     ngx_http_variable_value_t      *vv;
246     ngx_http_memcached_loc_conf_t  *mlcf;
247 
248     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
249 
250     vv = ngx_http_get_indexed_variable(r, mlcf->index);
251 
252     if (vv == NULL || vv->not_found || vv->len == 0) {
253         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
254                       "the \"$memcached_key\" variable is not set");
255         return NGX_ERROR;
256     }
257 
258     escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);
259 
260     len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;
261 
262     b = ngx_create_temp_buf(r->pool, len);
263     if (b == NULL) {
264         return NGX_ERROR;
265     }
266 
267     cl = ngx_alloc_chain_link(r->pool);
268     if (cl == NULL) {
269         return NGX_ERROR;
270     }
271 
272     cl->buf = b;
273     cl->next = NULL;
274 
275     r->upstream->request_bufs = cl;
276 
277     *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';
278 
279     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
280 
281     ctx->key.data = b->last;
282 
283     if (escape == 0) {
284         b->last = ngx_copy(b->last, vv->data, vv->len);
285 
286     } else {
287         b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
288                                             NGX_ESCAPE_MEMCACHED);
289     }
290 
291     ctx->key.len = b->last - ctx->key.data;
292 
293     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
294                    "http memcached request: \"%V\"", &ctx->key);
295 
296     *b->last++ = CR; *b->last++ = LF;
297 
298     return NGX_OK;
299 }
300 
301 
302 static ngx_int_t
ngx_http_memcached_reinit_request(ngx_http_request_t * r)303 ngx_http_memcached_reinit_request(ngx_http_request_t *r)
304 {
305     return NGX_OK;
306 }
307 
308 
309 static ngx_int_t
ngx_http_memcached_process_header(ngx_http_request_t * r)310 ngx_http_memcached_process_header(ngx_http_request_t *r)
311 {
312     u_char                         *p, *start;
313     ngx_str_t                       line;
314     ngx_uint_t                      flags;
315     ngx_table_elt_t                *h;
316     ngx_http_upstream_t            *u;
317     ngx_http_memcached_ctx_t       *ctx;
318     ngx_http_memcached_loc_conf_t  *mlcf;
319 
320     u = r->upstream;
321 
322     for (p = u->buffer.pos; p < u->buffer.last; p++) {
323         if (*p == LF) {
324             goto found;
325         }
326     }
327 
328     return NGX_AGAIN;
329 
330 found:
331 
332     line.data = u->buffer.pos;
333     line.len = p - u->buffer.pos;
334 
335     if (line.len == 0 || *(p - 1) != CR) {
336         goto no_valid;
337     }
338 
339     *p = '\0';
340     line.len--;
341 
342     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
343                    "memcached: \"%V\"", &line);
344 
345     p = u->buffer.pos;
346 
347     ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
348     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
349 
350     if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
351 
352         p += sizeof("VALUE ") - 1;
353 
354         if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
355             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
356                           "memcached sent invalid key in response \"%V\" "
357                           "for key \"%V\"",
358                           &line, &ctx->key);
359 
360             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
361         }
362 
363         p += ctx->key.len;
364 
365         if (*p++ != ' ') {
366             goto no_valid;
367         }
368 
369         /* flags */
370 
371         start = p;
372 
373         while (*p) {
374             if (*p++ == ' ') {
375                 if (mlcf->gzip_flag) {
376                     goto flags;
377                 } else {
378                     goto length;
379                 }
380             }
381         }
382 
383         goto no_valid;
384 
385     flags:
386 
387         flags = ngx_atoi(start, p - start - 1);
388 
389         if (flags == (ngx_uint_t) NGX_ERROR) {
390             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
391                           "memcached sent invalid flags in response \"%V\" "
392                           "for key \"%V\"",
393                           &line, &ctx->key);
394             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
395         }
396 
397         if (flags & mlcf->gzip_flag) {
398             h = ngx_list_push(&r->headers_out.headers);
399             if (h == NULL) {
400                 return NGX_ERROR;
401             }
402 
403             h->hash = 1;
404             ngx_str_set(&h->key, "Content-Encoding");
405             ngx_str_set(&h->value, "gzip");
406             r->headers_out.content_encoding = h;
407         }
408 
409     length:
410 
411         start = p;
412         p = line.data + line.len;
413 
414         u->headers_in.content_length_n = ngx_atoof(start, p - start);
415         if (u->headers_in.content_length_n == NGX_ERROR) {
416             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
417                           "memcached sent invalid length in response \"%V\" "
418                           "for key \"%V\"",
419                           &line, &ctx->key);
420             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
421         }
422 
423         u->headers_in.status_n = 200;
424         u->state->status = 200;
425         u->buffer.pos = p + sizeof(CRLF) - 1;
426 
427         return NGX_OK;
428     }
429 
430     if (ngx_strcmp(p, "END\x0d") == 0) {
431         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
432                       "key: \"%V\" was not found by memcached", &ctx->key);
433 
434         u->headers_in.content_length_n = 0;
435         u->headers_in.status_n = 404;
436         u->state->status = 404;
437         u->buffer.pos = p + sizeof("END" CRLF) - 1;
438         u->keepalive = 1;
439 
440         return NGX_OK;
441     }
442 
443 no_valid:
444 
445     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
446                   "memcached sent invalid response: \"%V\"", &line);
447 
448     return NGX_HTTP_UPSTREAM_INVALID_HEADER;
449 }
450 
451 
452 static ngx_int_t
ngx_http_memcached_filter_init(void * data)453 ngx_http_memcached_filter_init(void *data)
454 {
455     ngx_http_memcached_ctx_t  *ctx = data;
456 
457     ngx_http_upstream_t  *u;
458 
459     u = ctx->request->upstream;
460 
461     if (u->headers_in.status_n != 404) {
462         u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
463         ctx->rest = NGX_HTTP_MEMCACHED_END;
464 
465     } else {
466         u->length = 0;
467     }
468 
469     return NGX_OK;
470 }
471 
472 
473 static ngx_int_t
ngx_http_memcached_filter(void * data,ssize_t bytes)474 ngx_http_memcached_filter(void *data, ssize_t bytes)
475 {
476     ngx_http_memcached_ctx_t  *ctx = data;
477 
478     u_char               *last;
479     ngx_buf_t            *b;
480     ngx_chain_t          *cl, **ll;
481     ngx_http_upstream_t  *u;
482 
483     u = ctx->request->upstream;
484     b = &u->buffer;
485 
486     if (u->length == (ssize_t) ctx->rest) {
487 
488         if (ngx_strncmp(b->last,
489                    ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
490                    bytes)
491             != 0)
492         {
493             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
494                           "memcached sent invalid trailer");
495 
496             u->length = 0;
497             ctx->rest = 0;
498 
499             return NGX_OK;
500         }
501 
502         u->length -= bytes;
503         ctx->rest -= bytes;
504 
505         if (u->length == 0) {
506             u->keepalive = 1;
507         }
508 
509         return NGX_OK;
510     }
511 
512     for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
513         ll = &cl->next;
514     }
515 
516     cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
517     if (cl == NULL) {
518         return NGX_ERROR;
519     }
520 
521     cl->buf->flush = 1;
522     cl->buf->memory = 1;
523 
524     *ll = cl;
525 
526     last = b->last;
527     cl->buf->pos = last;
528     b->last += bytes;
529     cl->buf->last = b->last;
530     cl->buf->tag = u->output.tag;
531 
532     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
533                    "memcached filter bytes:%z size:%z length:%O rest:%z",
534                    bytes, b->last - b->pos, u->length, ctx->rest);
535 
536     if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
537         u->length -= bytes;
538         return NGX_OK;
539     }
540 
541     last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
542 
543     if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
544         ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
545                       "memcached sent invalid trailer");
546 
547         b->last = last;
548         cl->buf->last = last;
549         u->length = 0;
550         ctx->rest = 0;
551 
552         return NGX_OK;
553     }
554 
555     ctx->rest -= b->last - last;
556     b->last = last;
557     cl->buf->last = last;
558     u->length = ctx->rest;
559 
560     if (u->length == 0) {
561         u->keepalive = 1;
562     }
563 
564     return NGX_OK;
565 }
566 
567 
568 static void
ngx_http_memcached_abort_request(ngx_http_request_t * r)569 ngx_http_memcached_abort_request(ngx_http_request_t *r)
570 {
571     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
572                    "abort http memcached request");
573     return;
574 }
575 
576 
577 static void
ngx_http_memcached_finalize_request(ngx_http_request_t * r,ngx_int_t rc)578 ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
579 {
580     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
581                    "finalize http memcached request");
582     return;
583 }
584 
585 
586 static void *
ngx_http_memcached_create_loc_conf(ngx_conf_t * cf)587 ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
588 {
589     ngx_http_memcached_loc_conf_t  *conf;
590 
591     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
592     if (conf == NULL) {
593         return NULL;
594     }
595 
596     /*
597      * set by ngx_pcalloc():
598      *
599      *     conf->upstream.bufs.num = 0;
600      *     conf->upstream.next_upstream = 0;
601      *     conf->upstream.temp_path = NULL;
602      */
603 
604     conf->upstream.local = NGX_CONF_UNSET_PTR;
605     conf->upstream.socket_keepalive = NGX_CONF_UNSET;
606     conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
607     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
608     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
609     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
610     conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
611 
612     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
613 
614     /* the hardcoded values */
615     conf->upstream.cyclic_temp_file = 0;
616     conf->upstream.buffering = 0;
617     conf->upstream.ignore_client_abort = 0;
618     conf->upstream.send_lowat = 0;
619     conf->upstream.bufs.num = 0;
620     conf->upstream.busy_buffers_size = 0;
621     conf->upstream.max_temp_file_size = 0;
622     conf->upstream.temp_file_write_size = 0;
623     conf->upstream.intercept_errors = 1;
624     conf->upstream.intercept_404 = 1;
625     conf->upstream.pass_request_headers = 0;
626     conf->upstream.pass_request_body = 0;
627     conf->upstream.force_ranges = 1;
628 
629     conf->index = NGX_CONF_UNSET;
630     conf->gzip_flag = NGX_CONF_UNSET_UINT;
631 
632     return conf;
633 }
634 
635 
636 static char *
ngx_http_memcached_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)637 ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
638 {
639     ngx_http_memcached_loc_conf_t *prev = parent;
640     ngx_http_memcached_loc_conf_t *conf = child;
641 
642     ngx_conf_merge_ptr_value(conf->upstream.local,
643                               prev->upstream.local, NULL);
644 
645     ngx_conf_merge_value(conf->upstream.socket_keepalive,
646                               prev->upstream.socket_keepalive, 0);
647 
648     ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
649                               prev->upstream.next_upstream_tries, 0);
650 
651     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
652                               prev->upstream.connect_timeout, 60000);
653 
654     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
655                               prev->upstream.send_timeout, 60000);
656 
657     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
658                               prev->upstream.read_timeout, 60000);
659 
660     ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
661                               prev->upstream.next_upstream_timeout, 0);
662 
663     ngx_conf_merge_size_value(conf->upstream.buffer_size,
664                               prev->upstream.buffer_size,
665                               (size_t) ngx_pagesize);
666 
667     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
668                               prev->upstream.next_upstream,
669                               (NGX_CONF_BITMASK_SET
670                                |NGX_HTTP_UPSTREAM_FT_ERROR
671                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
672 
673     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
674         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
675                                        |NGX_HTTP_UPSTREAM_FT_OFF;
676     }
677 
678     if (conf->upstream.upstream == NULL) {
679         conf->upstream.upstream = prev->upstream.upstream;
680     }
681 
682     if (conf->index == NGX_CONF_UNSET) {
683         conf->index = prev->index;
684     }
685 
686     ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
687 
688     return NGX_CONF_OK;
689 }
690 
691 
692 static char *
ngx_http_memcached_pass(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)693 ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
694 {
695     ngx_http_memcached_loc_conf_t *mlcf = conf;
696 
697     ngx_str_t                 *value;
698     ngx_url_t                  u;
699     ngx_http_core_loc_conf_t  *clcf;
700 
701     if (mlcf->upstream.upstream) {
702         return "is duplicate";
703     }
704 
705     value = cf->args->elts;
706 
707     ngx_memzero(&u, sizeof(ngx_url_t));
708 
709     u.url = value[1];
710     u.no_resolve = 1;
711 
712     mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
713     if (mlcf->upstream.upstream == NULL) {
714         return NGX_CONF_ERROR;
715     }
716 
717     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
718 
719     clcf->handler = ngx_http_memcached_handler;
720 
721     if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {
722         clcf->auto_redirect = 1;
723     }
724 
725     mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
726 
727     if (mlcf->index == NGX_ERROR) {
728         return NGX_CONF_ERROR;
729     }
730 
731     return NGX_CONF_OK;
732 }
733