Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_scgi_module.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  * Copyright (C) Manlio Perillo (manlio.perillo@gmail.com)
6  */
7 
8 
9 #include <ngx_config.h>
10 #include <ngx_core.h>
11 #include <ngx_http.h>
12 
13 
14 typedef struct {
16 
21 
24 
27 
28 #if (NGX_HTTP_CACHE)
29  ngx_http_complex_value_t cache_key;
30 #endif
32 
33 
34 static ngx_int_t ngx_http_scgi_eval(ngx_http_request_t *r,
36 static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r);
37 static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);
38 static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);
39 static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);
40 static void ngx_http_scgi_abort_request(ngx_http_request_t *r);
41 static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
42 
43 static void *ngx_http_scgi_create_loc_conf(ngx_conf_t *cf);
44 static char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent,
45  void *child);
46 static ngx_int_t ngx_http_scgi_merge_params(ngx_conf_t *cf,
48 
49 static char *ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
50 static char *ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
51  void *conf);
52 
53 #if (NGX_HTTP_CACHE)
54 static ngx_int_t ngx_http_scgi_create_key(ngx_http_request_t *r);
55 static char *ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
56  void *conf);
57 static char *ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
58  void *conf);
59 #endif
60 
61 
62 static ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = {
65  { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
71  { ngx_null_string, 0 }
72 };
73 
74 
76 
77 
78 static ngx_command_t ngx_http_scgi_commands[] = {
79 
80  { ngx_string("scgi_pass"),
82  ngx_http_scgi_pass,
84  0,
85  NULL },
86 
87  { ngx_string("scgi_store"),
89  ngx_http_scgi_store,
91  0,
92  NULL },
93 
94  { ngx_string("scgi_store_access"),
98  offsetof(ngx_http_scgi_loc_conf_t, upstream.store_access),
99  NULL },
100 
101  { ngx_string("scgi_buffering"),
105  offsetof(ngx_http_scgi_loc_conf_t, upstream.buffering),
106  NULL },
107 
108  { ngx_string("scgi_ignore_client_abort"),
112  offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_client_abort),
113  NULL },
114 
115  { ngx_string("scgi_bind"),
119  offsetof(ngx_http_scgi_loc_conf_t, upstream.local),
120  NULL },
121 
122  { ngx_string("scgi_connect_timeout"),
126  offsetof(ngx_http_scgi_loc_conf_t, upstream.connect_timeout),
127  NULL },
128 
129  { ngx_string("scgi_send_timeout"),
133  offsetof(ngx_http_scgi_loc_conf_t, upstream.send_timeout),
134  NULL },
135 
136  { ngx_string("scgi_buffer_size"),
140  offsetof(ngx_http_scgi_loc_conf_t, upstream.buffer_size),
141  NULL },
142 
143  { ngx_string("scgi_pass_request_headers"),
147  offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_headers),
148  NULL },
149 
150  { ngx_string("scgi_pass_request_body"),
154  offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_body),
155  NULL },
156 
157  { ngx_string("scgi_intercept_errors"),
161  offsetof(ngx_http_scgi_loc_conf_t, upstream.intercept_errors),
162  NULL },
163 
164  { ngx_string("scgi_read_timeout"),
168  offsetof(ngx_http_scgi_loc_conf_t, upstream.read_timeout),
169  NULL },
170 
171  { ngx_string("scgi_buffers"),
175  offsetof(ngx_http_scgi_loc_conf_t, upstream.bufs),
176  NULL },
177 
178  { ngx_string("scgi_busy_buffers_size"),
182  offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf),
183  NULL },
184 
185 #if (NGX_HTTP_CACHE)
186 
187  { ngx_string("scgi_cache"),
189  ngx_http_scgi_cache,
191  0,
192  NULL },
193 
194  { ngx_string("scgi_cache_key"),
196  ngx_http_scgi_cache_key,
198  0,
199  NULL },
200 
201  { ngx_string("scgi_cache_path"),
204  0,
205  0,
206  &ngx_http_scgi_module },
207 
208  { ngx_string("scgi_cache_bypass"),
212  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_bypass),
213  NULL },
214 
215  { ngx_string("scgi_no_cache"),
219  offsetof(ngx_http_scgi_loc_conf_t, upstream.no_cache),
220  NULL },
221 
222  { ngx_string("scgi_cache_valid"),
226  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_valid),
227  NULL },
228 
229  { ngx_string("scgi_cache_min_uses"),
233  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_min_uses),
234  NULL },
235 
236  { ngx_string("scgi_cache_use_stale"),
240  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_use_stale),
241  &ngx_http_scgi_next_upstream_masks },
242 
243  { ngx_string("scgi_cache_methods"),
247  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_methods),
249 
250  { ngx_string("scgi_cache_lock"),
254  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock),
255  NULL },
256 
257  { ngx_string("scgi_cache_lock_timeout"),
261  offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout),
262  NULL },
263 
264 #endif
265 
266  { ngx_string("scgi_temp_path"),
270  offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_path),
271  NULL },
272 
273  { ngx_string("scgi_max_temp_file_size"),
277  offsetof(ngx_http_scgi_loc_conf_t, upstream.max_temp_file_size_conf),
278  NULL },
279 
280  { ngx_string("scgi_temp_file_write_size"),
284  offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_file_write_size_conf),
285  NULL },
286 
287  { ngx_string("scgi_next_upstream"),
291  offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream),
292  &ngx_http_scgi_next_upstream_masks },
293 
294  { ngx_string("scgi_param"),
298  offsetof(ngx_http_scgi_loc_conf_t, params_source),
299  NULL },
300 
301  { ngx_string("scgi_pass_header"),
305  offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_headers),
306  NULL },
307 
308  { ngx_string("scgi_hide_header"),
312  offsetof(ngx_http_scgi_loc_conf_t, upstream.hide_headers),
313  NULL },
314 
315  { ngx_string("scgi_ignore_headers"),
319  offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_headers),
321 
323 };
324 
325 
326 static ngx_http_module_t ngx_http_scgi_module_ctx = {
327  NULL, /* preconfiguration */
328  NULL, /* postconfiguration */
329 
330  NULL, /* create main configuration */
331  NULL, /* init main configuration */
332 
333  NULL, /* create server configuration */
334  NULL, /* merge server configuration */
335 
336  ngx_http_scgi_create_loc_conf, /* create location configuration */
337  ngx_http_scgi_merge_loc_conf /* merge location configuration */
338 };
339 
340 
341 ngx_module_t ngx_http_scgi_module = {
343  &ngx_http_scgi_module_ctx, /* module context */
344  ngx_http_scgi_commands, /* module directives */
345  NGX_HTTP_MODULE, /* module type */
346  NULL, /* init master */
347  NULL, /* init module */
348  NULL, /* init process */
349  NULL, /* init thread */
350  NULL, /* exit thread */
351  NULL, /* exit process */
352  NULL, /* exit master */
354 };
355 
356 
357 static ngx_str_t ngx_http_scgi_hide_headers[] = {
358  ngx_string("Status"),
359  ngx_string("X-Accel-Expires"),
360  ngx_string("X-Accel-Redirect"),
361  ngx_string("X-Accel-Limit-Rate"),
362  ngx_string("X-Accel-Buffering"),
363  ngx_string("X-Accel-Charset"),
365 };
366 
367 
368 #if (NGX_HTTP_CACHE)
369 
370 static ngx_keyval_t ngx_http_scgi_cache_headers[] = {
371  { ngx_string("HTTP_IF_MODIFIED_SINCE"), ngx_string("") },
372  { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
373  { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("") },
374  { ngx_string("HTTP_IF_MATCH"), ngx_string("") },
375  { ngx_string("HTTP_RANGE"), ngx_string("") },
376  { ngx_string("HTTP_IF_RANGE"), ngx_string("") },
378 };
379 
380 #endif
381 
382 
383 static ngx_path_init_t ngx_http_scgi_temp_path = {
384  ngx_string(NGX_HTTP_SCGI_TEMP_PATH), { 1, 2, 0 }
385 };
386 
387 
388 static ngx_int_t
389 ngx_http_scgi_handler(ngx_http_request_t *r)
390 {
391  ngx_int_t rc;
392  ngx_http_status_t *status;
395 
396  if (r->subrequest_in_memory) {
398  "ngx_http_scgi_module does not support "
399  "subrequests in memory");
401  }
402 
403  if (ngx_http_upstream_create(r) != NGX_OK) {
405  }
406 
407  status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));
408  if (status == NULL) {
410  }
411 
412  ngx_http_set_ctx(r, status, ngx_http_scgi_module);
413 
414  scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
415 
416  if (scf->scgi_lengths) {
417  if (ngx_http_scgi_eval(r, scf) != NGX_OK) {
419  }
420  }
421 
422  u = r->upstream;
423 
424  ngx_str_set(&u->schema, "scgi://");
425  u->output.tag = (ngx_buf_tag_t) &ngx_http_scgi_module;
426 
427  u->conf = &scf->upstream;
428 
429 #if (NGX_HTTP_CACHE)
430  u->create_key = ngx_http_scgi_create_key;
431 #endif
432  u->create_request = ngx_http_scgi_create_request;
433  u->reinit_request = ngx_http_scgi_reinit_request;
434  u->process_header = ngx_http_scgi_process_status_line;
435  u->abort_request = ngx_http_scgi_abort_request;
436  u->finalize_request = ngx_http_scgi_finalize_request;
437  r->state = 0;
438 
439  u->buffering = scf->upstream.buffering;
440 
441  u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
442  if (u->pipe == NULL) {
444  }
445 
447  u->pipe->input_ctx = r;
448 
450 
451  if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
452  return rc;
453  }
454 
455  return NGX_DONE;
456 }
457 
458 
459 static ngx_int_t
460 ngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf)
461 {
462  ngx_url_t url;
464 
465  ngx_memzero(&url, sizeof(ngx_url_t));
466 
467  if (ngx_http_script_run(r, &url.url, scf->scgi_lengths->elts, 0,
468  scf->scgi_values->elts)
469  == NULL)
470  {
471  return NGX_ERROR;
472  }
473 
474  url.no_resolve = 1;
475 
476  if (ngx_parse_url(r->pool, &url) != NGX_OK) {
477  if (url.err) {
479  "%s in upstream \"%V\"", url.err, &url.url);
480  }
481 
482  return NGX_ERROR;
483  }
484 
485  u = r->upstream;
486 
488  if (u->resolved == NULL) {
489  return NGX_ERROR;
490  }
491 
492  if (url.addrs && url.addrs[0].sockaddr) {
493  u->resolved->sockaddr = url.addrs[0].sockaddr;
494  u->resolved->socklen = url.addrs[0].socklen;
495  u->resolved->naddrs = 1;
496  u->resolved->host = url.addrs[0].name;
497 
498  } else {
499  u->resolved->host = url.host;
500  u->resolved->port = url.port;
501  u->resolved->no_port = url.no_port;
502  }
503 
504  return NGX_OK;
505 }
506 
507 
508 #if (NGX_HTTP_CACHE)
509 
510 static ngx_int_t
511 ngx_http_scgi_create_key(ngx_http_request_t *r)
512 {
513  ngx_str_t *key;
515 
516  key = ngx_array_push(&r->cache->keys);
517  if (key == NULL) {
518  return NGX_ERROR;
519  }
520 
521  scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
522 
523  if (ngx_http_complex_value(r, &scf->cache_key, key) != NGX_OK) {
524  return NGX_ERROR;
525  }
526 
527  return NGX_OK;
528 }
529 
530 #endif
531 
532 
533 static ngx_int_t
534 ngx_http_scgi_create_request(ngx_http_request_t *r)
535 {
536  off_t content_length_n;
537  u_char ch, *key, *val, *lowcase_key;
538  size_t len, key_len, val_len, allocated;
539  ngx_buf_t *b;
540  ngx_str_t content_length;
541  ngx_uint_t i, n, hash, skip_empty, header_params;
542  ngx_chain_t *cl, *body;
543  ngx_list_part_t *part;
544  ngx_table_elt_t *header, **ignored;
549  u_char buffer[NGX_OFF_T_LEN];
550 
551  content_length_n = 0;
552  body = r->upstream->request_bufs;
553 
554  while (body) {
555  content_length_n += ngx_buf_size(body->buf);
556  body = body->next;
557  }
558 
559  content_length.data = buffer;
560  content_length.len = ngx_sprintf(buffer, "%O", content_length_n) - buffer;
561 
562  len = sizeof("CONTENT_LENGTH") + content_length.len + 1;
563 
564  header_params = 0;
565  ignored = NULL;
566 
567  scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
568 
569  if (scf->params_len) {
571 
573  le.flushed = 1;
574 
575  le.ip = scf->params_len->elts;
576  le.request = r;
577 
578  while (*(uintptr_t *) le.ip) {
579 
580  lcode = *(ngx_http_script_len_code_pt *) le.ip;
581  key_len = lcode(&le);
582 
583  lcode = *(ngx_http_script_len_code_pt *) le.ip;
584  skip_empty = lcode(&le);
585 
586  for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
587  lcode = *(ngx_http_script_len_code_pt *) le.ip;
588  }
589  le.ip += sizeof(uintptr_t);
590 
591  if (skip_empty && val_len == 0) {
592  continue;
593  }
594 
595  len += key_len + val_len + 1;
596  }
597  }
598 
599  if (scf->upstream.pass_request_headers) {
600 
601  allocated = 0;
602  lowcase_key = NULL;
603 
604  if (scf->header_params) {
605  n = 0;
606  part = &r->headers_in.headers.part;
607 
608  while (part) {
609  n += part->nelts;
610  part = part->next;
611  }
612 
613  ignored = ngx_palloc(r->pool, n * sizeof(void *));
614  if (ignored == NULL) {
615  return NGX_ERROR;
616  }
617  }
618 
619  part = &r->headers_in.headers.part;
620  header = part->elts;
621 
622  for (i = 0; /* void */; i++) {
623 
624  if (i >= part->nelts) {
625  if (part->next == NULL) {
626  break;
627  }
628 
629  part = part->next;
630  header = part->elts;
631  i = 0;
632  }
633 
634  if (scf->header_params) {
635  if (allocated < header[i].key.len) {
636  allocated = header[i].key.len + 16;
637  lowcase_key = ngx_pnalloc(r->pool, allocated);
638  if (lowcase_key == NULL) {
639  return NGX_ERROR;
640  }
641  }
642 
643  hash = 0;
644 
645  for (n = 0; n < header[i].key.len; n++) {
646  ch = header[i].key.data[n];
647 
648  if (ch >= 'A' && ch <= 'Z') {
649  ch |= 0x20;
650 
651  } else if (ch == '-') {
652  ch = '_';
653  }
654 
655  hash = ngx_hash(hash, ch);
656  lowcase_key[n] = ch;
657  }
658 
659  if (ngx_hash_find(&scf->headers_hash, hash, lowcase_key, n)) {
660  ignored[header_params++] = &header[i];
661  continue;
662  }
663  }
664 
665  len += sizeof("HTTP_") - 1 + header[i].key.len + 1
666  + header[i].value.len + 1;
667  }
668  }
669 
670  /* netstring: "length:" + packet + "," */
671 
672  b = ngx_create_temp_buf(r->pool, NGX_SIZE_T_LEN + 1 + len + 1);
673  if (b == NULL) {
674  return NGX_ERROR;
675  }
676 
677  cl = ngx_alloc_chain_link(r->pool);
678  if (cl == NULL) {
679  return NGX_ERROR;
680  }
681 
682  cl->buf = b;
683 
684  b->last = ngx_sprintf(b->last, "%ui:CONTENT_LENGTH%Z%V%Z",
685  len, &content_length);
686 
687  if (scf->params_len) {
689 
690  e.ip = scf->params->elts;
691  e.pos = b->last;
692  e.request = r;
693  e.flushed = 1;
694 
695  le.ip = scf->params_len->elts;
696 
697  while (*(uintptr_t *) le.ip) {
698 
699  lcode = *(ngx_http_script_len_code_pt *) le.ip;
700  lcode(&le); /* key length */
701 
702  lcode = *(ngx_http_script_len_code_pt *) le.ip;
703  skip_empty = lcode(&le);
704 
705  for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
706  lcode = *(ngx_http_script_len_code_pt *) le.ip;
707  }
708  le.ip += sizeof(uintptr_t);
709 
710  if (skip_empty && val_len == 0) {
711  e.skip = 1;
712 
713  while (*(uintptr_t *) e.ip) {
714  code = *(ngx_http_script_code_pt *) e.ip;
715  code((ngx_http_script_engine_t *) &e);
716  }
717  e.ip += sizeof(uintptr_t);
718 
719  e.skip = 0;
720 
721  continue;
722  }
723 
724 #if (NGX_DEBUG)
725  key = e.pos;
726 #endif
727  code = *(ngx_http_script_code_pt *) e.ip;
728  code((ngx_http_script_engine_t *) & e);
729 
730 #if (NGX_DEBUG)
731  val = e.pos;
732 #endif
733  while (*(uintptr_t *) e.ip) {
734  code = *(ngx_http_script_code_pt *) e.ip;
735  code((ngx_http_script_engine_t *) &e);
736  }
737  *e.pos++ = '\0';
738  e.ip += sizeof(uintptr_t);
739 
741  "scgi param: \"%s: %s\"", key, val);
742  }
743 
744  b->last = e.pos;
745  }
746 
747  if (scf->upstream.pass_request_headers) {
748 
749  part = &r->headers_in.headers.part;
750  header = part->elts;
751 
752  for (i = 0; /* void */; i++) {
753 
754  if (i >= part->nelts) {
755  if (part->next == NULL) {
756  break;
757  }
758 
759  part = part->next;
760  header = part->elts;
761  i = 0;
762  }
763 
764  for (n = 0; n < header_params; n++) {
765  if (&header[i] == ignored[n]) {
766  goto next;
767  }
768  }
769 
770  key = b->last;
771  b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1);
772 
773  for (n = 0; n < header[i].key.len; n++) {
774  ch = header[i].key.data[n];
775 
776  if (ch >= 'a' && ch <= 'z') {
777  ch &= ~0x20;
778 
779  } else if (ch == '-') {
780  ch = '_';
781  }
782 
783  *b->last++ = ch;
784  }
785 
786  *b->last++ = (u_char) 0;
787 
788  val = b->last;
789  b->last = ngx_copy(val, header[i].value.data, header[i].value.len);
790  *b->last++ = (u_char) 0;
791 
793  "scgi param: \"%s: %s\"", key, val);
794 
795  next:
796 
797  continue;
798  }
799  }
800 
801  *b->last++ = (u_char) ',';
802 
803  if (scf->upstream.pass_request_body) {
804  body = r->upstream->request_bufs;
805  r->upstream->request_bufs = cl;
806 
807  while (body) {
808  b = ngx_alloc_buf(r->pool);
809  if (b == NULL) {
810  return NGX_ERROR;
811  }
812 
813  ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
814 
815  cl->next = ngx_alloc_chain_link(r->pool);
816  if (cl->next == NULL) {
817  return NGX_ERROR;
818  }
819 
820  cl = cl->next;
821  cl->buf = b;
822 
823  body = body->next;
824  }
825 
826  } else {
827  r->upstream->request_bufs = cl;
828  }
829 
830  cl->next = NULL;
831 
832  return NGX_OK;
833 }
834 
835 
836 static ngx_int_t
837 ngx_http_scgi_reinit_request(ngx_http_request_t *r)
838 {
839  ngx_http_status_t *status;
840 
841  status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
842 
843  if (status == NULL) {
844  return NGX_OK;
845  }
846 
847  status->code = 0;
848  status->count = 0;
849  status->start = NULL;
850  status->end = NULL;
851 
852  r->upstream->process_header = ngx_http_scgi_process_status_line;
853  r->state = 0;
854 
855  return NGX_OK;
856 }
857 
858 
859 static ngx_int_t
860 ngx_http_scgi_process_status_line(ngx_http_request_t *r)
861 {
862  size_t len;
863  ngx_int_t rc;
864  ngx_http_status_t *status;
866 
867  status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
868 
869  if (status == NULL) {
870  return NGX_ERROR;
871  }
872 
873  u = r->upstream;
874 
875  rc = ngx_http_parse_status_line(r, &u->buffer, status);
876 
877  if (rc == NGX_AGAIN) {
878  return rc;
879  }
880 
881  if (rc == NGX_ERROR) {
882  u->process_header = ngx_http_scgi_process_header;
883  return ngx_http_scgi_process_header(r);
884  }
885 
886  if (u->state) {
887  u->state->status = status->code;
888  }
889 
890  u->headers_in.status_n = status->code;
891 
892  len = status->end - status->start;
893  u->headers_in.status_line.len = len;
894 
896  if (u->headers_in.status_line.data == NULL) {
897  return NGX_ERROR;
898  }
899 
900  ngx_memcpy(u->headers_in.status_line.data, status->start, len);
901 
903  "http scgi status %ui \"%V\"",
905 
906  u->process_header = ngx_http_scgi_process_header;
907 
908  return ngx_http_scgi_process_header(r);
909 }
910 
911 
912 static ngx_int_t
913 ngx_http_scgi_process_header(ngx_http_request_t *r)
914 {
915  ngx_str_t *status_line;
916  ngx_int_t rc, status;
917  ngx_table_elt_t *h;
921 
923 
924  for ( ;; ) {
925 
926  rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
927 
928  if (rc == NGX_OK) {
929 
930  /* a header line has been parsed successfully */
931 
933  if (h == NULL) {
934  return NGX_ERROR;
935  }
936 
937  h->hash = r->header_hash;
938 
940  h->value.len = r->header_end - r->header_start;
941 
942  h->key.data = ngx_pnalloc(r->pool,
943  h->key.len + 1 + h->value.len + 1
944  + h->key.len);
945  if (h->key.data == NULL) {
946  return NGX_ERROR;
947  }
948 
949  h->value.data = h->key.data + h->key.len + 1;
950  h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
951 
953  h->key.data[h->key.len] = '\0';
955  h->value.data[h->value.len] = '\0';
956 
957  if (h->key.len == r->lowcase_index) {
959 
960  } else {
961  ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
962  }
963 
964  hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
965  h->lowcase_key, h->key.len);
966 
967  if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
968  return NGX_ERROR;
969  }
970 
972  "http scgi header: \"%V: %V\"", &h->key, &h->value);
973 
974  continue;
975  }
976 
977  if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
978 
979  /* a whole header has been parsed successfully */
980 
982  "http scgi header done");
983 
984  u = r->upstream;
985 
986  if (u->headers_in.status_n) {
987  goto done;
988  }
989 
990  if (u->headers_in.status) {
991  status_line = &u->headers_in.status->value;
992 
993  status = ngx_atoi(status_line->data, 3);
994  if (status == NGX_ERROR) {
996  "upstream sent invalid status \"%V\"",
997  status_line);
999  }
1000 
1001  u->headers_in.status_n = status;
1002  u->headers_in.status_line = *status_line;
1003 
1004  } else if (u->headers_in.location) {
1005  u->headers_in.status_n = 302;
1007  "302 Moved Temporarily");
1008 
1009  } else {
1010  u->headers_in.status_n = 200;
1011  ngx_str_set(&u->headers_in.status_line, "200 OK");
1012  }
1013 
1014  if (u->state) {
1015  u->state->status = u->headers_in.status_n;
1016  }
1017 
1018  done:
1019 
1021  && r->headers_in.upgrade)
1022  {
1023  u->upgrade = 1;
1024  }
1025 
1026  return NGX_OK;
1027  }
1028 
1029  if (rc == NGX_AGAIN) {
1030  return NGX_AGAIN;
1031  }
1032 
1033  /* there was error while a header line parsing */
1034 
1036  "upstream sent invalid header");
1037 
1039  }
1040 }
1041 
1042 
1043 static void
1044 ngx_http_scgi_abort_request(ngx_http_request_t *r)
1045 {
1047  "abort http scgi request");
1048 
1049  return;
1050 }
1051 
1052 
1053 static void
1054 ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
1055 {
1057  "finalize http scgi request");
1058 
1059  return;
1060 }
1061 
1062 
1063 static void *
1064 ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)
1065 {
1067 
1068  conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_loc_conf_t));
1069  if (conf == NULL) {
1070  return NULL;
1071  }
1072 
1073  conf->upstream.store = NGX_CONF_UNSET;
1077 
1079 
1083 
1086 
1090 
1093 
1094 #if (NGX_HTTP_CACHE)
1095  conf->upstream.cache = NGX_CONF_UNSET_PTR;
1096  conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
1097  conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
1098  conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
1099  conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
1100  conf->upstream.cache_lock = NGX_CONF_UNSET;
1101  conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
1102 #endif
1103 
1106 
1108 
1109  /* "scgi_cyclic_temp_file" is disabled */
1110  conf->upstream.cyclic_temp_file = 0;
1111 
1112  conf->upstream.change_buffering = 1;
1113 
1114  ngx_str_set(&conf->upstream.module, "scgi");
1115 
1116  return conf;
1117 }
1118 
1119 
1120 static char *
1121 ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1122 {
1123  ngx_http_scgi_loc_conf_t *prev = parent;
1124  ngx_http_scgi_loc_conf_t *conf = child;
1125 
1126  size_t size;
1127  ngx_hash_init_t hash;
1129 
1130  if (conf->upstream.store != 0) {
1132 
1133  if (conf->upstream.store_lengths == NULL) {
1136  }
1137  }
1138 
1140  prev->upstream.store_access, 0600);
1141 
1143  prev->upstream.buffering, 1);
1144 
1146  prev->upstream.ignore_client_abort, 0);
1147 
1149  prev->upstream.local, NULL);
1150 
1152  prev->upstream.connect_timeout, 60000);
1153 
1155  prev->upstream.send_timeout, 60000);
1156 
1158  prev->upstream.read_timeout, 60000);
1159 
1161  prev->upstream.send_lowat, 0);
1162 
1164  prev->upstream.buffer_size,
1165  (size_t) ngx_pagesize);
1166 
1167 
1169  8, ngx_pagesize);
1170 
1171  if (conf->upstream.bufs.num < 2) {
1173  "there must be at least 2 \"scgi_buffers\"");
1174  return NGX_CONF_ERROR;
1175  }
1176 
1177 
1178  size = conf->upstream.buffer_size;
1179  if (size < conf->upstream.bufs.size) {
1180  size = conf->upstream.bufs.size;
1181  }
1182 
1183 
1187 
1189  conf->upstream.busy_buffers_size = 2 * size;
1190  } else {
1191  conf->upstream.busy_buffers_size =
1193  }
1194 
1195  if (conf->upstream.busy_buffers_size < size) {
1197  "\"scgi_busy_buffers_size\" must be equal to or greater "
1198  "than the maximum of the value of \"scgi_buffer_size\" and "
1199  "one of the \"scgi_buffers\"");
1200 
1201  return NGX_CONF_ERROR;
1202  }
1203 
1204  if (conf->upstream.busy_buffers_size
1205  > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
1206  {
1208  "\"scgi_busy_buffers_size\" must be less than "
1209  "the size of all \"scgi_buffers\" minus one buffer");
1210 
1211  return NGX_CONF_ERROR;
1212  }
1213 
1214 
1218 
1220  conf->upstream.temp_file_write_size = 2 * size;
1221  } else {
1224  }
1225 
1226  if (conf->upstream.temp_file_write_size < size) {
1228  "\"scgi_temp_file_write_size\" must be equal to or greater than "
1229  "the maximum of the value of \"scgi_buffer_size\" and "
1230  "one of the \"scgi_buffers\"");
1231 
1232  return NGX_CONF_ERROR;
1233  }
1234 
1235 
1239 
1241  conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
1242  } else {
1245  }
1246 
1247  if (conf->upstream.max_temp_file_size != 0
1248  && conf->upstream.max_temp_file_size < size) {
1250  "\"scgi_max_temp_file_size\" must be equal to zero to disable "
1251  "temporary files usage or must be equal to or greater than "
1252  "the maximum of the value of \"scgi_buffer_size\" and "
1253  "one of the \"scgi_buffers\"");
1254 
1255  return NGX_CONF_ERROR;
1256  }
1257 
1258 
1260  prev->upstream.ignore_headers,
1262 
1263 
1265  prev->upstream.next_upstream,
1269 
1273  }
1274 
1276  prev->upstream.temp_path,
1277  &ngx_http_scgi_temp_path)
1278  != NGX_OK)
1279  {
1280  return NGX_CONF_ERROR;
1281  }
1282 
1283 #if (NGX_HTTP_CACHE)
1284 
1285  ngx_conf_merge_ptr_value(conf->upstream.cache,
1286  prev->upstream.cache, NULL);
1287 
1288  if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
1289  ngx_shm_zone_t *shm_zone;
1290 
1291  shm_zone = conf->upstream.cache;
1292 
1294  "\"scgi_cache\" zone \"%V\" is unknown",
1295  &shm_zone->shm.name);
1296 
1297  return NGX_CONF_ERROR;
1298  }
1299 
1300  ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
1301  prev->upstream.cache_min_uses, 1);
1302 
1303  ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
1304  prev->upstream.cache_use_stale,
1307 
1308  if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
1309  conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
1311  }
1312 
1313  if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
1314  conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
1315  }
1316 
1317  if (conf->upstream.cache_methods == 0) {
1318  conf->upstream.cache_methods = prev->upstream.cache_methods;
1319  }
1320 
1321  conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
1322 
1323  ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
1324  prev->upstream.cache_bypass, NULL);
1325 
1326  ngx_conf_merge_ptr_value(conf->upstream.no_cache,
1327  prev->upstream.no_cache, NULL);
1328 
1329  ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
1330  prev->upstream.cache_valid, NULL);
1331 
1332  if (conf->cache_key.value.data == NULL) {
1333  conf->cache_key = prev->cache_key;
1334  }
1335 
1336  ngx_conf_merge_value(conf->upstream.cache_lock,
1337  prev->upstream.cache_lock, 0);
1338 
1339  ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
1340  prev->upstream.cache_lock_timeout, 5000);
1341 
1342 #endif
1343 
1345  prev->upstream.pass_request_headers, 1);
1347  prev->upstream.pass_request_body, 1);
1348 
1350  prev->upstream.intercept_errors, 0);
1351 
1352  hash.max_size = 512;
1354  hash.name = "scgi_hide_headers_hash";
1355 
1357  &prev->upstream, ngx_http_scgi_hide_headers, &hash)
1358  != NGX_OK)
1359  {
1360  return NGX_CONF_ERROR;
1361  }
1362 
1363  if (conf->upstream.upstream == NULL) {
1364  conf->upstream.upstream = prev->upstream.upstream;
1365  }
1366 
1367  if (conf->scgi_lengths == NULL) {
1368  conf->scgi_lengths = prev->scgi_lengths;
1369  conf->scgi_values = prev->scgi_values;
1370  }
1371 
1372  if (conf->upstream.upstream || conf->scgi_lengths) {
1374  if (clcf->handler == NULL && clcf->lmt_excpt) {
1375  clcf->handler = ngx_http_scgi_handler;
1376  }
1377  }
1378 
1379  if (ngx_http_scgi_merge_params(cf, conf, prev) != NGX_OK) {
1380  return NGX_CONF_ERROR;
1381  }
1382 
1383  return NGX_CONF_OK;
1384 }
1385 
1386 
1387 static ngx_int_t
1388 ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf,
1390 {
1391  u_char *p;
1392  size_t size;
1393  uintptr_t *code;
1394  ngx_uint_t i, nsrc;
1395  ngx_array_t headers_names;
1396 #if (NGX_HTTP_CACHE)
1397  ngx_array_t params_merged;
1398 #endif
1399  ngx_hash_key_t *hk;
1400  ngx_hash_init_t hash;
1404 
1405  if (conf->params_source == NULL) {
1406  conf->params_source = prev->params_source;
1407 
1408  if (prev->headers_hash.buckets
1409 #if (NGX_HTTP_CACHE)
1410  && ((conf->upstream.cache == NULL)
1411  == (prev->upstream.cache == NULL))
1412 #endif
1413  )
1414  {
1415  conf->flushes = prev->flushes;
1416  conf->params_len = prev->params_len;
1417  conf->params = prev->params;
1418  conf->headers_hash = prev->headers_hash;
1419  conf->header_params = prev->header_params;
1420 
1421  return NGX_OK;
1422  }
1423  }
1424 
1425  if (conf->params_source == NULL
1426 #if (NGX_HTTP_CACHE)
1427  && (conf->upstream.cache == NULL)
1428 #endif
1429  )
1430  {
1431  conf->headers_hash.buckets = (void *) 1;
1432  return NGX_OK;
1433  }
1434 
1435  conf->params_len = ngx_array_create(cf->pool, 64, 1);
1436  if (conf->params_len == NULL) {
1437  return NGX_ERROR;
1438  }
1439 
1440  conf->params = ngx_array_create(cf->pool, 512, 1);
1441  if (conf->params == NULL) {
1442  return NGX_ERROR;
1443  }
1444 
1445  if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
1446  != NGX_OK)
1447  {
1448  return NGX_ERROR;
1449  }
1450 
1451  if (conf->params_source) {
1452  src = conf->params_source->elts;
1453  nsrc = conf->params_source->nelts;
1454 
1455  } else {
1456  src = NULL;
1457  nsrc = 0;
1458  }
1459 
1460 #if (NGX_HTTP_CACHE)
1461 
1462  if (conf->upstream.cache) {
1463  ngx_keyval_t *h;
1465 
1466  if (ngx_array_init(&params_merged, cf->temp_pool, 4,
1467  sizeof(ngx_http_upstream_param_t))
1468  != NGX_OK)
1469  {
1470  return NGX_ERROR;
1471  }
1472 
1473  for (i = 0; i < nsrc; i++) {
1474 
1475  s = ngx_array_push(&params_merged);
1476  if (s == NULL) {
1477  return NGX_ERROR;
1478  }
1479 
1480  *s = src[i];
1481  }
1482 
1483  h = ngx_http_scgi_cache_headers;
1484 
1485  while (h->key.len) {
1486 
1487  src = params_merged.elts;
1488  nsrc = params_merged.nelts;
1489 
1490  for (i = 0; i < nsrc; i++) {
1491  if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
1492  goto next;
1493  }
1494  }
1495 
1496  s = ngx_array_push(&params_merged);
1497  if (s == NULL) {
1498  return NGX_ERROR;
1499  }
1500 
1501  s->key = h->key;
1502  s->value = h->value;
1503  s->skip_empty = 0;
1504 
1505  next:
1506 
1507  h++;
1508  }
1509 
1510  src = params_merged.elts;
1511  nsrc = params_merged.nelts;
1512  }
1513 
1514 #endif
1515 
1516  for (i = 0; i < nsrc; i++) {
1517 
1518  if (src[i].key.len > sizeof("HTTP_") - 1
1519  && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
1520  {
1521  hk = ngx_array_push(&headers_names);
1522  if (hk == NULL) {
1523  return NGX_ERROR;
1524  }
1525 
1526  hk->key.len = src[i].key.len - 5;
1527  hk->key.data = src[i].key.data + 5;
1528  hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
1529  hk->value = (void *) 1;
1530 
1531  if (src[i].value.len == 0) {
1532  continue;
1533  }
1534  }
1535 
1536  copy = ngx_array_push_n(conf->params_len,
1537  sizeof(ngx_http_script_copy_code_t));
1538  if (copy == NULL) {
1539  return NGX_ERROR;
1540  }
1541 
1543  copy->len = src[i].key.len + 1;
1544 
1545  copy = ngx_array_push_n(conf->params_len,
1546  sizeof(ngx_http_script_copy_code_t));
1547  if (copy == NULL) {
1548  return NGX_ERROR;
1549  }
1550 
1552  copy->len = src[i].skip_empty;
1553 
1554 
1555  size = (sizeof(ngx_http_script_copy_code_t)
1556  + src[i].key.len + 1 + sizeof(uintptr_t) - 1)
1557  & ~(sizeof(uintptr_t) - 1);
1558 
1559  copy = ngx_array_push_n(conf->params, size);
1560  if (copy == NULL) {
1561  return NGX_ERROR;
1562  }
1563 
1565  copy->len = src[i].key.len + 1;
1566 
1567  p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
1568  (void) ngx_cpystrn(p, src[i].key.data, src[i].key.len + 1);
1569 
1570 
1571  ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1572 
1573  sc.cf = cf;
1574  sc.source = &src[i].value;
1575  sc.flushes = &conf->flushes;
1576  sc.lengths = &conf->params_len;
1577  sc.values = &conf->params;
1578 
1579  if (ngx_http_script_compile(&sc) != NGX_OK) {
1580  return NGX_ERROR;
1581  }
1582 
1583  code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
1584  if (code == NULL) {
1585  return NGX_ERROR;
1586  }
1587 
1588  *code = (uintptr_t) NULL;
1589 
1590 
1591  code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
1592  if (code == NULL) {
1593  return NGX_ERROR;
1594  }
1595 
1596  *code = (uintptr_t) NULL;
1597  }
1598 
1599  code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
1600  if (code == NULL) {
1601  return NGX_ERROR;
1602  }
1603 
1604  *code = (uintptr_t) NULL;
1605 
1606  code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
1607  if (code == NULL) {
1608  return NGX_ERROR;
1609  }
1610 
1611  *code = (uintptr_t) NULL;
1612 
1613  conf->header_params = headers_names.nelts;
1614 
1615  hash.hash = &conf->headers_hash;
1616  hash.key = ngx_hash_key_lc;
1617  hash.max_size = 512;
1618  hash.bucket_size = 64;
1619  hash.name = "scgi_params_hash";
1620  hash.pool = cf->pool;
1621  hash.temp_pool = NULL;
1622 
1623  return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
1624 }
1625 
1626 
1627 static char *
1628 ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1629 {
1630  ngx_http_scgi_loc_conf_t *scf = conf;
1631 
1632  ngx_url_t u;
1633  ngx_str_t *value, *url;
1634  ngx_uint_t n;
1637 
1638  if (scf->upstream.upstream || scf->scgi_lengths) {
1639  return "is duplicate";
1640  }
1641 
1643  clcf->handler = ngx_http_scgi_handler;
1644 
1645  value = cf->args->elts;
1646 
1647  url = &value[1];
1648 
1650 
1651  if (n) {
1652 
1653  ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1654 
1655  sc.cf = cf;
1656  sc.source = url;
1657  sc.lengths = &scf->scgi_lengths;
1658  sc.values = &scf->scgi_values;
1659  sc.variables = n;
1660  sc.complete_lengths = 1;
1661  sc.complete_values = 1;
1662 
1663  if (ngx_http_script_compile(&sc) != NGX_OK) {
1664  return NGX_CONF_ERROR;
1665  }
1666 
1667  return NGX_CONF_OK;
1668  }
1669 
1670  ngx_memzero(&u, sizeof(ngx_url_t));
1671 
1672  u.url = value[1];
1673  u.no_resolve = 1;
1674 
1675  scf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
1676  if (scf->upstream.upstream == NULL) {
1677  return NGX_CONF_ERROR;
1678  }
1679 
1680  if (clcf->name.data[clcf->name.len - 1] == '/') {
1681  clcf->auto_redirect = 1;
1682  }
1683 
1684  return NGX_CONF_OK;
1685 }
1686 
1687 
1688 static char *
1689 ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1690 {
1691  ngx_http_scgi_loc_conf_t *scf = conf;
1692 
1693  ngx_str_t *value;
1695 
1696  if (scf->upstream.store != NGX_CONF_UNSET || scf->upstream.store_lengths) {
1697  return "is duplicate";
1698  }
1699 
1700  value = cf->args->elts;
1701 
1702  if (ngx_strcmp(value[1].data, "off") == 0) {
1703  scf->upstream.store = 0;
1704  return NGX_CONF_OK;
1705  }
1706 
1707 #if (NGX_HTTP_CACHE)
1708 
1709  if (scf->upstream.cache != NGX_CONF_UNSET_PTR
1710  && scf->upstream.cache != NULL)
1711  {
1712  return "is incompatible with \"scgi_cache\"";
1713  }
1714 
1715 #endif
1716 
1717  if (ngx_strcmp(value[1].data, "on") == 0) {
1718  scf->upstream.store = 1;
1719  return NGX_CONF_OK;
1720  }
1721 
1722  /* include the terminating '\0' into script */
1723  value[1].len++;
1724 
1725  ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1726 
1727  sc.cf = cf;
1728  sc.source = &value[1];
1729  sc.lengths = &scf->upstream.store_lengths;
1730  sc.values = &scf->upstream.store_values;
1731  sc.variables = ngx_http_script_variables_count(&value[1]);;
1732  sc.complete_lengths = 1;
1733  sc.complete_values = 1;
1734 
1735  if (ngx_http_script_compile(&sc) != NGX_OK) {
1736  return NGX_CONF_ERROR;
1737  }
1738 
1739  return NGX_CONF_OK;
1740 }
1741 
1742 
1743 #if (NGX_HTTP_CACHE)
1744 
1745 static char *
1746 ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1747 {
1748  ngx_http_scgi_loc_conf_t *scf = conf;
1749 
1750  ngx_str_t *value;
1751 
1752  value = cf->args->elts;
1753 
1754  if (scf->upstream.cache != NGX_CONF_UNSET_PTR) {
1755  return "is duplicate";
1756  }
1757 
1758  if (ngx_strcmp(value[1].data, "off") == 0) {
1759  scf->upstream.cache = NULL;
1760  return NGX_CONF_OK;
1761  }
1762 
1763  if (scf->upstream.store > 0 || scf->upstream.store_lengths) {
1764  return "is incompatible with \"scgi_store\"";
1765  }
1766 
1767  scf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
1768  &ngx_http_scgi_module);
1769  if (scf->upstream.cache == NULL) {
1770  return NGX_CONF_ERROR;
1771  }
1772 
1773  return NGX_CONF_OK;
1774 }
1775 
1776 
1777 static char *
1778 ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1779 {
1780  ngx_http_scgi_loc_conf_t *scf = conf;
1781 
1782  ngx_str_t *value;
1784 
1785  value = cf->args->elts;
1786 
1787  if (scf->cache_key.value.data) {
1788  return "is duplicate";
1789  }
1790 
1792 
1793  ccv.cf = cf;
1794  ccv.value = &value[1];
1795  ccv.complex_value = &scf->cache_key;
1796 
1797  if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1798  return NGX_CONF_ERROR;
1799  }
1800 
1801  return NGX_CONF_OK;
1802 }
1803 
1804 #endif