summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kneschke <jan@kneschke.de>2005-08-08 13:48:33 +0000
committerJan Kneschke <jan@kneschke.de>2005-08-08 13:48:33 +0000
commit8073d5fe9f720a0564dbced1fdef187f5c19ffa3 (patch)
treea8191be15dd3a92d7f91d51230f2071f70bb418d
parent4d6933c0c19ac27330b5a1e7367a24834fb18f79 (diff)
downloadlighttpd-git-8073d5fe9f720a0564dbced1fdef187f5c19ffa3.tar.gz
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
-rw-r--r--src/Makefile.am4
-rw-r--r--src/array.c12
-rw-r--r--src/array.h21
-rw-r--r--src/base.h2
-rw-r--r--src/configfile-glue.c190
-rw-r--r--src/configfile.c (renamed from src/config.c)116
-rw-r--r--src/configfile.h6
-rw-r--r--src/configparser.y105
-rw-r--r--src/connections.c1
-rw-r--r--src/data_config.c9
-rw-r--r--src/network.c2
-rwxr-xr-xtests/LightyTest.pm11
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/condition.conf59
-rwxr-xr-xtests/core-condition.t53
15 files changed, 500 insertions, 93 deletions
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
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} = ( <<EOF
+GET /index.html HTTP/1.0
+Host: www.example.org
+EOF
+ );
+$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } );
+ok($tf->handle_http($t) == 0, 'config deny');
+
+$t->{REQUEST} = ( <<EOF
+GET /index.html HTTP/1.0
+Host: test1.example.org
+EOF
+ );
+$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } );
+ok($tf->handle_http($t) == 0, '2nd child of chaining');
+
+$t->{REQUEST} = ( <<EOF
+GET /index.html HTTP/1.0
+Host: test2.example.org
+EOF
+ );
+$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } );
+ok($tf->handle_http($t) == 0, '3rd child of chaining');
+
+$t->{REQUEST} = ( <<EOF
+GET /index.html HTTP/1.0
+Host: test3.example.org
+EOF
+ );
+$t->{RESPONSE} = ( { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } );
+ok($tf->handle_http($t) == 0, 'nesting');
+
+ok($tf->stop_proc == 0, "Stopping lighttpd");
+