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
This commit is contained in:
parent
4d6933c0c1
commit
8073d5fe9f
|
@ -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
|
||||
|
|
12
src/array.c
12
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;
|
||||
|
|
21
src/array.h
21
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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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" )
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
Loading…
Reference in New Issue