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 (bytes > u->length
489             || ngx_strncmp(b->last,
490                    ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
491                    bytes)
492                != 0)
493         {
494             ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
495                           "memcached sent invalid trailer");
496 
497             u->length = 0;
498             ctx->rest = 0;
499 
500             return NGX_OK;
501         }
502 
503         u->length -= bytes;
504         ctx->rest -= bytes;
505 
506         if (u->length == 0) {
507             u->keepalive = 1;
508         }
509 
510         return NGX_OK;
511     }
512 
513     for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
514         ll = &cl->next;
515     }
516 
517     cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
518     if (cl == NULL) {
519         return NGX_ERROR;
520     }
521 
522     cl->buf->flush = 1;
523     cl->buf->memory = 1;
524 
525     *ll = cl;
526 
527     last = b->last;
528     cl->buf->pos = last;
529     b->last += bytes;
530     cl->buf->last = b->last;
531     cl->buf->tag = u->output.tag;
532 
533     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
534                    "memcached filter bytes:%z size:%z length:%O rest:%z",
535                    bytes, b->last - b->pos, u->length, ctx->rest);
536 
537     if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
538         u->length -= bytes;
539         return NGX_OK;
540     }
541 
542     last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
543 
544     if (bytes > u->length
545         || ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0)
546     {
547         ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
548                       "memcached sent invalid trailer");
549 
550         b->last = last;
551         cl->buf->last = last;
552         u->length = 0;
553         ctx->rest = 0;
554 
555         return NGX_OK;
556     }
557 
558     ctx->rest -= b->last - last;
559     b->last = last;
560     cl->buf->last = last;
561     u->length = ctx->rest;
562 
563     if (u->length == 0) {
564         u->keepalive = 1;
565     }
566 
567     return NGX_OK;
568 }
569 
570 
571 static void
ngx_http_memcached_abort_request(ngx_http_request_t * r)572 ngx_http_memcached_abort_request(ngx_http_request_t *r)
573 {
574     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
575                    "abort http memcached request");
576     return;
577 }
578 
579 
580 static void
ngx_http_memcached_finalize_request(ngx_http_request_t * r,ngx_int_t rc)581 ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
582 {
583     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
584                    "finalize http memcached request");
585     return;
586 }
587 
588 
589 static void *
ngx_http_memcached_create_loc_conf(ngx_conf_t * cf)590 ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
591 {
592     ngx_http_memcached_loc_conf_t  *conf;
593 
594     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
595     if (conf == NULL) {
596         return NULL;
597     }
598 
599     /*
600      * set by ngx_pcalloc():
601      *
602      *     conf->upstream.bufs.num = 0;
603      *     conf->upstream.next_upstream = 0;
604      *     conf->upstream.temp_path = NULL;
605      */
606 
607     conf->upstream.local = NGX_CONF_UNSET_PTR;
608     conf->upstream.socket_keepalive = NGX_CONF_UNSET;
609     conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
610     conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
611     conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
612     conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
613     conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
614 
615     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
616 
617     /* the hardcoded values */
618     conf->upstream.cyclic_temp_file = 0;
619     conf->upstream.buffering = 0;
620     conf->upstream.ignore_client_abort = 0;
621     conf->upstream.send_lowat = 0;
622     conf->upstream.bufs.num = 0;
623     conf->upstream.busy_buffers_size = 0;
624     conf->upstream.max_temp_file_size = 0;
625     conf->upstream.temp_file_write_size = 0;
626     conf->upstream.intercept_errors = 1;
627     conf->upstream.intercept_404 = 1;
628     conf->upstream.pass_request_headers = 0;
629     conf->upstream.pass_request_body = 0;
630     conf->upstream.force_ranges = 1;
631 
632     conf->index = NGX_CONF_UNSET;
633     conf->gzip_flag = NGX_CONF_UNSET_UINT;
634 
635     return conf;
636 }
637 
638 
639 static char *
ngx_http_memcached_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)640 ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
641 {
642     ngx_http_memcached_loc_conf_t *prev = parent;
643     ngx_http_memcached_loc_conf_t *conf = child;
644 
645     ngx_conf_merge_ptr_value(conf->upstream.local,
646                               prev->upstream.local, NULL);
647 
648     ngx_conf_merge_value(conf->upstream.socket_keepalive,
649                               prev->upstream.socket_keepalive, 0);
650 
651     ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
652                               prev->upstream.next_upstream_tries, 0);
653 
654     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
655                               prev->upstream.connect_timeout, 60000);
656 
657     ngx_conf_merge_msec_value(conf->upstream.send_timeout,
658                               prev->upstream.send_timeout, 60000);
659 
660     ngx_conf_merge_msec_value(conf->upstream.read_timeout,
661                               prev->upstream.read_timeout, 60000);
662 
663     ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
664                               prev->upstream.next_upstream_timeout, 0);
665 
666     ngx_conf_merge_size_value(conf->upstream.buffer_size,
667                               prev->upstream.buffer_size,
668                               (size_t) ngx_pagesize);
669 
670     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
671                               prev->upstream.next_upstream,
672                               (NGX_CONF_BITMASK_SET
673                                |NGX_HTTP_UPSTREAM_FT_ERROR
674                                |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
675 
676     if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
677         conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
678                                        |NGX_HTTP_UPSTREAM_FT_OFF;
679     }
680 
681     if (conf->upstream.upstream == NULL) {
682         conf->upstream.upstream = prev->upstream.upstream;
683     }
684 
685     if (conf->index == NGX_CONF_UNSET) {
686         conf->index = prev->index;
687     }
688 
689     ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
690 
691     return NGX_CONF_OK;
692 }
693 
694 
695 static char *
ngx_http_memcached_pass(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)696 ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
697 {
698     ngx_http_memcached_loc_conf_t *mlcf = conf;
699 
700     ngx_str_t                 *value;
701     ngx_url_t                  u;
702     ngx_http_core_loc_conf_t  *clcf;
703 
704     if (mlcf->upstream.upstream) {
705         return "is duplicate";
706     }
707 
708     value = cf->args->elts;
709 
710     ngx_memzero(&u, sizeof(ngx_url_t));
711 
712     u.url = value[1];
713     u.no_resolve = 1;
714 
715     mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
716     if (mlcf->upstream.upstream == NULL) {
717         return NGX_CONF_ERROR;
718     }
719 
720     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
721 
722     clcf->handler = ngx_http_memcached_handler;
723 
724     if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {
725         clcf->auto_redirect = 1;
726     }
727 
728     mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
729 
730     if (mlcf->index == NGX_ERROR) {
731         return NGX_CONF_ERROR;
732     }
733 
734     return NGX_CONF_OK;
735 }
736