source: nginx/src/http/modules/ngx_http_memcached_module.c

tip
Last change on this file was 5765:d80543940f9a, checked in by Tatsuhiko Kubo <cubicdaiya@…>, 2 weeks ago

Style: use ngx_str_set().

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