From 8073d5fe9f720a0564dbced1fdef187f5c19ffa3 Mon Sep 17 00:00:00 2001 From: Jan Kneschke Date: Mon, 8 Aug 2005 13:48:33 +0000 Subject: [PATCH] added nested conditionals (merged [298]) git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x@519 152afb58-edef-0310-8abb-c4023f1b3aa9 --- src/Makefile.am | 4 +- src/array.c | 12 +++ src/array.h | 21 ++-- src/base.h | 2 + src/configfile-glue.c | 190 +++++++++++++++++++++++++++++++-- src/{config.c => configfile.c} | 116 +++++++++++++------- src/configfile.h | 6 +- src/configparser.y | 105 +++++++++++++----- src/connections.c | 1 + src/data_config.c | 9 +- src/network.c | 2 +- tests/LightyTest.pm | 11 +- tests/Makefile.am | 2 + tests/condition.conf | 59 ++++++++++ tests/core-condition.t | 53 +++++++++ 15 files changed, 500 insertions(+), 93 deletions(-) rename src/{config.c => configfile.c} (93%) create mode 100644 tests/condition.conf create mode 100755 tests/core-condition.t diff --git a/src/Makefile.am b/src/Makefile.am index 75878a11..d8e58f5c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,7 +24,7 @@ mod_ssi_exprparser.c mod_ssi_exprparser.h: mod_ssi_exprparser.y $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c endif -config.c: configparser.h +configfile.c: configparser.h mod_ssi_expr.c: mod_ssi_exprparser.h common_src=buffer.c log.c \ @@ -47,7 +47,7 @@ src = server.c response.c connections.c network.c \ network_write.c network_linux_sendfile.c \ network_freebsd_sendfile.c network_writev.c \ network_solaris_sendfilev.c network_openssl.c \ - config.c request.c + configfile.c request.c spawn_fcgi_SOURCES=spawn-fcgi.c diff --git a/src/array.c b/src/array.c index a8ab00bc..0a1f081d 100644 --- a/src/array.c +++ b/src/array.c @@ -45,6 +45,18 @@ void array_reset(array *a) { a->used = 0; } +data_unset *array_pop(array *a) { + data_unset *du; + + assert(a->used != 0); + + a->used --; + du = a->data[a->used]; + a->data[a->used] = NULL; + + return du; +} + static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) { int ndx = -1; int i, pos = 0; diff --git a/src/array.h b/src/array.h index f78798e6..37f21812 100644 --- a/src/array.h +++ b/src/array.h @@ -62,12 +62,14 @@ typedef struct { data_array *data_array_init(void); typedef enum { CONFIG_COND_UNSET, CONFIG_COND_EQ, CONFIG_COND_MATCH, CONFIG_COND_NE, CONFIG_COND_NOMATCH } config_cond_t; +typedef enum { COND_RESULT_FALSE, COND_RESULT_TRUE, COND_RESULT_UNSET } cond_result_t; /* $HTTP["host"] == "incremental.home.kneschke.de" { ... } * comp_key cond string/regex */ -typedef struct { +typedef struct _data_config data_config; +struct _data_config { DATA_UNSET; array *value; @@ -75,14 +77,20 @@ typedef struct { buffer *comp_key; config_cond_t cond; + int context_ndx; /* more or less like an id */ + array *childs; + /* nested */ + data_config *parent; + /* for chaining only */ + data_config *prev; + data_config *next; - union { - buffer *string; + buffer *string; #ifdef HAVE_PCRE_H - pcre *regex; + pcre *regex; + pcre_extra *regex_study; #endif - } match; -} data_config; +}; data_config *data_config_init(void); @@ -115,6 +123,7 @@ array *array_init(void); void array_free(array *a); void array_reset(array *a); int array_insert_unique(array *a, data_unset *str); +data_unset *array_pop(array *a); int array_print(array *a, int depth); data_unset *array_get_unused_element(array *a, data_type_t t); data_unset *array_get_element(array *a, const char *key); diff --git a/src/base.h b/src/base.h index 197e49f7..4996c005 100644 --- a/src/base.h +++ b/src/base.h @@ -251,6 +251,7 @@ typedef struct { unsigned short log_request_header; unsigned short log_request_handling; unsigned short log_response_header; + unsigned short log_condition_handling; /* server wide */ @@ -360,6 +361,7 @@ typedef struct { void **plugin_ctx; /* plugin connection specific config */ specific_config conf; /* global connection specific config */ + cond_result_t *cond_results_cache; buffer *server_name; diff --git a/src/configfile-glue.c b/src/configfile-glue.c index a9ea39a0..ffad77e2 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -3,6 +3,7 @@ #include "buffer.h" #include "array.h" #include "log.h" +#include "plugin.h" /** * like all glue code this file contains functions which @@ -146,15 +147,149 @@ int config_insert_values_global(server *srv, array *ca, const config_values_t cv return config_insert_values_internal(srv, ca, cv); } -int config_check_cond(server *srv, connection *con, data_config *dc) { +static int config_check_cond_cached(server *srv, connection *con, data_config *dc); + +static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { buffer *l; server_socket *srv_sock = con->srv_socket; + /* check parent first */ + if (dc->parent) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->string); + } + if (!config_check_cond_cached(srv, con, dc->parent)) { + return COND_RESULT_FALSE; + } + } + + if (dc->prev) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->string); + } + /* make sure prev is checked first */ + config_check_cond_cached(srv, con, dc->prev); + /* one of prev set me to FALSE */ + if (con->cond_results_cache[dc->context_ndx] == COND_RESULT_FALSE) { + return COND_RESULT_FALSE; + } + } + + /* + * OPTIMIZE + * + * - replace all is_equal be simple == to an enum + * + */ + /* pass the rules */ l = srv->empty_string; if (buffer_is_equal_string(dc->comp_key, CONST_STR_LEN("HTTPhost"))) { l = con->uri.authority; +#if 0 + /* FIXME: get this working again */ + char *ck_colon = NULL, *val_colon = NULL; + + if (!buffer_is_empty(con->uri.authority)) { + + /* + * append server-port to the HTTP_POST if necessary + */ + + buffer_copy_string_buffer(srv->cond_check_buf, con->uri.authority); + + switch(dc->cond) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + ck_colon = strchr(dc->string->ptr, ':'); + val_colon = strchr(con->uri.authority->ptr, ':'); + + if (ck_colon && !val_colon) { + /* colon found */ + BUFFER_APPEND_STRING_CONST(srv->cond_check_buf, ":"); + buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); + } + break; + default: + break; + } + } + } else if (buffer_is_equal_string(dc->comp_key, CONST_STR_LEN("HTTPremoteip"))) { + char *nm_slash; + /* handle remoteip limitations + * + * "10.0.0.1" is provided for all comparisions + * + * only for == and != we support + * + * "10.0.0.1/24" + */ + + if ((dc->cond == CONFIG_COND_EQ || + dc->cond == CONFIG_COND_NE) && + (con->dst_addr.plain.sa_family == AF_INET) && + (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) { + int nm_bits; + long nm; + char *err; + struct in_addr val_inp; + + if (*(nm_slash+1) == '\0') { + log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); + + return COND_RESULT_FALSE; + } + + nm_bits = strtol(nm_slash + 1, &err, 10); + + if (*err) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, *err); + + return COND_RESULT_FALSE; + } + + /* take IP convert to the native */ + buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr); +#ifdef __WIN32 + if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); + + return COND_RESULT_FALSE; + } + +#else + if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); + + return COND_RESULT_FALSE; + } +#endif + + /* build netmask */ + nm = htonl(~((1 << (32 - nm_bits)) - 1)); + + if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; + } else { + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; + } + } else { + const char *s; +#ifdef HAVE_IPV6 + char b2[INET6_ADDRSTRLEN + 1]; + + s = inet_ntop(con->dst_addr.plain.sa_family, + con->dst_addr.plain.sa_family == AF_INET6 ? + (const void *) &(con->dst_addr.ipv6.sin6_addr) : + (const void *) &(con->dst_addr.ipv4.sin_addr), + b2, sizeof(b2)-1); +#else + s = inet_ntoa(con->dst_addr.ipv4.sin_addr); +#endif + buffer_copy_string(srv->cond_check_buf, s); + } +#endif } else if (buffer_is_equal_string(dc->comp_key, CONST_STR_LEN("HTTPurl"))) { l = con->uri.path; } else if (buffer_is_equal_string(dc->comp_key, CONST_STR_LEN("SERVERsocket"))) { @@ -176,16 +311,19 @@ int config_check_cond(server *srv, connection *con, data_config *dc) { l = ds->value; } } else { - return 0; + return COND_RESULT_FALSE; } + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, "(", l, ") compare to ", dc->string); + } switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: - if (buffer_is_equal(l, dc->match.string)) { - return (dc->cond == CONFIG_COND_EQ) ? 1 : 0; + if (buffer_is_equal(l, dc->string)) { + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { - return (dc->cond == CONFIG_COND_EQ) ? 0 : 1; + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; #ifdef HAVE_PCRE_H @@ -195,14 +333,13 @@ int config_check_cond(server *srv, connection *con, data_config *dc) { int ovec[N * 3]; int n; - n = pcre_exec(dc->match.regex, NULL, l->ptr, l->used - 1, 0, 0, ovec, N * 3); + n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, ovec, N * 3); if (n > 0) { - return (dc->cond == CONFIG_COND_MATCH) ? 1 : 0; + return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { - return (dc->cond == CONFIG_COND_MATCH) ? 0 : 1; + return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } - break; } #endif @@ -211,6 +348,39 @@ int config_check_cond(server *srv, connection *con, data_config *dc) { break; } - return 0; + return COND_RESULT_FALSE; } +static int config_check_cond_cached(server *srv, connection *con, data_config *dc) { + cond_result_t *cache = con->cond_results_cache; + + if (cache[dc->context_ndx] == COND_RESULT_UNSET) { + if (COND_RESULT_TRUE == (cache[dc->context_ndx] = config_check_cond_nocache(srv, con, dc))) { + if (dc->next) { + data_config *c; + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "setting remains of chaining to FALSE"); + } + for (c = dc->next; c; c = c->next) { + cache[c->context_ndx] = COND_RESULT_FALSE; + } + } + } + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(uncached) result:", cache[dc->context_ndx]); + } + } + else { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dsd", dc->context_ndx, "(cached) result:", cache[dc->context_ndx]); + } + } + return cache[dc->context_ndx]; +} + +int config_check_cond(server *srv, connection *con, data_config *dc) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ==="); + } + return config_check_cond_cached(srv, con, dc); +} diff --git a/src/config.c b/src/configfile.c similarity index 93% rename from src/config.c rename to src/configfile.c index 5fcb6ab4..099e517c 100644 --- a/src/config.c +++ b/src/configfile.c @@ -70,7 +70,6 @@ static int config_insert(server *srv) { { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 35 */ { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 36 */ { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ - { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 38 */ { "dir-listing.hide-dotfiles", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 39 */ @@ -81,6 +80,8 @@ static int config_insert(server *srv) { { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 43 */ { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 44 */ + { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 45 */ + { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.virtual-root", "load mod_simple_vhost and use simple-vhost.server-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, @@ -191,6 +192,8 @@ static int config_insert(server *srv) { cv[41].destination = s->dirlist_encoding; cv[43].destination = &(s->range_requests); + cv[45].destination = &(s->log_condition_handling); + srv->config_storage[i] = s; if (0 != (ret = config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv))) { @@ -222,6 +225,11 @@ static int config_insert(server *srv) { #define PATCH(x) con->conf.x = s->x int config_setup_connection(server *srv, connection *con) { specific_config *s = srv->config_storage[0]; + int i; + + for (i = srv->config_context->used - 1; i >= 0; i --) { + con->cond_results_cache[i] = COND_RESULT_UNSET; + } PATCH(allow_http11); PATCH(mimetypes); @@ -250,6 +258,7 @@ int config_setup_connection(server *srv, connection *con) { PATCH(log_request_header); PATCH(log_response_header); PATCH(log_request_handling); + PATCH(log_condition_handling); PATCH(log_file_not_found); PATCH(range_requests); @@ -325,6 +334,8 @@ int config_patch_connection(server *srv, connection *con, const char *stage, siz PATCH(log_request_header); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-response-header"))) { PATCH(log_response_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-condition-handling"))) { + PATCH(log_condition_handling); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-file-not-found"))) { PATCH(log_file_not_found); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) { @@ -356,6 +367,27 @@ typedef struct { int in_cond; } tokenizer_t; +static int config_skip_newline(tokenizer_t *t) { + int skipped = 1; + assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n'); + if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') { + skipped ++; + t->offset ++; + } + t->offset ++; + return skipped; +} + +static int config_skip_comment(tokenizer_t *t) { + int i; + assert(t->input[t->offset] == '#'); + for (i = 1; t->input[t->offset + i] && + (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r'); + i++); + t->offset += i; + return i; +} + static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) { int tid = 0; size_t i; @@ -447,39 +479,41 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * t->offset++; t->line_pos++; break; + case '\n': case '\r': if (t->in_brace == 0) { - if (t->input[t->offset + 1] == '\n') { - t->in_key = 1; - t->offset += 2; - - tid = TK_EOL; - t->line++; - t->line_pos = 1; - - buffer_copy_string(token, "(EOL)"); - } else { - log_error_write(srv, __FILE__, __LINE__, "sdsds", - "line:", t->line, "pos:", t->line_pos, - "CR without LF"); - return 0; + int done = 0; + while (!done) { + switch (t->input[t->offset]) { + case '\r': + case '\n': + config_skip_newline(t); + t->line_pos = 1; + t->line++; + break; + + case '#': + t->line_pos += config_skip_comment(t); + break; + + case '\t': + case ' ': + t->offset++; + t->line_pos++; + break; + + default: + done = 1; + } } - } else { - t->offset++; - t->line_pos++; - } - break; - case '\n': - if (t->in_brace == 0) { t->in_key = 1; - tid = TK_EOL; - buffer_copy_string(token, "(EOL)"); + } else { + config_skip_newline(t); + t->line_pos = 1; + t->line++; } - t->line++; - t->line_pos = 1; - t->offset++; break; case ',': if (t->in_brace > 0) { @@ -559,6 +593,12 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * buffer_copy_string(token, "$"); break; + case '|': + t->offset++; + tid = TK_OR; + buffer_copy_string(token, "|"); + break; + case '{': t->offset++; @@ -578,6 +618,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * buffer_copy_string(token, "}"); break; + case '[': t->offset++; @@ -596,11 +637,7 @@ static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer * break; case '#': - for (i = 1; t->input[t->offset + i] && - (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r'); - i++); - - t->offset += i; + t->line_pos += config_skip_comment(t); break; default: @@ -721,14 +758,16 @@ int config_read(server *srv, const char *fn) { t.in_cond = 0; context.ok = 1; - context.config = srv->config_context; + context.all_configs = srv->config_context; + context.configs_stack = array_init(); dc = data_config_init(); buffer_copy_string(dc->key, "global"); - array_insert_unique(srv->config_context, (data_unset *)dc); - - context.ctx_name = dc->key; - context.ctx_config = dc->value; + + assert(context.all_configs->used == 0); + dc->context_ndx = context.all_configs->used; + array_insert_unique(context.all_configs, (data_unset *)dc); + context.current = dc; /* default context */ srv->config = dc->value; @@ -760,6 +799,9 @@ int config_read(server *srv, const char *fn) { return -1; } + assert(context.configs_stack->used == 0); + array_free(context.configs_stack); + if (0 != config_insert(srv)) { return -1; } diff --git a/src/configfile.h b/src/configfile.h index 55a13d5e..5f87d181 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -6,9 +6,9 @@ typedef struct { int ok; - array *config; - buffer *ctx_name; - array *ctx_config; + array *all_configs; + array *configs_stack; /* to parse nested block */ + data_config *current; /* current started with { */ } config_t; void *configparserAlloc(void *(*mallocProc)(size_t)); diff --git a/src/configparser.y b/src/configparser.y index 306e5f4d..d88cc240 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -10,6 +10,22 @@ #include "configfile.h" #include "buffer.h" #include "array.h" + +static void configparser_push(config_t *ctx, data_config *dc, int isnew) { + if (isnew) { + dc->context_ndx = ctx->all_configs->used; + array_insert_unique(ctx->all_configs, (data_unset *)dc); + } + array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current); + ctx->current = dc; +} + +static data_config *configparser_pop(config_t *ctx) { + data_config *old = ctx->current; + ctx->current = (data_config *) array_pop(ctx->configs_stack); + return old; +} + } %parse_failure { @@ -20,23 +36,25 @@ input ::= metalines. metalines ::= metalines metaline. metalines ::= . metaline ::= varline. -metaline ::= condline. +metaline ::= condlines EOL. metaline ::= EOL. %type value {data_unset *} %type aelement {data_unset *} %type aelements {array *} %type array {array *} +%type condline {data_config *} +%type condlines {data_config *} %type cond {config_cond_t } %token_destructor { buffer_free($$); } varline ::= key(A) ASSIGN value(B). { buffer_copy_string_buffer(B->key, A); - if (NULL == array_get_element(ctx->ctx_config, B->key->ptr)) { - array_insert_unique(ctx->ctx_config, B); + if (NULL == array_get_element(ctx->current->value, B->key->ptr)) { + array_insert_unique(ctx->current->value, B); } else { - fprintf(stderr, "Duplicate config variable in conditional %s: %s\n", - ctx->ctx_name->ptr, B->key->ptr); + fprintf(stderr, "Duplicate config variable in conditional 1 %s: %s\n", + ctx->current->key->ptr, B->key->ptr); ctx->ok = 0; B->free(B); } @@ -103,13 +121,38 @@ aelement(A) ::= STRING(B) ARRAY_ASSIGN value(C). { A = C; C = NULL; } -condline ::= context LCURLY metalines RCURLY EOL. { - data_config *dc; + +eols ::= EOL. +eols ::= . + +condlines(A) ::= condlines(B) eols OR condline(C). { + assert(B->context_ndx < C->context_ndx); + C->prev = B; + B->next = C; + A = C; + B = NULL; + C = NULL; +} + +condlines(A) ::= condline(B). { + A = B; + B = NULL; +} + +condline(A) ::= context LCURLY metalines RCURLY. { + data_config *parent, *cur; - dc = (data_config *)array_get_element(ctx->config, "global"); - assert(dc); - ctx->ctx_name = dc->key; - ctx->ctx_config = dc->value; + cur = ctx->current; + configparser_pop(ctx); + parent = ctx->current; + + assert(cur && parent); + + if (0 != parent->context_ndx) { /* not global */ + assert(cur->context_ndx > parent->context_ndx); + cur->parent = parent; + } + A = cur; } context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D). { @@ -117,14 +160,15 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D). buffer *b; b = buffer_init(); - buffer_copy_string_buffer(b, B); + buffer_copy_string_buffer(b, ctx->current->key); + buffer_append_string(b, "/"); + buffer_append_string_buffer(b, B); buffer_append_string_buffer(b, C); buffer_append_string_buffer(b, D); buffer_append_long(b, E); - if (NULL != (dc = (data_config *)array_get_element(ctx->config, b->ptr))) { - ctx->ctx_name = dc->key; - ctx->ctx_config = dc->value; + if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) { + configparser_push(ctx, dc, 0); } else { dc = data_config_init(); @@ -136,30 +180,43 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET STRING(C) RBRACKET cond(E) STRING(D). switch(E) { case CONFIG_COND_NE: case CONFIG_COND_EQ: - dc->match.string = buffer_init_string(D->ptr); + dc->string = buffer_init_string(D->ptr); break; -#ifdef HAVE_PCRE_H case CONFIG_COND_NOMATCH: case CONFIG_COND_MATCH: { +#ifdef HAVE_PCRE_H const char *errptr; int erroff; - if (NULL == (dc->match.regex = + if (NULL == (dc->regex = pcre_compile(D->ptr, 0, &errptr, &erroff, NULL))) { - dc->match.string = buffer_init_string(errptr); + dc->string = buffer_init_string(errptr); dc->cond = CONFIG_COND_UNSET; + + ctx->ok = 0; + } else if (NULL == (dc->regex_study = pcre_study(dc->regex, 0, &errptr)) && + errptr != NULL) { + fprintf(stderr, "studying regex failed: %s -> %s\n", + D->ptr, errptr); + ctx->ok = 0; } +#else + fprintf(stderr, "regex conditionals are not allowed as pcre-support" \ + "is missing: $%s[%s]\n", + B->ptr, C->ptr); + ctx->ok = 0; +#endif break; } -#endif + default: + fprintf(stderr, "unknown condition for $%s[%s]\n", + B->ptr, C->ptr); + ctx->ok = 0; break; } - array_insert_unique(ctx->config, (data_unset *)dc); - - ctx->ctx_name = dc->key; - ctx->ctx_config = dc->value; + configparser_push(ctx, dc, 1); } buffer_free(b); buffer_free(B); diff --git a/src/connections.c b/src/connections.c index 80eeb98f..96083485 100644 --- a/src/connections.c +++ b/src/connections.c @@ -603,6 +603,7 @@ connection *connection_init(server *srv) { con->plugin_ctx = calloc(srv->plugins.used + 1, sizeof(void *)); + con->cond_results_cache = calloc(srv->config_context->used, sizeof(cond_result_t)); config_setup_connection(srv, con); return con; diff --git a/src/data_config.c b/src/data_config.c index f43a2e6b..0ed85686 100644 --- a/src/data_config.c +++ b/src/data_config.c @@ -12,14 +12,11 @@ static void data_config_free(data_unset *d) { array_free(ds->value); - switch(ds->cond) { - case CONFIG_COND_EQ: buffer_free(ds->match.string); break; + if (ds->string) buffer_free(ds->string); #ifdef HAVE_PCRE_H - case CONFIG_COND_MATCH: pcre_free(ds->match.regex); break; + if (ds->regex) pcre_free(ds->regex); + if (ds->regex_study) pcre_free(ds->regex_study); #endif - default: - break; - } free(d); } diff --git a/src/network.c b/src/network.c index 19139404..66075cdc 100644 --- a/src/network.c +++ b/src/network.c @@ -394,7 +394,7 @@ int network_init(server *srv) { return -1; } - if (0 != network_server_init(srv, dc->match.string, s)) { + if (0 != network_server_init(srv, dc->string, s)) { return -1; } } diff --git a/tests/LightyTest.pm b/tests/LightyTest.pm index 6ecd1e87..a95ca06d 100755 --- a/tests/LightyTest.pm +++ b/tests/LightyTest.pm @@ -69,11 +69,14 @@ sub start_proc { system("cat ".$self->{SRCDIR}."/".$self->{CONFIGFILE}.' | perl -pe "s#\@SRCDIR\@#'.$pwd.'/'.$self->{BASEDIR}.'/tests/#" > /tmp/cfg.file'); unlink($self->{LIGHTTPD_PIDFILE}); - system($self->{LIGHTTPD_PATH}." -f /tmp/cfg.file"); - # system("valgrind --tool=memcheck --show-reachable=yes --leak-check=yes --logfile=foo ".$lighttpd_path." -D -f /tmp/cfg.file &"); - # + if (1) { + system($self->{LIGHTTPD_PATH}." -f /tmp/cfg.file"); + select(undef, undef, undef, 0.1); + } else { + system("valgrind --tool=memcheck --show-reachable=yes --leak-check=yes --logfile=foo ".$self->{LIGHTTPD_PATH}." -D -f /tmp/cfg.file &"); + select(undef, undef, undef, 1); + } - select(undef, undef, undef, 0.1); # sleep(1); diff --git a/tests/Makefile.am b/tests/Makefile.am index 54e629ae..da190ae0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,8 @@ CONFS=fastcgi-10.conf \ fastcgi-13.conf \ bug-06.conf \ bug-12.conf \ + condition.conf \ + core-condition.t \ core-request.t \ core-response.t \ core.t \ diff --git a/tests/condition.conf b/tests/condition.conf new file mode 100644 index 00000000..f5947cf6 --- /dev/null +++ b/tests/condition.conf @@ -0,0 +1,59 @@ + +debug.log-request-handling = "enable" +debug.log-condition-handling = "enable" + +server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/" +server.pid-file = "/tmp/lighttpd/lighttpd.pid" + +## bind to port (default: 80) +server.port = 2048 + +## bind to localhost (default: all interfaces) +server.bind = "localhost" +server.errorlog = "/tmp/lighttpd/logs/lighttpd.error.log" +server.name = "www.example.org" +server.tag = "Apache 1.3.29" + + +server.modules = ( + "mod_access", + "mod_accesslog" ) + +######################## MODULE CONFIG ############################ + + +accesslog.filename = "/tmp/lighttpd/logs/lighttpd.access.log" + +mimetype.assign = ( ".html" => "text/html" ) + +# ban first, unban later +url.access-deny = ( "index.html" ) + +$HTTP["host"] == "www.example.org" { + server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "www.example.org" +} +| $HTTP["host"] == "test1.example.org" { + server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "test1.example.org" + url.access-deny = ( "nothing" ) +} +# comments +| $HTTP["host"] == "test2.example.org" { + server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "test2.example.org" + url.access-deny = ( "nothing" ) +} + + # comments + +| $HTTP["host"] == "test3.example.org" { + server.document-root = "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "test3.example.org" + # comments + url.access-deny = ( "nothing" ) + + $HTTP["url"] == "/index.html" { + url.access-deny = ( "index.html" ) + } +} diff --git a/tests/core-condition.t b/tests/core-condition.t new file mode 100755 index 00000000..933f566c --- /dev/null +++ b/tests/core-condition.t @@ -0,0 +1,53 @@ +#! /usr/bin/perl -w +BEGIN { + # add current source dir to the include-path + # we need this for make distcheck + (my $srcdir = $0) =~ s#/[^/]+$#/#; + unshift @INC, $srcdir; +} + +use strict; +use IO::Socket; +use Test::More tests => 6; +use LightyTest; + +my $tf = LightyTest->new(); +my $t; + +$tf->{CONFIGFILE} = 'condition.conf'; +ok($tf->start_proc == 0, "Starting lighttpd") or die(); + +$t->{REQUEST} = ( <{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ); +ok($tf->handle_http($t) == 0, 'config deny'); + +$t->{REQUEST} = ( <{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ); +ok($tf->handle_http($t) == 0, '2nd child of chaining'); + +$t->{REQUEST} = ( <{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ); +ok($tf->handle_http($t) == 0, '3rd child of chaining'); + +$t->{REQUEST} = ( <{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ); +ok($tf->handle_http($t) == 0, 'nesting'); + +ok($tf->stop_proc == 0, "Stopping lighttpd"); +