nginx 0.1.38
[nginx-catap.git] / src / http / ngx_http_script.c
blob716a6f054041092dbfdc5a9dd49dacf821b0376d
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
12 #define ngx_http_script_exit (u_char *) &ngx_http_script_exit_code
14 static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
17 ngx_uint_t
18 ngx_http_script_variables_count(ngx_str_t *value)
20 ngx_uint_t i, n;
22 for (n = 0, i = 0; i < value->len; i++) {
23 if (value->data[i] == '$') {
24 n++;
28 return n;
32 ngx_int_t
33 ngx_http_script_compile(ngx_http_script_compile_t *sc)
35 u_char ch;
36 size_t size;
37 ngx_int_t index;
38 ngx_str_t name;
39 uintptr_t *code;
40 ngx_uint_t i, n, bracket;
41 ngx_http_script_var_code_t *var_code;
42 ngx_http_script_copy_code_t *copy;
43 ngx_http_script_copy_capture_code_t *copy_capture;
45 if (*sc->lengths == NULL) {
46 n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
47 + sizeof(ngx_http_script_var_code_t))
48 + sizeof(uintptr_t);
50 *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
51 if (*sc->lengths == NULL) {
52 return NGX_ERROR;
57 if (*sc->values == NULL) {
58 n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
59 + sizeof(ngx_http_script_var_code_t))
60 + sizeof(uintptr_t)
61 + sc->source->len
62 + sizeof(uintptr_t) - 1)
63 & ~(sizeof(uintptr_t) - 1);
65 *sc->values = ngx_array_create(sc->cf->pool, n, 1);
66 if (*sc->values == NULL) {
67 return NGX_ERROR;
71 sc->variables = 0;
73 for (i = 0; i < sc->source->len; /* void */ ) {
75 name.len = 0;
77 if (sc->source->data[i] == '$') {
79 if (++i == sc->source->len) {
80 goto invalid_variable;
83 if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
85 copy_capture = ngx_http_script_add_code(*sc->lengths,
86 sizeof(ngx_http_script_copy_capture_code_t),
87 NULL);
88 if (copy_capture == NULL) {
89 return NGX_ERROR;
92 copy_capture->code = (ngx_http_script_code_pt)
93 ngx_http_script_copy_capture_len_code;
94 copy_capture->n = 2 * (sc->source->data[i] - '0');
96 copy_capture = ngx_http_script_add_code(*sc->values,
97 sizeof(ngx_http_script_copy_capture_code_t),
98 &sc->main);
99 if (copy_capture == NULL) {
100 return NGX_ERROR;
103 copy_capture->code = ngx_http_script_copy_capture_code;
104 copy_capture->n = sc->source->data[i] - '0';
106 if (sc->ncaptures < copy_capture->n) {
107 sc->ncaptures = copy_capture->n;
110 copy_capture->n *= 2;
112 i++;
114 continue;
117 if (sc->source->data[i] == '{') {
118 bracket = 1;
120 if (++i == sc->source->len) {
121 goto invalid_variable;
124 name.data = &sc->source->data[i];
126 } else {
127 bracket = 0;
128 name.data = &sc->source->data[i];
131 for ( /* void */ ; i < sc->source->len; i++, name.len++) {
132 ch = sc->source->data[i];
134 if (ch == '}' && bracket) {
135 i++;
136 bracket = 0;
137 break;
140 if ((ch >= 'A' && ch <= 'Z')
141 || (ch >= 'a' && ch <= 'z')
142 || (ch >= '0' && ch <= '9')
143 || ch == '_')
145 continue;
148 break;
151 if (bracket) {
152 ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
153 "the closing bracket in \"%V\" "
154 "variable is missing", &name);
155 return NGX_ERROR;
158 if (name.len == 0) {
159 goto invalid_variable;
162 sc->variables++;
164 index = ngx_http_get_variable_index(sc->cf, &name);
166 if (index == NGX_ERROR) {
167 return NGX_ERROR;
170 var_code = ngx_http_script_add_code(*sc->lengths,
171 sizeof(ngx_http_script_var_code_t),
172 NULL);
173 if (var_code == NULL) {
174 return NGX_ERROR;
177 var_code->code = (ngx_http_script_code_pt)
178 ngx_http_script_copy_var_len_code;
179 var_code->index = (uintptr_t) index;
182 var_code = ngx_http_script_add_code(*sc->values,
183 sizeof(ngx_http_script_var_code_t),
184 &sc->main);
185 if (var_code == NULL) {
186 return NGX_ERROR;
189 var_code->code = ngx_http_script_copy_var_code;
190 var_code->index = (uintptr_t) index;
192 continue;
195 if (sc->source->data[i] == '?' && sc->compile_args) {
196 sc->args = 1;
197 sc->compile_args = 0;
199 code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
200 &sc->main);
201 if (code == NULL) {
202 return NGX_ERROR;
205 *code = (uintptr_t) ngx_http_script_start_args_code;
207 i++;
209 continue;
212 name.data = &sc->source->data[i];
214 while (i < sc->source->len
215 && sc->source->data[i] != '$'
216 && !(sc->source->data[i] == '?' && sc->compile_args))
218 i++;
219 name.len++;
222 sc->size += name.len;
224 copy = ngx_http_script_add_code(*sc->lengths,
225 sizeof(ngx_http_script_copy_code_t),
226 NULL);
227 if (copy == NULL) {
228 return NGX_ERROR;
231 copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
232 copy->len = name.len;
234 size = (sizeof(ngx_http_script_copy_code_t) + name.len
235 + sizeof(uintptr_t) - 1)
236 & ~(sizeof(uintptr_t) - 1);
238 copy = ngx_http_script_add_code(*sc->values, size, &sc->main);
239 if (copy == NULL) {
240 return NGX_ERROR;
243 copy->code = ngx_http_script_copy_code;
244 copy->len = name.len;
246 ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t),
247 name.data, name.len);
250 if (sc->complete_lengths) {
251 code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
252 if (code == NULL) {
253 return NGX_ERROR;
256 *code = (uintptr_t) NULL;
259 if (sc->complete_values) {
260 code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
261 &sc->main);
262 if (code == NULL) {
263 return NGX_ERROR;
266 *code = (uintptr_t) NULL;
269 return NGX_OK;
271 invalid_variable:
273 ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
275 return NGX_ERROR;
279 void *
280 ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
282 if (*codes == NULL) {
283 *codes = ngx_array_create(pool, 256, 1);
284 if (*codes == NULL) {
285 return NULL;
289 return ngx_array_push_n(*codes, size);
293 void *
294 ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)
296 u_char *elts, **p;
297 void *new;
299 elts = codes->elts;
301 new = ngx_array_push_n(codes, size);
302 if (new == NULL) {
303 return NGX_CONF_ERROR;
306 if (code) {
307 if (elts != codes->elts) {
308 p = code;
309 *p += (u_char *) codes->elts - elts;
313 return new;
317 size_t
318 ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
320 ngx_http_script_copy_code_t *code;
322 code = (ngx_http_script_copy_code_t *) e->ip;
324 e->ip += sizeof(ngx_http_script_copy_code_t);
326 return code->len;
330 void
331 ngx_http_script_copy_code(ngx_http_script_engine_t *e)
333 ngx_http_script_copy_code_t *code;
335 code = (ngx_http_script_copy_code_t *) e->ip;
337 if (!e->skip) {
338 e->pos = ngx_cpymem(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t),
339 code->len);
342 e->ip += sizeof(ngx_http_script_copy_code_t)
343 + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
345 if (e->log) {
346 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
347 "http script copy: \"%V\"", &e->buf);
352 size_t
353 ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
355 ngx_http_variable_value_t *value;
356 ngx_http_script_var_code_t *code;
358 code = (ngx_http_script_var_code_t *) e->ip;
360 e->ip += sizeof(ngx_http_script_var_code_t);
362 value = ngx_http_get_indexed_variable(e->request, code->index);
364 if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
365 return 0;
368 return value->text.len;
372 void
373 ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
375 ngx_http_variable_value_t *value;
376 ngx_http_script_var_code_t *code;
378 code = (ngx_http_script_var_code_t *) e->ip;
380 e->ip += sizeof(ngx_http_script_var_code_t);
382 if (!e->skip) {
383 value = ngx_http_get_indexed_variable(e->request, code->index);
385 if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
386 return;
389 e->pos = ngx_cpymem(e->pos, value->text.data, value->text.len);
391 if (e->log) {
392 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
393 "http script var: \"%V\"", &e->buf);
399 size_t
400 ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
402 ngx_http_script_copy_capture_code_t *code;
404 code = (ngx_http_script_copy_capture_code_t *) e->ip;
406 e->ip += sizeof(ngx_http_script_copy_capture_code_t);
408 if (code->n < e->ncaptures) {
409 if ((e->args || e->quote)
410 && (e->request->quoted_uri || e->request->plus_in_uri))
412 return e->captures[code->n + 1] - e->captures[code->n]
413 + ngx_escape_uri(NULL,
414 &e->line.data[e->captures[code->n]],
415 e->captures[code->n + 1] - e->captures[code->n],
416 NGX_ESCAPE_ARGS);
417 } else {
418 return e->captures[code->n + 1] - e->captures[code->n];
422 return 0;
426 void
427 ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
429 ngx_http_script_copy_capture_code_t *code;
431 code = (ngx_http_script_copy_capture_code_t *) e->ip;
433 e->ip += sizeof(ngx_http_script_copy_capture_code_t);
435 if (code->n < e->ncaptures) {
436 if ((e->args || e->quote)
437 && (e->request->quoted_uri || e->request->plus_in_uri))
439 e->pos = (u_char *) ngx_escape_uri(e->pos,
440 &e->line.data[e->captures[code->n]],
441 e->captures[code->n + 1] - e->captures[code->n],
442 NGX_ESCAPE_ARGS);
443 } else {
444 e->pos = ngx_cpymem(e->pos,
445 &e->line.data[e->captures[code->n]],
446 e->captures[code->n + 1] - e->captures[code->n]);
450 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
451 "http script capture: \"%V\"", &e->buf);
455 void
456 ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
458 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
459 "http script args");
461 e->args = e->pos;
462 e->ip += sizeof(uintptr_t);
467 #if (NGX_PCRE)
469 void
470 ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
472 size_t len;
473 ngx_int_t rc;
474 ngx_uint_t n;
475 ngx_http_request_t *r;
476 ngx_http_script_engine_t le;
477 ngx_http_script_len_code_pt lcode;
478 ngx_http_script_regex_code_t *code;
480 code = (ngx_http_script_regex_code_t *) e->ip;
482 r = e->request;
484 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
485 "http script regex: \"%V\"", &code->name);
487 if (code->uri) {
488 e->line = r->uri;
489 } else {
490 e->sp--;
491 e->line = e->sp->text;
494 rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures);
496 if (rc == NGX_REGEX_NO_MATCHED) {
497 if (e->log) {
498 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
499 "\"%V\" does not match \"%V\"",
500 &code->name, &e->line);
503 e->ncaptures = 0;
505 if (code->test) {
506 e->sp->value = 0;
507 e->sp->text.len = 0;
508 e->sp->text.data = (u_char *) "";
509 e->sp++;
511 e->ip += sizeof(ngx_http_script_regex_code_t);
512 return;
515 e->ip += code->next;
516 return;
519 if (rc < 0) {
520 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
521 ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"",
522 rc, &e->line, &code->name);
524 e->ip = ngx_http_script_exit;
525 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
526 return;
529 if (e->log) {
530 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
531 "\"%V\" matches \"%V\"", &code->name, &e->line);
534 e->ncaptures = code->ncaptures;
536 if (code->test) {
537 e->sp->value = 1;
538 e->sp->text.len = 1;
539 e->sp->text.data = (u_char *) "1";
540 e->sp++;
542 e->ip += sizeof(ngx_http_script_regex_code_t);
543 return;
546 if (code->status) {
547 e->status = code->status;
549 if (!code->redirect) {
550 e->ip = ngx_http_script_exit;
551 return;
555 if (code->uri) {
556 r->internal = 1;
557 r->valid_unparsed_uri = 0;
559 if (code->break_cycle) {
560 r->valid_location = 0;
561 r->uri_changed = 0;
563 } else {
564 r->uri_changed = 1;
568 if (code->lengths == NULL) {
569 e->buf.len = code->size;
571 if (code->uri) {
572 if (rc && (r->quoted_uri || r->plus_in_uri)) {
573 e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
574 NGX_ESCAPE_ARGS);
578 for (n = 1; n < (ngx_uint_t) rc; n++) {
579 e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n];
582 } else {
583 ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
585 le.ip = code->lengths->elts;
586 le.request = r;
587 le.captures = e->captures;
588 le.ncaptures = e->ncaptures;
590 len = 1; /* reserve 1 byte for possible "?" */
592 while (*(uintptr_t *) le.ip) {
593 lcode = *(ngx_http_script_len_code_pt *) le.ip;
594 len += lcode(&le);
597 e->buf.len = len;
600 if (code->add_args && r->args.len) {
601 e->buf.len += r->args.len + 1;
604 e->buf.data = ngx_palloc(r->pool, e->buf.len);
605 if (e->buf.data == NULL) {
606 e->ip = ngx_http_script_exit;
607 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
608 return;
611 e->quote = code->redirect;
613 e->pos = e->buf.data;
615 e->ip += sizeof(ngx_http_script_regex_code_t);
619 void
620 ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
622 ngx_http_request_t *r;
623 ngx_http_script_regex_end_code_t *code;
625 code = (ngx_http_script_regex_end_code_t *) e->ip;
627 r = e->request;
629 e->quote = 0;
631 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
632 "http script regex end");
634 if (code->redirect) {
636 if (code->add_args && r->args.len) {
637 *e->pos++ = (u_char) (code->args ? '&' : '?');
638 e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len);
641 e->buf.len = e->pos - e->buf.data;
643 if (e->log) {
644 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
645 "rewritten redirect: \"%V\"", &e->buf);
648 r->headers_out.location = ngx_list_push(&r->headers_out.headers);
649 if (r->headers_out.location == NULL) {
650 e->ip = ngx_http_script_exit;
651 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
652 return;
655 r->headers_out.location->hash = 1;
656 r->headers_out.location->key.len = sizeof("Location") - 1;
657 r->headers_out.location->key.data = (u_char *) "Location";
658 r->headers_out.location->value = e->buf;
660 e->ip += sizeof(ngx_http_script_regex_end_code_t);
661 return;
664 if (e->args) {
665 e->buf.len = e->args - e->buf.data;
667 if (code->add_args && r->args.len) {
668 *e->pos++ = '&';
669 e->pos = ngx_cpymem(e->pos, r->args.data, r->args.len);
672 r->args.len = e->pos - e->args;
673 r->args.data = e->args;
675 e->args = NULL;
677 } else {
678 e->buf.len = e->pos - e->buf.data;
681 if (e->log) {
682 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
683 "rewritten data: \"%V\", args: \"%V\"",
684 &e->buf, &r->args);
687 if (code->uri) {
688 r->uri = e->buf;
690 if (ngx_http_set_exten(r) != NGX_OK) {
691 e->ip = ngx_http_script_exit;
692 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
693 return;
697 e->ip += sizeof(ngx_http_script_regex_end_code_t);
700 #endif
703 void
704 ngx_http_script_return_code(ngx_http_script_engine_t *e)
706 ngx_http_script_return_code_t *code;
708 code = (ngx_http_script_return_code_t *) e->ip;
710 e->status = code->status;
712 e->ip += sizeof(ngx_http_script_return_code_t) - sizeof(uintptr_t);
716 void
717 ngx_http_script_break_code(ngx_http_script_engine_t *e)
719 e->request->uri_changed = 0;
721 e->ip = ngx_http_script_exit;
725 void
726 ngx_http_script_if_code(ngx_http_script_engine_t *e)
728 ngx_http_script_if_code_t *code;
730 code = (ngx_http_script_if_code_t *) e->ip;
732 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
733 "http script if");
735 e->sp--;
737 if (e->sp->value) {
738 if (code->loc_conf) {
739 e->request->loc_conf = code->loc_conf;
742 e->ip += sizeof(ngx_http_script_if_code_t);
743 return;
746 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
747 "http script if false");
749 e->ip += code->next;
753 void
754 ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
756 size_t len;
757 ngx_http_script_engine_t le;
758 ngx_http_script_len_code_pt lcode;
759 ngx_http_script_complex_value_code_t *code;
761 code = (ngx_http_script_complex_value_code_t *) e->ip;
763 e->ip += sizeof(ngx_http_script_complex_value_code_t);
765 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
766 "http script complex value");
768 ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
770 le.ip = code->lengths->elts;
771 le.request = e->request;
772 le.captures = e->captures;
773 le.ncaptures = e->ncaptures;
775 for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
776 lcode = *(ngx_http_script_len_code_pt *) le.ip;
779 e->buf.len = len;
780 e->buf.data = ngx_palloc(e->request->pool, len);
781 if (e->buf.data == NULL) {
782 e->ip = ngx_http_script_exit;
783 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
784 return;
787 e->pos = e->buf.data;
789 e->sp->value = 0;
790 e->sp->text = e->buf;
791 e->sp++;
795 void
796 ngx_http_script_value_code(ngx_http_script_engine_t *e)
798 ngx_http_script_value_code_t *code;
800 code = (ngx_http_script_value_code_t *) e->ip;
802 e->ip += sizeof(ngx_http_script_value_code_t);
804 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
805 "http script value");
807 e->sp->value = (ngx_uint_t) code->value;
808 e->sp->text.len = (size_t) code->text_len;
809 e->sp->text.data = (u_char *) code->text_data;
810 e->sp++;
814 void
815 ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
817 ngx_http_request_t *r;
818 ngx_http_variable_value_t *value;
819 ngx_http_core_main_conf_t *cmcf;
820 ngx_http_script_var_code_t *code;
822 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
823 "http script set var");
825 code = (ngx_http_script_var_code_t *) e->ip;
827 e->ip += sizeof(ngx_http_script_var_code_t);
829 r = e->request;
831 if (r->variables == NULL) {
832 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
834 r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
835 * sizeof(ngx_http_variable_value_t *));
836 if (r->variables == NULL) {
837 e->ip = ngx_http_script_exit;
838 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
839 return;
843 value = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
844 if (value == NULL) {
845 e->ip = ngx_http_script_exit;
846 e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
847 return;
850 e->sp--;
852 *value = *e->sp;
854 r->variables[code->index] = value;
858 void
859 ngx_http_script_var_code(ngx_http_script_engine_t *e)
861 ngx_http_variable_value_t *value;
862 ngx_http_script_var_code_t *code;
864 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
865 "http script var");
867 code = (ngx_http_script_var_code_t *) e->ip;
869 e->ip += sizeof(ngx_http_script_var_code_t);
871 value = ngx_http_get_indexed_variable(e->request, code->index);
873 if (value == NULL || value == NGX_HTTP_VAR_NOT_FOUND) {
874 e->sp->value = 0;
875 e->sp->text.len = 0;
876 e->sp->text.data = (u_char *) "";
877 e->sp++;
879 return;
882 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
883 "http script var: %ui, \"%V\"", value->value, &value->text);
885 *e->sp = *value;
886 e->sp++;
890 void
891 ngx_http_script_nop_code(ngx_http_script_engine_t *e)
893 e->ip += sizeof(uintptr_t);