Browse Source

angel: some basic structs, code not ready yet (config parser "works")

personal/stbuehler/wip
Stefan Bühler 13 years ago
parent
commit
72136b4d03
  1. 27
      include/lighttpd/angel_base.h
  2. 14
      include/lighttpd/angel_config_parser.h
  3. 135
      include/lighttpd/angel_connection.h
  4. 58
      include/lighttpd/angel_data.h
  5. 11
      include/lighttpd/angel_plugin.h
  6. 36
      include/lighttpd/angel_server.h
  7. 60
      include/lighttpd/angel_value.h
  8. 4
      include/lighttpd/base.h
  9. 13
      src/CMakeLists.txt
  10. 428
      src/angel_config_parser.rl
  11. 0
      src/angel_connection.c
  12. 126
      src/angel_data.c
  13. 61
      src/angel_main.c
  14. 0
      src/angel_plugin.c
  15. 0
      src/angel_server.c
  16. 210
      src/angel_value.c

27
include/lighttpd/angel_base.h

@ -0,0 +1,27 @@
#ifndef _LIGHTTPD_ANGEL_BASE_H_
#define _LIGHTTPD_ANGEL_BASE_H_
#ifdef _LIGHTTPD_BASE_H_
#error Do not mix lighty with angel code
#endif
#include <lighttpd/settings.h>
#include <lighttpd/module.h>
/* angel_server.h */
struct server;
typedef struct server server;
struct instance;
typedef struct instance instance;
#include <lighttpd/angel_value.h>
#include <lighttpd/angel_data.h>
#include <lighttpd/angel_connection.h>
#include <lighttpd/angel_server.h>
#include <lighttpd/angel_plugin.h>
#endif

14
include/lighttpd/angel_config_parser.h

@ -0,0 +1,14 @@
#ifndef _LIGHTTPD_ANGEL_CONIG_PARSER_H_
#define _LIGHTTPD_ANGEL_CONIG_PARSER_H_
/* error handling */
#define ANGEL_CONFIG_PARSER_ERROR angel_config_parser_error_quark()
LI_API GQuark angel_config_parser_error_quark();
typedef enum {
ANGEL_CONFIG_PARSER_ERROR_PARSE, /* parse error */
} AngelConfigParserError;
LI_API gboolean angel_config_parse_file(const gchar *filename, GError **err);
#endif

135
include/lighttpd/angel_connection.h

@ -0,0 +1,135 @@
#ifndef _LIGHTTPD_ANGEL_CONNECTION_H_
#define _LIGHTTPD_ANGEL_CONNECTION_H_
struct angel_connection;
typedef struct angel_connection angel_connection;
struct angel_call;
typedef struct angel_call angel_call;
typedef void (*AngelCallback)(angel_call *acall, gboolean timeout, GString *error, GString *data, GArray *fds);
struct angel_connection {
GStaticMutex mutex; /* angel itself has no threads */
struct ev_loop *loop;
int fd;
};
/* with multi-threading you should protect the structure
* containing the angel_call with a lock
*/
struct angel_call {
gpointer context;
AngelCallback callback;
/* internal data */
gint32 id; /* id is -1 if there is no call pending (the callback may still be running) */
guint timeout;
ev_timer timeout_watcher;
ev_io fd_watcher;
};
/* error handling */
#define ANGEL_CALL_ERROR angel_call_error_quark()
LI_API GQuark angel_call_error_quark();
typedef enum {
ANGEL_CALL_ALREADY_RUNNING /* the angel_call struct is already in use for a call */
} AngelCallError;
/* create connection */
LI_API angel_connection* angel_connection_create(int fd);
/* calls */
/* the GString* parameters get stolen by the angel call (moved to chunkqueue) */
LI_API void angel_call_init(angel_call *call);
LI_API gboolean angel_send_simple_call(
angel_connection *acon,
const gchar *module, gsize module_len, const gchar *action, gsize action_len,
GString *data,
GError **err);
LI_API gboolean angel_send_call(
angel_connection *acon,
const gchar *module, gsize module_len, const gchar *action, gsize action_len,
angel_call *call, guint timeout,
GString *data,
GError **err);
LI_API gboolean angel_send_result(
angel_connection *acon,
const gchar *module, gsize module_len, const gchar *action, gsize action_len,
angel_call *call, guint timeout,
GString *error, GString *data, GArray *fds,
GError **err);
LI_API gboolean angel_cancel_call(angel_connection *acon, angel_call *call);
/* Usage */
#if 0
void init() {
/* ... init ctx... */
angel_call_init(&ctx->call);
ctx->call.context = ctx;
ctx->call.callback = my_callback;
}
gboolean start_call(curctx) {
GError *err = NULL;
GString *data;
lock();
ctx = get_ctx();
/* another thread may have already called angel, depending on the task (e.g. fastcgi spawn) */
if (!angel_call_is_needed(ctx)) {
unlock();
return TRUE;
}
data = build_call_data();
if (!angel_send_call(acon, CONST_STR_LEN("mymod"), CONST_STR_LEN("myaction"), &ctx->call, 10, data, &err)) {
unlock();
report_error(&err);
return FALSE;
}
/* add current context (e.g. vrequest) to a wakeup list */
push_to_queue(ctx->waitqueue, curctx);
unlock();
return TRUE; /* at this point the callback may be already finished */
}
void my_callback(angel_call *acall, gboolean timeout, GString *error, GString *data, GArray *fds) {
lock();
handle_error();
parse_data();
/* ... */
done:
wakeup(acall->ctx->waitqueue);
if (error) g_string_free(error, TRUE);
if (data) g_string_free(error, TRUE);
/* destroy fd-array? */
perhaps_free_ctx(acall->ctx);
unlock();
}
void stop_call() {
lock();
ctx = get_ctx();
if (!angel_cancel_call(acon, ctx)) {
/* callback either is already done or just to be called */
/* do _not_ destroy the context */
unlock();
return;
}
perhaps_free_ctx(ctx);
unlock();
}
#endif
#endif

58
include/lighttpd/angel_data.h

@ -0,0 +1,58 @@
#ifndef _LIGHTTPD_ANGEL_DATA_H_
#define _LIGHTTPD_ANGEL_DATA_H_
/* write/read data from/to a buffer (GString) (binary)
* this is not meant to be the most performant way to do this,
* as communication with the angel shouldn't happen to often anyway.
*
* Please never send "user" data to the angel (i.e. do not implement
* something like a mod_cgi via sending the request data to the angel;
* instead use the angel to spawn a fastcgi backend (or something similar)
* and send the request via a socket to the backend directly.
*/
/* angel obviously doesn't work across platforms, so we don't need
* to care about endianess
*/
/* The buffer may be bigger of course, but a single string should not
* exceed this length: */
#define ANGEL_DATA_MAX_STR_LEN 1024 /* must fit into a gint32 */
/* Needed for reading data */
struct angel_buffer;
typedef struct angel_buffer angel_buffer;
struct angel_buffer {
GString *data;
gsize pos;
};
/* error handling */
#define ANGEL_DATA_ERROR angel_data_error_quark()
LI_API GQuark angel_data_error_quark();
typedef enum {
ANGEL_DATA_ERROR_EOF, /* not enough data to read value */
ANGEL_DATA_ERROR_INVALID_STRING_LENGTH, /* invalid string length read from buffer (< 0 || > max-str-len) */
ANGEL_DATA_ERROR_STRING_TOO_LONG /* string too long (len > max-str-len) */
} AngelDataError;
/* write */
LI_API gboolean angel_data_write_int32(GString *buf, gint32 i, GError **err);
LI_API gboolean angel_data_write_int64(GString *buf, gint64 i, GError **err);
LI_API gboolean angel_data_write_char (GString *buf, gchar c, GError **err);
LI_API gboolean angel_data_write_str (GString *buf, const GString *str, GError **err);
LI_API gboolean angel_data_write_cstr (GString *buf, const gchar *str, gsize len, GError **err);
/* read:
* - if the val pointer is NULL, the data will be discarded
* - reading strings: if *val != NULL *val will be reused;
* otherwise a new GString* will be created
* - *val will only be modified if no error is returned
*/
LI_API gboolean angel_data_read_int32(angel_buffer *buf, gint32 *val, GError **err);
LI_API gboolean angel_data_read_int64(angel_buffer *buf, gint64 *val, GError **err);
LI_API gboolean angel_data_read_char (angel_buffer *buf, gchar *val, GError **err);
LI_API gboolean angel_data_read_str (angel_buffer *buf, GString **val, GError **err);
#endif

11
include/lighttpd/angel_plugin.h

@ -0,0 +1,11 @@
#ifndef _LIGHTTPD_ANGEL_PLUGIN_H_
#define _LIGHTTPD_ANGEL_PLUGIN_H_
#ifndef _LIGHTTPD_ANGEL_BASE_H_
#error Please include <lighttpd/angel_base.h> instead of this file
#endif
LI_API gboolean plugins_handle_item(server *srv, value *hash);
#endif

36
include/lighttpd/angel_server.h

@ -0,0 +1,36 @@
#ifndef _LIGHTTPD_ANGEL_SERVER_H_
#define _LIGHTTPD_ANGEL_SERVER_H_
#ifndef _LIGHTTPD_ANGEL_BASE_H_
#error Please include <lighttpd/angel_base.h> instead of this file
#endif
#ifndef LIGHTTPD_ANGEL_MAGIC
#define LIGHTTPD_ANGEL_MAGIC ((guint)0x3e14ac65)
#endif
struct instance {
pid_t pid;
};
struct server {
guint32 magic; /** server magic version, check against LIGHTTPD_ANGEL_MAGIC in plugins */
struct ev_loop *loop;
ev_signal
sig_w_INT,
sig_w_TERM,
sig_w_PIPE;
struct modules *modules;
GHashTable *plugins; /**< const gchar* => (plugin*) */
struct plugin *core_plugin;
ev_tstamp started;
};
LI_API server* server_new(const gchar *module_dir);
LI_API void server_free(server* srv);
#endif

60
include/lighttpd/angel_value.h

@ -0,0 +1,60 @@
#ifndef _LIGHTTPD_ANGEL_VALUE_H_
#define _LIGHTTPD_ANGEL_VALUE_H_
#ifndef _LIGHTTPD_ANGEL_BASE_H_
#error Please include <lighttpd/angel_base.h> instead of this file
#endif
struct value;
typedef struct value value;
struct value_range;
typedef struct value_range value_range;
typedef enum {
VALUE_NONE,
/* primitive types */
VALUE_BOOLEAN,
VALUE_NUMBER,
VALUE_STRING,
VALUE_RANGE,
/* container */
VALUE_LIST,
VALUE_HASH
} value_type;
struct value_range {
guint64 from, to;
};
struct value {
value_type type;
union {
gboolean boolean;
gint64 number;
GString *string;
value_range range;
/* array of (value*) */
GPtrArray *list;
/* hash GString => value */
GHashTable *hash;
} data;
};
LI_API value* value_new_none();
LI_API value* value_new_bool(gboolean val);
LI_API value* value_new_number(gint64 val);
LI_API value* value_new_string(GString *val);
LI_API value* value_new_range(value_range val);
LI_API value* value_new_list();
LI_API value* value_new_hash();
LI_API value* value_copy(value* val);
LI_API void value_free(value* val);
LI_API const char* value_type_string(value_type type);
LI_API GString *value_to_string(value *val);
#endif

4
include/lighttpd/base.h

@ -1,6 +1,10 @@
#ifndef _LIGHTTPD_BASE_H_
#define _LIGHTTPD_BASE_H_
#ifdef _LIGHTTPD_ANGEL_BASE_H_
#error Do not mix lighty with angel code
#endif
#include <lighttpd/settings.h>
/* Next try to fix strict-alias warning */

13
src/CMakeLists.txt

@ -268,6 +268,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CM
SET(COMMON_SRC
angel.c
angel_fake.c
angel_data.c
actions.c
base.c
chunk.c
@ -324,6 +325,7 @@ RAGEL_PARSER(config_parser.rl -T0)
RAGEL_PARSER(http_request_parser.rl)
RAGEL_PARSER(http_response_parser.rl)
RAGEL_PARSER(url_parser.rl)
RAGEL_PARSER(angel_config_parser.rl)
SET(L_INSTALL_TARGETS)
@ -356,6 +358,17 @@ ADD_AND_INSTALL_LIBRARY(mod_vhost "modules/mod_vhost.c")
ADD_TARGET_PROPERTIES(lighttpd LINK_FLAGS ${COMMON_LDFLAGS})
ADD_TARGET_PROPERTIES(lighttpd COMPILE_FLAGS ${COMMON_CFLAGS})
ADD_EXECUTABLE(lighttpd-angel
angel_data.c
angel_main.c
angel_value.c
angel_plugin.c
angel_config_parser.c
)
ADD_TARGET_PROPERTIES(lighttpd-angel LINK_FLAGS "${LUA_LDFLAGS} ${EV_LDFLAGS} ${GMODULE_LDFLAGS} ${WARN_FLAGS}")
ADD_TARGET_PROPERTIES(lighttpd-angel COMPILE_FLAGS "${LUA_CFLAGS} ${EV_CFLAGS} ${GMODULE_CFLAGS} ${WARN_FLAGS}")
IF(HAVE_PCRE_H)
TARGET_LINK_LIBRARIES(lighttpd ${PCRE_LIBRARY})
ENDIF(HAVE_PCRE_H)

428
src/angel_config_parser.rl

@ -0,0 +1,428 @@
#include <lighttpd/angel_base.h>
#include <lighttpd/angel_config_parser.h>
typedef struct {
GArray *g_stack;
int *stack;
int cs, top;
GString *token;
gboolean readingtoken;
char hexchar;
gint64 number, number2;
gint sign;
/* item */
GString *itemname;
value *itemvalue;
GPtrArray *valuestack;
value *curvalue;
gchar buf[8];
} pcontext;
typedef struct {
const gchar *filename;
int line, column;
} filecontext;
GQuark angel_config_parser_error_quark() {
return g_quark_from_static_string("angel-config-parser-error-quark");
}
static gchar *format_char(pcontext *ctx, gchar c) {
if (g_ascii_isprint(c)) {
ctx->buf[0] = c;
ctx->buf[1] = '\0';
} else switch (c) {
case '\n': ctx->buf[0] = '\\'; ctx->buf[1] = 'n'; ctx->buf[2] = '\0'; break;
case '\r': ctx->buf[0] = '\\'; ctx->buf[1] = 'r'; ctx->buf[2] = '\0'; break;
case '\t': ctx->buf[0] = '\\'; ctx->buf[1] = 't'; ctx->buf[2] = '\0'; break;
default: g_snprintf(ctx->buf, 8, "\\x%02X", (unsigned int) (unsigned char) c); break;
}
return ctx->buf;
}
#define PARSE_ERROR_FMT(fmt, ...) do { \
g_set_error(err, \
ANGEL_CONFIG_PARSER_ERROR, \
ANGEL_CONFIG_PARSER_ERROR_PARSE, \
"Parsing failed in '%s:%i,%i': " fmt, \
fctx->filename, fctx->line, fctx->column, \
__VA_ARGS__); \
} while (0)
#define PARSE_ERROR(msg) PARSE_ERROR_FMT("%s", msg)
#define UPDATE_COLUMN() do { \
fctx->column += p - linestart; \
linestart = p; \
} while (0)
%%{
machine angel_config_parser;
access ctx->;
prepush { update_stack(ctx); }
postpop { update_stack(ctx); }
action starttoken {
g_string_truncate(ctx->token, 0);
tokenstart = fpc;
ctx->readingtoken = TRUE;
}
action endtoken {
g_string_append_len(ctx->token, tokenstart, fpc - tokenstart);
tokenstart = NULL;
ctx->readingtoken = FALSE;
}
action newline {
fctx->line++;
fctx->column = 1;
linestart = fpc+1;
}
action startitem {
ctx->itemname = ctx->token;
ctx->token = g_string_sized_new(0);
ctx->itemvalue = value_new_hash();
}
action enditem {
GString *tmp = value_to_string(ctx->itemvalue);
g_printerr("Item '%s': %s\n", ctx->itemname->str, tmp->str);
g_string_free(tmp, TRUE);
g_string_free(ctx->itemname, TRUE);
ctx->itemname = NULL;
value_free(ctx->itemvalue);
ctx->itemvalue = NULL;
}
action error_unknown {
UPDATE_COLUMN();
PARSE_ERROR("internal parse error");
ctx->cs = angel_config_parser_error; fbreak;
}
action error_unexpected_char {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s'", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_block {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected block ('{...}' or ';') for item '%s'", format_char(ctx, fc), ctx->itemname->str);
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_xdigit {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected hexdigit", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_forbidden_character {
UPDATE_COLUMN();
PARSE_ERROR_FMT("forbidden character '%s' in string", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_semicolon {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected ';'", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_colon {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected ':'", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_string {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected a string (\"...\")", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_value {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected a value", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action error_expected_hash_end {
UPDATE_COLUMN();
PARSE_ERROR_FMT("unexpected character '%s', expected ']'", format_char(ctx, fc));
ctx->cs = angel_config_parser_error; fbreak;
}
action string_start {
g_string_truncate(ctx->token, 0);
}
action string_append {
g_string_append_len(ctx->token, fpc, 1);
}
action value_true {
ctx->curvalue = value_new_bool(TRUE);
}
action value_false {
ctx->curvalue = value_new_bool(FALSE);
}
action value_number {
ctx->curvalue = value_new_number(ctx->number);
}
action value_range {
value_range vr = { ctx->number2, ctx->number };
ctx->curvalue = value_new_range(vr);
}
action value_string {
ctx->curvalue = value_new_string(ctx->token);
ctx->token = g_string_sized_new(0);
}
action value_list_start {
g_ptr_array_add(ctx->valuestack, value_new_list());
ctx->curvalue = NULL;
fcall value_list_sub;
}
action value_list_push {
value *vlist = g_ptr_array_index(ctx->valuestack, ctx->valuestack->len-1);
g_ptr_array_add(vlist->data.list, ctx->curvalue);
ctx->curvalue = NULL;
}
action value_list_end {
guint ndx = ctx->valuestack->len - 1;
ctx->curvalue = g_ptr_array_index(ctx->valuestack, ndx);
g_ptr_array_set_size(ctx->valuestack, ndx);
fret;
}
action value_hash_start {
g_ptr_array_add(ctx->valuestack, value_new_hash());
ctx->curvalue = NULL;
fcall value_hash_sub;
}
action value_hash_push_name {
g_ptr_array_add(ctx->valuestack, ctx->curvalue);
ctx->curvalue = NULL;
}
action value_hash_push_value {
guint ndx = ctx->valuestack->len-1;
value *vname = g_ptr_array_index(ctx->valuestack, ndx);
value *vhash = g_ptr_array_index(ctx->valuestack, ndx-1);
g_ptr_array_set_size(ctx->valuestack, ndx);
g_hash_table_insert(vhash->data.hash, vname->data.string, ctx->curvalue);
ctx->curvalue = NULL;
vname->type = VALUE_NONE;
value_free(vname);
}
action value_hash_end {
guint ndx = ctx->valuestack->len - 1;
ctx->curvalue = g_ptr_array_index(ctx->valuestack, ndx);
g_ptr_array_set_size(ctx->valuestack, ndx);
fret;
}
action option_start {
g_ptr_array_add(ctx->valuestack, value_new_string(ctx->token));
ctx->token = g_string_sized_new(0);
ctx->curvalue= NULL;
}
action option_push {
guint ndx = ctx->valuestack->len-1;
value *vname = g_ptr_array_index(ctx->valuestack, ndx);
g_ptr_array_set_size(ctx->valuestack, ndx);
if (!ctx->curvalue) ctx->curvalue = value_new_none();
g_hash_table_insert(ctx->itemvalue->data.hash, vname->data.string, ctx->curvalue);
vname->type = VALUE_NONE;
value_free(vname);
}
line_sane = ( '\n' ) @newline;
line_weird = ( '\r' ) @newline;
line_insane = ( '\r\n' ) @{ fctx->line--; };
line = ( line_sane | line_weird | line_insane );
# line = ( '\n' | '\r' '\n'? <: '' ) %newline;
ws = [\t\v\f ];
comment = '#' (any - line)* line;
noise = ( ws | line | comment )+;
# FIXME for viewing the statemachine
# noise = ( ws | [\r\n] )+;
id = (alpha (alnum | '.' | '-' | '_')** @err(error_unexpected_char) ) >starttoken %endtoken;
special_chars = '\\' (
'n' @{ g_string_append(ctx->token, "\n"); }
| 't' @{ g_string_append(ctx->token, "\t"); }
| 'r' @{ g_string_append(ctx->token, "\r"); }
| 'x'
(xdigit @{ ctx->hexchar = 16*g_ascii_xdigit_value(fc); }
xdigit @{ ctx->hexchar += g_ascii_xdigit_value(fc);
g_string_append_len(ctx->token, &ctx->hexchar, 1);
} ) @err(error_expected_xdigit)
| (any - [ntrx] - cntrl) @string_append
) ;
string = ('"' @string_start ( (any - ["\\] - cntrl)@string_append | special_chars )* '"' ) <>err(error_forbidden_character);
# FIXME for viewing the statemachine
# string = '"' @string_start '"';
action number_digit {
ctx->number = ctx->number*10 + ctx->sign * (fc - '0');
}
number = (('-'@{ctx->sign = -1;})? (digit digit**) $number_digit) >{ ctx->number = 0; ctx->sign = 1; };
value_bool = ('true'i | 'enabled'i) %value_true | ('false'i | 'disabled'i) %value_false;
value_number = number noise** ('-'@{ ctx->number2 = ctx->number; } noise* number %value_range | '' %value_number);
value_string = string @value_string;
value_list = '(' @value_list_start;
value_hash = '[' @value_hash_start;
value = (value_bool | value_number | value_string | value_list | value_hash) >err(error_expected_value);
# FIXME for viewing the statemachine
# value = "v";
value_list_sub_item = value %value_list_push;
value_list_sub := ((noise | value_list_sub_item noise* ',')* value_list_sub_item? (noise*) ')' @value_list_end) $err(error_unexpected_char);
value_hash_sub_item = (value_string %value_hash_push_name) $err(error_expected_string) (noise*) $err(error_expected_colon) ':' noise* value %value_hash_push_value;
value_hash_sub := (noise | value_hash_sub_item noise* ',')* value_hash_sub_item? (noise*) $err(error_expected_hash_end) ']' @value_hash_end;
option = id %option_start <: noise* value? (noise*) $err(error_expected_semicolon) ';' @option_push ;
optionlist = (noise | option)*;
item = (id %startitem) noise* ( ('{' optionlist '}') | ';' ) >err(error_expected_block) @enditem ;
main := ((noise | item) >err(error_unexpected_char) )*;
}%%
%% write data;
static void update_stack(pcontext *ctx) {
g_array_set_size(ctx->g_stack, ctx->top + 1);
ctx->stack = (int*) ctx->g_stack->data;
}
static int angel_config_parser_has_error(pcontext *ctx) {
return ctx->cs == angel_config_parser_error;
}
static int angel_config_parser_is_finished(pcontext *ctx) {
return ctx->cs >= angel_config_parser_first_final;
}
static pcontext* angel_config_parser_new() {
pcontext *ctx = g_slice_new0(pcontext);
ctx->g_stack = g_array_sized_new(FALSE, FALSE, sizeof(int), 8);
ctx->top = 0;
g_array_set_size(ctx->g_stack, ctx->top + 1);
ctx->stack = (int*) ctx->g_stack->data;
ctx->token = g_string_sized_new(0);
ctx->valuestack = g_ptr_array_new();
%% write init;
return ctx;
}
static void angel_config_parser_free(pcontext *ctx) {
if (!ctx) return;
g_array_free(ctx->g_stack, TRUE);
g_string_free(ctx->token, TRUE);
for (guint i = 0; i < ctx->valuestack->len; i++) {
value_free(g_ptr_array_index(ctx->valuestack, i));
}
g_ptr_array_free(ctx->valuestack, TRUE);
if (ctx->itemname) g_string_free(ctx->itemname, TRUE);
value_free(ctx->itemvalue);
value_free(ctx->curvalue);
g_slice_free(pcontext, ctx);
}
static gboolean angel_config_parser_finalize(pcontext *ctx, filecontext *fctx, GError **err) {
/*
gchar c;
gchar *p = &c, *pe = p, *eof = p, *linestart = p, *tokenstart = NULL;
if (ctx->readingtoken) tokenstart = p;
%% write exec;
if (ctx->readingtoken) g_string_append_len(ctx->token, tokenstart, p - tokenstart);
UPDATE_COLUMN();
if (err && *err) return FALSE;
if (angel_config_parser_has_error(ctx)) {
PARSE_ERROR("unknown parser error");
return FALSE;
}
*/
if (!angel_config_parser_is_finished(ctx)) {
PARSE_ERROR("unexpected end of file");
return FALSE;
}
return TRUE;
}
static gboolean angel_config_parse_data(pcontext *ctx, filecontext *fctx, gchar *data, gsize len, GError **err) {
gchar *p = data, *pe = p+len, *eof = NULL, *linestart = p, *tokenstart = NULL;
if (ctx->readingtoken) tokenstart = p;
%% write exec;
if (ctx->readingtoken) g_string_append_len(ctx->token, tokenstart, p - tokenstart);
UPDATE_COLUMN();
fctx->column--;
if (err && *err) return FALSE;
if (angel_config_parser_has_error(ctx)) {
PARSE_ERROR("unknown parser error");
return FALSE;
}
return TRUE;
}
gboolean angel_config_parse_file(const gchar *filename, GError **err) {
char *data = NULL;
gsize len = 0;
filecontext sfctx, *fctx = &sfctx;
pcontext *ctx = angel_config_parser_new();
if (err && *err) goto error;
if (!g_file_get_contents(filename, &data, &len, err)) goto error;
sfctx.filename = filename;
sfctx.line = 1;
sfctx.column = 1;
if (!angel_config_parse_data(ctx, fctx, data, len, err)) goto error;
if (!angel_config_parser_finalize(ctx, fctx, err)) goto error;
if (data) g_free(data);
angel_config_parser_free(ctx);
return TRUE;
error:
if (data) g_free(data);
angel_config_parser_free(ctx);
return FALSE;
}

0
src/angel_connection.c

126
src/angel_data.c

@ -0,0 +1,126 @@
#include <lighttpd/base.h>
#include <lighttpd/angel_data.h>
/* error handling */
GQuark angel_data_error_quark() {
return g_quark_from_static_string("angel-data-error-quark");
}
static gboolean error_eof(GError **err, const gchar *info) {
g_set_error(err,
ANGEL_DATA_ERROR,
ANGEL_DATA_ERROR_EOF,
"Not enough data to read value '%s'", info);
return FALSE;
}
/* write */
gboolean angel_data_write_int32(GString *buf, gint32 i, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
g_string_append_len(buf, (const gchar*) &i, sizeof(i));
return TRUE;
}
gboolean angel_data_write_int64(GString *buf, gint64 i, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
g_string_append_len(buf, (const gchar*) &i, sizeof(i));
return TRUE;
}
gboolean angel_data_write_char (GString *buf, gchar c, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
g_string_append_len(buf, &c, sizeof(c));
return TRUE;
}
gboolean angel_data_write_str (GString *buf, const GString *str, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
if (str->len > ANGEL_DATA_MAX_STR_LEN) {
g_set_error(err,
ANGEL_DATA_ERROR,
ANGEL_DATA_ERROR_STRING_TOO_LONG,
"String too long (len: %" G_GSIZE_FORMAT "): '%s'", str->len, str->str);
return FALSE;
}
if (!angel_data_write_int32(buf, str->len, err)) return FALSE;
g_string_append_len(buf, GSTR_LEN(str));
return TRUE;
}
gboolean angel_data_write_cstr (GString *buf, const gchar *str, gsize len, GError **err) {
const GString tmps = { (gchar*) str, len, 0 }; /* fake const GString */
return angel_data_write_str(buf, &tmps, err);
}
/* read */
gboolean angel_data_read_int32(angel_buffer *buf, gint32 *val, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
if (buf->data->len - buf->pos < sizeof(gint32)) {
return error_eof(err, "int32");
}
if (val) {
memcpy(val, buf->data->str + buf->pos, sizeof(gint32));
}
buf->pos += sizeof(gint32);
return TRUE;
}
gboolean angel_data_read_int64(angel_buffer *buf, gint64 *val, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
if (buf->data->len - buf->pos < sizeof(gint64)) {
return error_eof(err, "int64");
}
if (val) {
memcpy(val, buf->data->str + buf->pos, sizeof(gint64));
}
buf->pos += sizeof(gint64);
return TRUE;
}
gboolean angel_data_read_char (angel_buffer *buf, gchar *val, GError **err) {
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
if (buf->data->len - buf->pos < sizeof(gchar)) {
return error_eof(err, "char");
}
if (val) {
*val = buf->data->str[buf->pos];
}
buf->pos += sizeof(gchar);
return TRUE;
}
gboolean angel_data_read_str (angel_buffer *buf, GString **val, GError **err) {
gint32 ilen;
gsize len;
GString *s;
g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
if (buf->data->len - buf->pos < sizeof(gint32)) {
return error_eof(err, "string-length");
}
memcpy(&ilen, buf->data->str + buf->pos, sizeof(len));
buf->pos += sizeof(gint32);
if (ilen < 0 || ilen > ANGEL_DATA_MAX_STR_LEN) {
g_set_error(err,
ANGEL_DATA_ERROR,
ANGEL_DATA_ERROR_INVALID_STRING_LENGTH,
"String length in buffer invalid: %i", (gint) ilen);
return FALSE;
}
len = (gsize) ilen;
if (buf->data->len - buf->pos < len) {
return error_eof(err, "string-data");
}
s = *val;
if (!s) {
*val = s = g_string_sized_new(len);
} else {
g_string_truncate(s, 0);
}
g_string_append_len(s, buf->data->str + buf->pos, len);
buf->pos += len;
return TRUE;
}

61
src/angel_main.c

@ -0,0 +1,61 @@
#include <lighttpd/angel_base.h>
#include <lighttpd/angel_config_parser.h>
int main(int argc, char *argv[]) {
GError *error = NULL;
GOptionContext *context;
/* options */
gboolean show_version = FALSE, no_fork = FALSE;
gchar const *const def_module_dir = "/usr/local/lib"; /* TODO: configure module-dir with make-system */
gchar const *module_dir = def_module_dir;
gchar const *config_path = NULL, *pidfile = NULL;
gboolean res;
int result = 0;
GOptionEntry entries[] = {
{ "config", 'c', 0, G_OPTION_ARG_FILENAME, &config_path, "filename/path of the config", "PATH" },
{ "module-dir", 'm', 0, G_OPTION_ARG_STRING, &module_dir, "module directory", "PATH" },
{ "no-daemon", 'n', 0, G_OPTION_ARG_NONE, &no_fork, "Don't fork (for daemontools)", NULL },
{ "pid-file", 0, 0, G_OPTION_ARG_STRING, &pidfile, "Location of the pid file (only valid in daemon mode)", "PATH" },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "show version and exit", NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
/* parse commandline options */
context = g_option_context_new("- fast and lightweight webserver");
g_option_context_add_main_entries(context, entries, NULL);
res = g_option_context_parse(context, &argc, &argv, &error);
g_option_context_free(context);
if (!res) {
g_printerr("failed to parse command line arguments: %s\n", error->message);
g_error_free(error);
goto cleanup;
}
if (show_version) {
g_print("%s %s - a fast and lightweight webserver\n", PACKAGE_NAME "-angel", PACKAGE_VERSION);
g_print("Build date: %s\n", PACKAGE_BUILD_DATE);
goto cleanup;
}
if (!angel_config_parse_file(config_path, &error)) {
g_printerr("failed to parse config file: %s\n", error->message);
g_error_free(error);
result = -1;
goto cleanup;
}
g_printerr("Parsed config file\n");
cleanup:
if (config_path) g_free((gchar*) config_path);
if (module_dir != def_module_dir) g_free((gchar*) module_dir);
return 0;
}

0
src/angel_plugin.c

0
src/angel_server.c

210
src/angel_value.c

@ -0,0 +1,210 @@
#include <lighttpd/angel_base.h>
value* value_new_none() {
value *v = g_slice_new0(value);
v->type = VALUE_NONE;
return v;
}
value* value_new_bool(gboolean val) {
value *v = g_slice_new0(value);
v->data.boolean = val;
v->type = VALUE_BOOLEAN;
return v;
}
value* value_new_number(gint64 val) {
value *v = g_slice_new0(value);
v->data.number = val;
v->type = VALUE_NUMBER;
return v;
}
value* value_new_string(GString *val) {
value *v = g_slice_new0(value);
v->data.string = val;
v->type = VALUE_STRING;
return v;
}
value* value_new_range(value_range val) {
value *v = g_slice_new0(value);
v->data.range = val;
v->type = VALUE_RANGE;
return v;
}
value* value_new_list() {
value *v = g_slice_new0(value);
v->data.list = g_ptr_array_new();
v->type = VALUE_LIST;
return v;
}
static void _value_hash_free_key(gpointer data) {
g_string_free((GString*) data, TRUE);
}
static void _value_hash_free_value(gpointer data) {
value_free((value*) data);
}
value* value_new_hash() {
value *v = g_slice_new0(value);
v->data.hash = g_hash_table_new_full(
(GHashFunc) g_string_hash, (GEqualFunc) g_string_equal,
_value_hash_free_key, _value_hash_free_value);
v->type = VALUE_HASH;
return v;
}
value* value_copy(value* val) {
value *n;
switch (val->type) {
case VALUE_NONE: n = value_new_bool(FALSE); n->type = VALUE_NONE; return n; /* hack */
case VALUE_BOOLEAN: return value_new_bool(val->data.boolean);
case VALUE_NUMBER: return value_new_number(val->data.number);
case VALUE_STRING: return value_new_string(g_string_new_len(GSTR_LEN(val->data.string)));
case VALUE_RANGE: return value_new_range(val->data.range);
/* list: we have to copy every value in the list! */
case VALUE_LIST:
n = value_new_list();
g_ptr_array_set_size(n->data.list, val->data.list->len);
for (guint i = 0; i < val->data.list->len; i++) {
g_ptr_array_index(n->data.list, i) = value_copy(g_ptr_array_index(val->data.list, i));
}
return n;
/* hash: iterate over hashtable, clone each value */
case VALUE_HASH:
n = value_new_hash();
{
GHashTableIter iter;
gpointer k, v;
g_hash_table_iter_init(&iter, val->data.hash);
while (g_hash_table_iter_next(&iter, &k, &v))
g_hash_table_insert(n->data.hash, g_string_new_len(GSTR_LEN((GString*)k)), value_copy((value*)v));
}
return n;
}
return NULL;
}
static void value_list_free(GPtrArray *vallist) {
if (!vallist) return;
for (gsize i = 0; i < vallist->len; i++) {
value_free(g_ptr_array_index(vallist, i));
}
g_ptr_array_free(vallist, TRUE);
}
void value_free(value* val) {
if (!val) return;
switch (val->type) {
case VALUE_NONE:
case VALUE_BOOLEAN:
case VALUE_NUMBER:
/* Nothing to free */
break;
case VALUE_STRING:
g_string_free(val->data.string, TRUE);
break;
case VALUE_RANGE:
break;
case VALUE_LIST:
value_list_free(val->data.list);
break;
case VALUE_HASH:
g_hash_table_destroy(val->data.hash);
break;
}
val->type = VALUE_NONE;
g_slice_free(value, val);
}
const char* value_type_string(value_type type) {
switch(type) {
case VALUE_NONE:
return "none";
case VALUE_BOOLEAN:
return "boolean";
case VALUE_NUMBER:
return "number";
case VALUE_STRING:
return "string";
case VALUE_RANGE:
return "range";
case VALUE_LIST:
return "list";
case VALUE_HASH:
return "hash";
}
return "<unknown>";
}
GString *value_to_string(value *val) {
GString *str = NULL;
switch (val->type) {
case VALUE_NONE:
return NULL;
case VALUE_BOOLEAN:
str = g_string_new(val->data.boolean ? "true" : "false");
break;
case VALUE_NUMBER:
str = g_string_sized_new(0);
g_string_printf(str, "%" G_GINT64_FORMAT, val->data.number);
break;
case VALUE_STRING:
str = g_string_new_len(CONST_STR_LEN("\""));
g_string_append_len(str, GSTR_LEN(val->data.string));
g_string_append_c(str, '"');
break;
case VALUE_RANGE:
str = g_string_sized_new(0);
g_string_printf(str, "%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT, val->data.range.from, val->data.range.to);
break;
case VALUE_LIST:
str = g_string_new_len(CONST_STR_LEN("("));
if (val->data.list->len) {
GString *tmp = value_to_string(g_ptr_array_index(val->data.list, 0));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
for (guint i = 1; i < val->data.list->len; i++) {
tmp = value_to_string(g_ptr_array_index(val->data.list, i));
g_string_append_len(str, CONST_STR_LEN(", "));
g_string_append(str, tmp->str);
g_string_free(tmp, TRUE);
}
}
g_string_append_c(str, ')');
break;
case VALUE_HASH:
{
GHashTableIter iter;
gpointer k, v;
GString *tmp;
guint i = 0;
str = g_string_new_len(CONST_STR_LEN("["));
g_hash_table_iter_init(&iter, val->data.hash);
while (g_hash_table_iter_next(&iter, &k, &v)) {
if (i)
g_string_append_len(str, CONST_STR_LEN(", "));
tmp = value_to_string((value*)v);
g_string_append_len(str, GSTR_LEN((GString*)k));
g_string_append_len(str, CONST_STR_LEN(": "));
g_string_append_len(str, GSTR_LEN(tmp));
g_string_free(tmp, TRUE);
i++;
}
g_string_append_c(str, ']');
break;
}
}
return str;
}
Loading…
Cancel
Save