2
0
Fork 0

Request/condition

This commit is contained in:
Stefan Bühler 2008-06-30 12:25:01 +02:00
parent 777232ab5d
commit c612995cfe
9 changed files with 420 additions and 247 deletions

View File

@ -1,158 +1,140 @@
void action_stack_init(action_stack* as)
{
// preallocate a stack of 15 elements
as->stack = g_array_sized_new(FALSE, TRUE, sizeof(action_stack_elem), 15);
as->index = 0;
}
void action_stack_push(action_stack* as, action_stack_elem ase)
{
// stack needs to grow
if (as->index == (as->stack->len -1)) // we are at the end of the stack
g_array_append_val(as, ase);
else
g_array_insert_val(as, as->index, ase);
#include "actions.h"
#include "condition.h"
as->index++;
}
struct action_stack_element;
typedef struct action_stack_element action_stack_element;
// pops the last entry of the action stack
action_stack_elem action_stack_pop(action_stack* as)
{
as->index--;
return g_array_index(as, action_stack_elem, as->index);
}
struct action_stack_element {
action_list *list;
guint pos;
};
action_result action_list_exec(action_list* al, action_stack* as, guint index)
{
action_stack_elem ase;
guint i;
action* act;
action_result ar;
// iterate over list
for (i = index; i < al->list->len; i++)
{
act = g_array_index(al->list, action*, i);
switch (act->type)
{
case ACTION_CONDITION:
if (TRUE == condition_check(&(act->value.cond)))
{
// save current
ase.al = al;
ase.index = i;
action_stack_push(as, ase);
ar = action_list_exec(act->target, as);
break;
}
else
continue;
case ACTION_SETTING:
ar = ACTION_RESULT_GO_ON;
break;
case ACTION_FUNCTION:
ar = ACTION_RESULT_GO_ON;
break;
default:
ar = ACTION_RESULT_GO_ON;
// TODO: print error and exit
}
if (ar == ACTION_RESULT_BREAK)
void action_release(action *a) {
assert(a->refcount > 0);
if (!(--a->refcount)) {
switch (a->type) {
case ACTION_TSETTING:
/* TODO */
break;
else if (ar == ACTION_RESULT_WAIT_FOR_EVENT)
return ACTION_RESULT_WAIT_FOR_EVENT;
}
// executed all actions in the list
// if the action stack index is > 0, we need to jump back to the previous list
if (as->index > 0)
{
ase = action_stack_pop(as);
return action_list_exec(ase->al, ase->index);
}
return ACTION_RESULT_GO_ON;
}
// checks if a condition is fulfilled. returns the next action to jump to if fulfilled, otherwise NULL
gboolean condition_check(condition* cond)
{
switch (cond->type)
{
case CONDITION_STRING:
return condition_check_string(cond);
case CONDITION_INT:
return condition_check_int(cond);
case CONDITION_BOOL:
return condition_check_bool(cond);
case CONDITION_IP:
// todo
default:
// TODO: print error and exit
return FALSE;
case ACTION_TFUNCTION:
/* TODO */
break;
case ACTION_TCONDITION:
condition_release(a->value.condition.cond);
action_list_release(a->value.condition.target);
break;
}
g_slice_free(action, a);
}
}
void action_acquire(action *a) {
assert(a->refcount > 0);
a->refcount++;
}
// string condition, operators: ==, !=, =~, !~
gboolean condition_check_string(condition* cond)
{
switch (cond->op)
{
case CONDITION_EQUAL:
if (cond->lvalue.val_string->len != cond->rvalue.val_string->len)
return FALSE;
return g_string_equal(cond->lvalue.val_string, cond->rvalue.val_string);
case CONDITION_UNEQUAL:
return (FALSE == g_string_equal(cond->lvalue.val_string, cond->rvalue.val_string)) ? TRUE : FALSE;
case CONDITION_REGEX_MATCH:
// todo
case CONDITION_REGEX_NOMATCH:
// todo
default:
// TODO: print error and exit
return FALSE;
void action_list_release(action_list *al) {
assert(al->refcount > 0);
if (!(--al->refcount)) {
guint i;
for (i = al->actions->len; i-- > 0; ) {
action_release(g_array_index(al->actions, action*, i));
}
g_array_free(al->actions, TRUE);
g_slice_free(action_list, al);
}
}
// integer condition, operators: ==, !=, <, <=, >, >=
gboolean condition_check_int(condition* cond)
{
switch (cond->op)
{
case CONDITION_EQUAL:
return (cond->lvalue.val_int == cond->rvalue.val_int) ? TRUE : FALSE;
case CONDITION_UNEQUAL:
return (cond->lvalue.val_int != cond->rvalue.val_int) ? TRUE : FALSE;
case CONDITION_LESS:
return (cond->lvalue.val_int < cond->rvalue.val_int) ? TRUE : FALSE;
case CONDITION_LESS_EQUAL:
return (cond->lvalue.val_int <= cond->rvalue.val_int) ? TRUE : FALSE;
case CONDITION_GREATER:
return (cond->lvalue.val_int > cond->rvalue.val_int) ? TRUE : FALSE;
case CONDITION_GREATER_EQUAL:
return (cond->lvalue.val_int >= cond->rvalue.val_int) ? TRUE : FALSE;
default:
// TODO: print error and exit
return FALSE;
}
void action_list_acquire(action_list *al) {
assert(al->refcount > 0);
al->refcount++;
}
// bool condition, operators: ==, !=
gboolean condition_check_bool(condition* cond)
{
switch (cond->op)
{
case CONDITION_EQUAL:
return (cond->lvalue.val_bool == cond->rvalue.val_bool) ? TRUE : FALSE;
case CONDITION_UNEQUAL:
return (cond->lvalue.val_bool != cond->rvalue.val_bool) ? TRUE : FALSE;
default:
// TODO: print error and exit
return FALSE;
}
void action_stack_element_release(action_stack_element *ase) {
if (!ase || !ase->list) return;
action_list_release(ase->list);
ase->list = NULL;
}
void action_stack_init(action_stack *as) {
as->stack = g_array_sized_new(FALSE, TRUE, sizeof(action_stack_element), 15);
}
void action_stack_reset(action_stack *as) {
guint i;
for (i = as->stack->len; i-- > 0; ) {
action_stack_element_release(&g_array_index(as->stack, action_stack_element, i));
}
g_array_set_size(as->stack, 0);
}
void action_stack_clear(action_stack *as) {
guint i;
for (i = as->stack->len; i-- > 0; ) {
action_stack_element_release(&g_array_index(as->stack, action_stack_element, i));
}
g_array_free(as->stack, TRUE);
}
/** handle sublist now, remember current position (stack) */
void action_enter(connection *con, action_list *al) {
action_list_acquire(al);
action_stack_element ase = { al, 0 };
g_array_append_val(con->action_stack.stack, ase);
}
static action_stack_element *action_stack_top(action_stack* as) {
return as->stack->len > 0 ? &g_array_index(as->stack, action_stack_element, as->stack->len - 1) : NULL;
}
static void action_stack_pop(action_stack *as) {
action_stack_element_release(&g_array_index(as->stack, action_stack_element, as->stack->len - 1));
g_array_set_size(as->stack, as->stack->len - 1);
}
static action* action_stack_element_next(action_stack_element *ase) {
action_list *al = ase->list;
return ase->pos < al->actions->len ? g_array_index(al->actions, action*, ase->pos++) : NULL;
}
action_result action_execute(server *srv, connection *con) {
action *a;
action_stack *as = &con->action_stack;
action_stack_element *ase;
action_result res;
while (NULL != (ase = action_stack_top(as))) {
a = action_stack_element_next(ase);
if (!a) {
action_stack_pop(as);
continue;
}
switch (a->type) {
case ACTION_TSETTING:
/* TODO */
break;
case ACTION_TFUNCTION:
res = a->value.function.func(srv, con, a->value.function.param);
switch (res) {
case ACTION_GO_ON:
break;
case ACTION_FINISHED:
case ACTION_ERROR:
action_stack_clear(as);
return res;
case ACTION_WAIT_FOR_EVENT:
return ACTION_WAIT_FOR_EVENT;
}
break;
case ACTION_TCONDITION:
if (condition_check(srv, con, a->value.condition.cond)) {
action_enter(con, a->value.condition.target);
}
break;
}
}
return ACTION_GO_ON;
}

View File

@ -1,10 +1,21 @@
#ifndef _LIGHTTPD_ACTIONS_H_
#define _LIGHTTPD_ACTIONS_H_
typedef enum { ACTION_RESULT_GO_ON, ACTION_RESULT_BREAK, ACTION_RESULT_WAIT_FOR_EVENT } action_result;
#include "settings.h"
typedef enum {
ACTION_GO_ON,
ACTION_FINISHED,
ACTION_ERROR,
ACTION_WAIT_FOR_EVENT
} action_result;
// action type
typedef enum { ACTION_SETTING, ACTION_FUNCTION, ACTION_CONDITION } action_type;
typedef enum {
ACTION_TSETTING,
ACTION_TFUNCTION,
ACTION_TCONDITION
} action_type;
struct action;
typedef struct action action;
@ -15,61 +26,51 @@ typedef struct action_list action_list;
struct action_stack;
typedef struct action_stack action_stack;
struct action_list {
GArray* actions;
guint refcount;
};
struct action_stack {
GArray* stack;
guint index;
};
struct action_stack_elem {
action_list* al;
guint index;
struct server; struct connection;
typedef action_result (*action_func)(struct server *srv, struct connection *con, void* param);
#include "condition.h"
struct action_list {
gint refcount;
GArray* actions; /** array of (action*) */
};
typedef struct action_stack_elem action_stack_elem;
struct action {
gint refcount;
action_type type;
union {
struct {
option_mark opt;
option newvalue;
GArray *options; /** array of option_mark */
} setting;
condition cond;
struct {
condition *cond;
action_list* target; /** action target to jump to if condition is fulfilled */
} condition;
struct {
action_func* func;
action_func func;
gpointer param;
} actionfunc;
} function;
} value;
};
struct condition {
condition_type type;
condition_op op;
action_list* target; // action target to jump to if condition is fulfilled
LI_API void action_list_release(action_list *al);
// left value of condition
union {
guint val_int;
gboolean val_bool;
GString* val_string;
} lvalue;
/* no new/free function, so just use the struct direct (i.e. not a pointer) */
LI_API void action_stack_init(action_stack *as);
LI_API void action_stack_reset(action_stack *as);
LI_API void action_stack_clear(action_stack *as);
// right value of condition
union {
guint val_int;
gboolean val_bool;
GString* val_string;
} rvalue;
};
/** handle sublist now, remember current position (stack) */
LI_API void action_enter(connection *con, action_list *al);
LI_API action_result action_execute(server *srv, connection *con);
#endif

View File

@ -12,6 +12,8 @@ struct connection;
typedef struct connection connection;
#include "plugin.h"
#include "actions.h"
#include "request.h"
struct server {
GHashTable *plugins;
@ -21,4 +23,14 @@ struct server {
gpointer *option_def_values;
};
struct connection {
sock_addr dst_addr, src_addr;
GString *dst_addr_str, *src_addr_str;
action_stack action_stack;
request request;
};
#endif

View File

@ -5,10 +5,13 @@
static condition* condition_find_cached(server *srv, GString *key);
static void condition_cache_insert(server *srv, GString *key, condition *c);
static condition* condition_new(config_cond_t cond, comp_key_t comp);
static condition* condition_new_with_string(config_cond_t cond, comp_key_t comp, GString *str);
static condition* condition_new_with_int(config_cond_t cond, comp_key_t comp, gint i);
static condition* cond_new_string(config_cond_t cond, comp_key_t comp, GString *str);
static condition* cond_new_socket(config_cond_t cond, comp_key_t comp, GString *str);
static condition* condition_new_from_string(config_cond_t cond, comp_key_t comp, GString *str);
static void condition_free(condition *c);
static gboolean condition_check_eval(server *srv, connection *con, condition *cond);
static condition* condition_find_cached(server *srv, GString *key) {
UNUSED(srv);
UNUSED(key);
@ -32,7 +35,7 @@ static condition* condition_new(config_cond_t cond, comp_key_t comp) {
return c;
}
static condition* condition_new_with_string(config_cond_t cond, comp_key_t comp, GString *str) {
static condition* cond_new_string(config_cond_t cond, comp_key_t comp, GString *str) {
condition *c = condition_new(cond, comp);
switch (c->cond) {
case CONFIG_COND_EQ: /** == */
@ -66,29 +69,33 @@ static condition* condition_new_with_string(config_cond_t cond, comp_key_t comp,
condition_free(c);
return NULL;
}
c->value_type = COND_VALUE_STRING;
return c;
}
static condition* condition_new_with_int(config_cond_t cond, comp_key_t comp, gint i) {
condition *c = condition_new(cond, comp);
switch (c->cond) {
case CONFIG_COND_EQ: /** == */
case CONFIG_COND_NE: /** != */
case CONFIG_COND_MATCH: /** =~ */
case CONFIG_COND_NOMATCH: /** !~ */
ERROR("Cannot compare with integer in condition: %s %s %i",
config_cond_to_string(cond), comp_key_to_string(comp),
i);
condition_free(c);
return NULL;
case CONFIG_COND_GT: /** > */
case CONFIG_COND_GE: /** >= */
case CONFIG_COND_LT: /** < */
case CONFIG_COND_LE: /** <= */
c->value.i = i;
break;
static condition* cond_new_socket(config_cond_t cond, comp_key_t comp, GString *str) {
return cond_new_string(cond, comp, str);
/* TODO: parse str as socket addr */
}
static condition* condition_new_from_string(config_cond_t cond, comp_key_t comp, GString *str) {
switch (comp) {
case COMP_SERVER_SOCKET:
case COMP_HTTP_REMOTE_IP:
return cond_new_socket(cond, comp, str);
case COMP_HTTP_PATH:
case COMP_HTTP_HOST:
case COMP_HTTP_REFERER:
case COMP_HTTP_USER_AGENT:
case COMP_HTTP_COOKIE:
case COMP_HTTP_SCHEME:
case COMP_HTTP_QUERY_STRING:
case COMP_HTTP_REQUEST_METHOD:
case COMP_PHYSICAL_PATH:
case COMP_PHYSICAL_PATH_EXISTS:
return cond_new_string(cond, comp, str);
}
return c;
return NULL;
}
condition* condition_new_string(server *srv, config_cond_t cond, comp_key_t comp, GString *str) {
@ -101,22 +108,7 @@ condition* condition_new_string(server *srv, config_cond_t cond, comp_key_t comp
return c;
}
c = condition_new_with_string(cond, comp, str);
condition_cache_insert(srv, key, c);
return c;
}
condition* condition_new_int(server *srv, config_cond_t cond, comp_key_t comp, gint i) {
condition *c;
GString *key = g_string_sized_new(0);
g_string_sprintf(key, "%i:%i#%i", (int) cond, (int) comp, i);
if (NULL != (c = condition_find_cached(srv, key))) {
g_string_free(key, TRUE);
return c;
}
c = condition_new_with_int(cond, comp, i);
c = condition_new_from_string(cond, comp, str);
condition_cache_insert(srv, key, c);
return c;
}
@ -130,38 +122,25 @@ condition* condition_new_string_uncached(server *srv, config_cond_t cond, comp_k
g_string_free(key, TRUE);
if (NULL != c) return c;
return condition_new_with_string(cond, comp, str);
}
condition* condition_new_int_uncached(server *srv, config_cond_t cond, comp_key_t comp, gint i) {
condition *c;
GString *key = g_string_sized_new(0);
g_string_sprintf(key, "%i:%i#%i", (int) cond, (int) comp, i);
c = condition_find_cached(srv, key);
g_string_free(key, TRUE);
if (NULL != c) return c;
return condition_new_with_int(cond, comp, i);
return condition_new_from_string(cond, comp, str);
}
static void condition_free(condition *c) {
switch (c->cond) {
case CONFIG_COND_EQ: /** == */
case CONFIG_COND_NE: /** != */
g_string_free(c->value.string, TRUE);
switch (c->value_type) {
case COND_VALUE_INT:
case COND_VALUE_SOCKET_IPV4:
case COND_VALUE_SOCKET_IPV6:
/* nothing to free */
break;
case CONFIG_COND_MATCH: /** =~ */
case CONFIG_COND_NOMATCH: /** !~ */
case COND_VALUE_STRING:
if (c->cond == CONFIG_COND_MATCH || c->cond == CONFIG_COND_NOMATCH) {
#ifdef HAVE_PCRE_H
if (c->value.regex) pcre_free(c->value.regex);
if (c->value.regex_study) pcre_free(c->value.regex_study);
if (c->value.regex) pcre_free(c->value.regex);
if (c->value.regex_study) pcre_free(c->value.regex_study);
#endif
break;
case CONFIG_COND_GT: /** > */
case CONFIG_COND_GE: /** >= */
case CONFIG_COND_LT: /** < */
case CONFIG_COND_LE: /** <= */
} else {
g_string_free(c->value.string, TRUE);
}
break;
}
g_slice_free(condition, c);
@ -187,3 +166,96 @@ const char* comp_key_to_string(comp_key_t comp) {
/* TODO */
return "";
}
gboolean condition_check(server *srv, connection *con, condition *cond) {
/* TODO: implement cache */
return condition_check_eval(srv, con, cond);
}
static gboolean condition_check_eval_string(server *srv, connection *con, condition *cond) {
const char *value = NULL;
GString *tmp = NULL;
gboolean result = FALSE;
UNUSED(srv);
UNUSED(con);
switch (cond->comp) {
/* TODO: get values */
case COMP_SERVER_SOCKET:
break;
case COMP_HTTP_PATH:
value = con->request.uri.path->str;
break;
case COMP_HTTP_HOST:
value = con->request.host->str;
break;
case COMP_HTTP_REFERER:
break;
case COMP_HTTP_USER_AGENT:
break;
case COMP_HTTP_COOKIE:
break;
case COMP_HTTP_SCHEME:
/* TODO: check for ssl */
value = "http"; /* ssl ? "https" : "http" */
break;
case COMP_HTTP_REMOTE_IP:
value = con->dst_addr_str->str;
break;
case COMP_HTTP_QUERY_STRING:
value = con->request.uri.query->str;
break;
case COMP_HTTP_REQUEST_METHOD:
value = con->request.http_method_str->str;
break;
case COMP_PHYSICAL_PATH:
case COMP_PHYSICAL_PATH_EXISTS:
break;
}
if (value) switch (cond->cond) {
case CONFIG_COND_EQ: /** == */
result = 0 == strcmp(value, cond->value.string->str);
break;
case CONFIG_COND_NE: /** != */
result = 0 != strcmp(value, cond->value.string->str);
break;
case CONFIG_COND_MATCH: /** =~ */
case CONFIG_COND_NOMATCH: /** !~ */
/* TODO: pcre */
break;
default:
break;
}
if (tmp) g_string_free(tmp, TRUE);
return result;
}
static gboolean ipv4_in_ipv4_net(guint32 target, guint32 match, guint32 networkmask) {
return (target & networkmask) == (match & networkmask);
}
#ifdef HAVE_IPV6
static gboolean ipv6_in_ipv6_net(const unsigned char *target, const guint8 *match, guint network) {
guint8 mask = network % 8;
if (0 != memcmp(target, match, network / 8)) return FALSE;
if (!mask) return TRUE;
mask = ~(((guint) 1 << (8-mask)) - 1);
return (target[network / 8] & mask) == (match[network / 8] & mask);
}
static gboolean ipv6_in_ipv4_net(const unsigned char *target, guint32 match, guint32 networkmask) {
static const guint8 ipv6match[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
if (!ipv6_in_ipv6_net(target, ipv6match, 96)) return FALSE;
return ipv4_in_ipv4_net(*(guint32*)(target+12), match, networkmask);
}
#endif
static gboolean condition_check_eval(server *srv, connection *con, condition *cond) {
switch (cond->value_type) {
case COND_VALUE_STRING:
return condition_check_eval_string(srv, con, cond);
/* TODO: implement checks */
default:
return FALSE;
}
}

View File

@ -5,10 +5,15 @@
* possible compare ops in the configfile parser
*/
typedef enum {
/* everything */
CONFIG_COND_EQ, /** == */
CONFIG_COND_MATCH, /** =~ */
CONFIG_COND_NE, /** != */
/* only with strings (including socket name) */
CONFIG_COND_MATCH, /** =~ */
CONFIG_COND_NOMATCH, /** !~ */
/* only with int */
CONFIG_COND_GT, /** > */
CONFIG_COND_GE, /** >= */
CONFIG_COND_LT, /** < */
@ -19,7 +24,6 @@ typedef enum {
* possible fields to match against
*/
typedef enum {
COMP_UNSET,
COMP_SERVER_SOCKET,
COMP_HTTP_PATH,
COMP_HTTP_HOST,
@ -31,11 +35,16 @@ typedef enum {
COMP_HTTP_QUERY_STRING,
COMP_HTTP_REQUEST_METHOD,
COMP_PHYSICAL_PATH,
COMP_PHYSICAL_PATH_EXISTS,
COMP_LAST_ELEMENT
COMP_PHYSICAL_PATH_EXISTS
} comp_key_t;
typedef enum {
COND_VALUE_INT,
COND_VALUE_STRING,
COND_VALUE_SOCKET_IPV4, /** only match ip/netmask */
COND_VALUE_SOCKET_IPV6 /** only match ip/netmask */
} cond_value_t;
struct condition;
typedef struct condition condition;
@ -50,6 +59,7 @@ struct condition {
/* index into connection conditional caching table, -1 if uncached */
int cache_index;
cond_value_t value_type;
union {
GString *string;
#ifdef HAVE_PCRE_H
@ -59,6 +69,17 @@ struct condition {
};
#endif
gint i;
struct {
guint32 addr;
guint32 networkmask;
} ipv4;
#ifdef HAVE_IPV6
struct {
guint8 addr[16];
guint network;
} ipv6;
#endif
sock_addr addr;
} value;
};
@ -73,4 +94,7 @@ LI_API void condition_release(condition* c);
LI_API const char* config_cond_to_string(config_cond_t cond);
LI_API const char* comp_key_to_string(comp_key_t comp);
LI_API gboolean condition_check(server *srv, connection *con, condition *cond);
#endif

0
src/request.c Normal file
View File

74
src/request.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef _LIGHTTPD_REQUEST_H_
#define _LIGHTTPD_REQUEST_H_
typedef enum {
HTTP_METHOD_UNSET = -1,
HTTP_METHOD_GET,
HTTP_METHOD_POST,
HTTP_METHOD_HEAD,
HTTP_METHOD_OPTIONS,
HTTP_METHOD_PROPFIND, /* WebDAV */
HTTP_METHOD_MKCOL,
HTTP_METHOD_PUT,
HTTP_METHOD_DELETE,
HTTP_METHOD_COPY,
HTTP_METHOD_MOVE,
HTTP_METHOD_PROPPATCH,
HTTP_METHOD_REPORT, /* DeltaV */
HTTP_METHOD_CHECKOUT,
HTTP_METHOD_CHECKIN,
HTTP_METHOD_VERSION_CONTROL,
HTTP_METHOD_UNCHECKOUT,
HTTP_METHOD_MKACTIVITY,
HTTP_METHOD_MERGE,
HTTP_METHOD_LOCK,
HTTP_METHOD_UNLOCK,
HTTP_METHOD_LABEL,
HTTP_METHOD_CONNECT
} http_method_t;
typedef enum {
HTTP_VERSION_UNSET = -1,
HTTP_VERSION_1_0,
HTTP_VERSION_1_1
} http_version_t;
struct request;
typedef struct request request;
struct request_uri;
typedef struct request_uri request_uri;
struct request_uri {
GString *uri, *orig_uri;
GString *scheme;
GString *path;
GString *query;
};
struct pyhsical {
GString *path;
GString *basedir;
GString *doc_root;
GString *rel_path;
GString *pathinfo;
};
struct request {
http_method_t http_method;
GString *http_method_str;
http_version_t http_version;
request_uri uri;
GArray *headers;
/* Parsed headers: */
GString *host;
goffset content_length;
};
#endif

View File

@ -5,8 +5,15 @@
#include "config.h"
#endif
#if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H
# define USE_OPENSSL
# include <openssl/ssl.h>
#endif
#include <glib.h>
#include <assert.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

View File

@ -7,6 +7,7 @@ import Params
common_uselib = 'glib '
common_source='''
actions.c
base.c
chunks.c
condition.c