2
0
Fork 0

merged from lp

personal/stbuehler/wip
Thomas Porzelt 2008-08-09 19:19:18 +02:00
commit b0615d2198
32 changed files with 1107 additions and 233 deletions

View File

@ -7,6 +7,14 @@
#define GSTR_LEN(x) (x) ? (x)->str : "", (x) ? (x)->len : 0
/* we don't use ev_init for now (stupid alias warnings), as ev_init
* just does set some values to zero and calls ev_set_cb.
* But every structure we allacote is initialized with zero, so we don't care
* about that.
* If this ever changes, we can easily use ev_init again.
*/
#define my_ev_init(ev, cb) ev_set_cb(ev, cb)
typedef enum {
HTTP_TRANSFER_ENCODING_IDENTITY,
HTTP_TRANSFER_ENCODING_CHUNKED

View File

@ -10,7 +10,11 @@
static chunkfile *chunkfile_new(GString *name, int fd, gboolean is_temp) {
chunkfile *cf = g_slice_new(chunkfile);
cf->refcount = 1;
cf->name = name;
if (name) {
cf->name = g_string_new_len(GSTR_LEN(name));
} else {
cf->name = NULL;
}
cf->fd = fd;
cf->is_temp = is_temp;
return cf;
@ -29,7 +33,7 @@ static void chunkfile_release(chunkfile *cf) {
cf->fd = -1;
if (cf->is_temp) unlink(cf->name->str);
cf->is_temp = FALSE;
g_string_free(cf->name, TRUE);
if (cf->name) g_string_free(cf->name, TRUE);
cf->name = NULL;
g_slice_free(chunkfile, cf);
}
@ -95,7 +99,7 @@ handler_t chunkiter_read(server *srv, connection *con, chunkiter iter, off_t sta
if ( !(c->file.mmap.data != MAP_FAILED || c->mem) /* no data present */
|| !( /* or in the wrong range */
(start + c->offset >= c->file.mmap.offset)
&& (start + c->offset + length <= c->file.mmap.offset + c->file.mmap.length)) ) {
&& (start + c->offset + length <= c->file.mmap.offset + (ssize_t) c->file.mmap.length)) ) {
/* then find new range */
our_offset = start % MMAP_CHUNK_ALIGN;
our_start = start - our_offset;
@ -278,6 +282,7 @@ void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len) {
static void __chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd, gboolean is_temp) {
chunk *c = chunk_new();
c->type = FILE_CHUNK;
c->file.file = chunkfile_new(filename, fd, is_temp);
c->file.start = start;
c->file.length = length;

View File

@ -47,6 +47,7 @@ static gboolean condition_ip_from_socket(condition_rvalue *val, sock_addr *addr)
condition_lvalue* condition_lvalue_new(cond_lvalue_t type, GString *key) {
condition_lvalue *lvalue = g_slice_new0(condition_lvalue);
if (type == COMP_REQUEST_HEADER) g_string_ascii_down(key);
lvalue->type = type;
lvalue->key = key;
lvalue->refcount = 1;
@ -89,7 +90,7 @@ static condition* cond_new_string(comp_operator_t op, condition_lvalue *lvalue,
static condition* cond_new_match(server *srv, comp_operator_t op, condition_lvalue *lvalue, GString *str) {
UNUSED(op); UNUSED(lvalue); UNUSED(str);
ERROR(srv, "%s", "pcre not supported for now");
/* TODO */
/* TODO: pcre */
return NULL;
}
#endif
@ -237,13 +238,11 @@ const char* cond_lvalue_to_string(cond_lvalue_t t) {
/* COND_VALUE_STRING and COND_VALUE_REGEXP only */
static gboolean condition_check_eval_string(server *srv, connection *con, condition *cond) {
const char *value = "";
GString *tmp = NULL;
gboolean result = FALSE;
UNUSED(srv);
UNUSED(con);
switch (cond->lvalue->type) {
/* TODO: get values */
case COMP_REQUEST_LOCALIP:
value = con->local_addr_str->str;
break;
@ -254,7 +253,7 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
value = con->request.uri.path->str;
break;
case COMP_REQUEST_HOST:
value = con->request.host->str;
value = con->request.uri.host->str;
break;
case COMP_REQUEST_SCHEME:
value = con->is_ssl ? "https" : "http";
@ -266,21 +265,23 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
value = con->request.http_method_str->str;
break;
case COMP_PHYSICAL_PATH:
value = con->physical.path->str;
break;
case COMP_PHYSICAL_PATH_EXISTS:
/* TODO */
/* TODO: physical path exists */
break;
case COMP_REQUEST_HEADER:
/* TODO */
http_header_get_fast(srv->tmp_str, con->request.headers, GSTR_LEN(cond->lvalue->key));
value = srv->tmp_str->str;
break;
case COMP_PHYSICAL_SIZE:
/* TODO */
g_string_printf((tmp = g_string_sized_new(0)), "%"L_GOFFSET_FORMAT, (goffset) 0);
value = tmp->str;
/* TODO: physical size */
g_string_printf(srv->tmp_str, "%"L_GOFFSET_FORMAT, (goffset) 0);
value = srv->tmp_str->str;
break;
case COMP_REQUEST_CONTENT_LENGTH:
/* TODO */
g_string_printf((tmp = g_string_sized_new(0)), "%"L_GOFFSET_FORMAT, (goffset) 0);
value = tmp->str;
g_string_printf(srv->tmp_str, "%"L_GOFFSET_FORMAT, con->request.content_length);
value = srv->tmp_str->str;
break;
}
@ -310,7 +311,6 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
break;
}
if (tmp) g_string_free(tmp, TRUE);
return result;
}
@ -327,6 +327,7 @@ static gboolean condition_check_eval_int(server *srv, connection *con, condition
value = con->physical.size;
break;
default:
CON_ERROR(srv, con, "couldn't get int value for '%s', using -1", cond_lvalue_to_string(cond->lvalue->type));
value = -1;
}
@ -349,8 +350,6 @@ static gboolean condition_check_eval_int(server *srv, connection *con, condition
case CONFIG_COND_NOTIP:
ERROR(srv, "cannot compare int with '%s'", comp_op_to_string(cond->op));
return FALSE;
} else {
ERROR(srv, "couldn't get int value for '%s'", cond_lvalue_to_string(cond->lvalue->type));
}
return FALSE;
@ -408,7 +407,6 @@ static gboolean condition_check_eval_ip(server *srv, connection *con, condition
ipval.type = COND_VALUE_INT;
switch (cond->lvalue->type) {
/* TODO: get values */
case COMP_REQUEST_LOCALIP:
if (!condition_ip_from_socket(&ipval, &con->local_addr))
return (cond->op == CONFIG_COND_NOTIP);
@ -418,10 +416,11 @@ static gboolean condition_check_eval_ip(server *srv, connection *con, condition
return (cond->op == CONFIG_COND_NOTIP);
break;
case COMP_REQUEST_PATH:
value = con->request.uri.path->str;
ERROR(srv, "%s", "Cannot parse request.path as ip");
return (cond->op == CONFIG_COND_NOTIP);
break;
case COMP_REQUEST_HOST:
value = con->request.host->str;
value = con->request.uri.host->str;
break;
case COMP_REQUEST_SCHEME:
ERROR(srv, "%s", "Cannot parse request.scheme as ip");
@ -430,14 +429,17 @@ static gboolean condition_check_eval_ip(server *srv, connection *con, condition
value = con->request.uri.query->str;
break;
case COMP_REQUEST_METHOD:
value = con->request.http_method_str->str;
ERROR(srv, "%s", "Cannot request.method as ip");
return (cond->op == CONFIG_COND_NOTIP);
break;
case COMP_PHYSICAL_PATH:
case COMP_PHYSICAL_PATH_EXISTS:
/* TODO */
ERROR(srv, "%s", "Cannot physical.path(-exists) as ip");
return (cond->op == CONFIG_COND_NOTIP);
break;
case COMP_REQUEST_HEADER:
/* TODO */
http_header_get_fast(srv->tmp_str, con->request.headers, GSTR_LEN(cond->lvalue->key));
value = srv->tmp_str->str;
break;
case COMP_PHYSICAL_SIZE:
case COMP_REQUEST_CONTENT_LENGTH:

View File

@ -51,7 +51,7 @@ typedef enum {
COMP_PHYSICAL_SIZE,
/* needs a key */
COMP_REQUEST_HEADER
COMP_REQUEST_HEADER /**< needs lowercase key, enforced by condition_lvalue_new */
} cond_lvalue_t;
#define COND_LVALUE_FIRST_WITH_KEY COMP_REQUEST_HEADER

View File

@ -28,9 +28,9 @@
gboolean parse_ipv4(const char *str, guint32 *ip, guint32 *netmask) {
guint8 *data = (guint8*) ip;
const char *p = str, *mark;
const char *p = str, *mark = NULL;
gboolean res = FALSE;
int cs, tmpval, i = 0;
int cs, tmpval = 0, i = 0;
%% write init nocs;
@ -92,9 +92,9 @@ gboolean parse_ipv6(const char *str, guint8 *ip, guint *network) {
guint8 data[4] = {0,0,0,0};
guint16 *predata = (guint16*) ip, postdata[8];
size_t prec = 0, postc = 0;
const char *p = str, *mark;
const char *p = str, *mark = NULL;
gboolean res = FALSE, compressed = FALSE;
int cs, tmpval, i = 0;
int cs, tmpval = 0, i = 0;
%% write init nocs;

View File

@ -2,6 +2,7 @@
#include "connection.h"
#include "network.h"
#include "utils.h"
#include "plugin_core.h"
void con_put(server *srv, connection *con); /* server.c */
@ -68,21 +69,24 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
dojoblist = TRUE;
break;
case NETWORK_STATUS_FATAL_ERROR:
CON_ERROR(srv, con, "%s", "network read fatal error");
connection_set_state(srv, con, CON_STATE_ERROR);
dojoblist = TRUE;
break;
case NETWORK_STATUS_CONNECTION_CLOSE:
con->raw_in->is_closed = TRUE;
shutdown(w->fd, SHUT_RD);
connection_set_state(srv, con, CON_STATE_CLOSE);
dojoblist = TRUE;
break;
case NETWORK_STATUS_WAIT_FOR_EVENT:
break;
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
/* TODO ? */
/* TODO: aio */
ev_io_rem_events(loop, w, EV_READ);
break;
case NETWORK_STATUS_WAIT_FOR_FD:
/* TODO */
/* TODO: wait for fd */
ev_io_rem_events(loop, w, EV_READ);
break;
}
@ -96,6 +100,7 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
dojoblist = TRUE;
break;
case NETWORK_STATUS_FATAL_ERROR:
CON_ERROR(srv, con, "%s", "network write fatal error");
connection_set_state(srv, con, CON_STATE_ERROR);
dojoblist = TRUE;
break;
@ -106,18 +111,16 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
case NETWORK_STATUS_WAIT_FOR_EVENT:
break;
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
/* TODO ? */
/* TODO: aio */
ev_io_rem_events(loop, w, EV_WRITE);
break;
case NETWORK_STATUS_WAIT_FOR_FD:
/* TODO */
/* TODO: wait for fd */
ev_io_rem_events(loop, w, EV_WRITE);
break;
}
// CON_TRACE(srv, con, "cq->len: raw_out=%i, out=%i", (int) con->raw_out->length, (int) con->out->length);
}
if (con->raw_out->length == 0) {
// CON_TRACE(srv, con, "%s", "stop write");
ev_io_rem_events(loop, w, EV_WRITE);
dojoblist = TRUE;
}
@ -135,20 +138,24 @@ connection* connection_new(server *srv) {
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
con->raw_in = chunkqueue_new();
con->raw_out = chunkqueue_new();
con->in = chunkqueue_new();
con->out = chunkqueue_new();
ev_io_init(&con->sock.watcher, connection_cb, -1, 0);
my_ev_init(&con->sock.watcher, connection_cb);
ev_io_set(&con->sock.watcher, -1, 0);
con->sock.srv = srv; con->sock.con = con; con->sock.watcher.data = &con->sock;
con->remote_addr_str = g_string_sized_new(0);
con->local_addr_str = g_string_sized_new(0);
con->keep_alive = TRUE;
con->raw_in = chunkqueue_new();
con->raw_out = chunkqueue_new();
con->in = chunkqueue_new();
con->out = chunkqueue_new();
action_stack_init(&con->action_stack);
con->options = g_slice_copy(srv->option_count * sizeof(*srv->option_def_values), srv->option_def_values);
request_init(&con->request, con->raw_in);
physical_init(&con->physical);
response_init(&con->response);
return con;
@ -159,24 +166,31 @@ void connection_reset(server *srv, connection *con) {
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
chunkqueue_reset(con->raw_in);
chunkqueue_reset(con->raw_out);
chunkqueue_reset(con->in);
chunkqueue_reset(con->out);
ev_io_stop(srv->loop, &con->sock.watcher);
if (con->sock.watcher.fd != -1) {
shutdown(con->sock.watcher.fd, SHUT_RDWR);
close(con->sock.watcher.fd);
if (con->raw_in->is_closed) { /* read already shutdown */
shutdown(con->sock.watcher.fd, SHUT_WR);
close(con->sock.watcher.fd);
} else {
server_add_closing_socket(srv, con->sock.watcher.fd);
}
}
ev_io_set(&con->sock.watcher, -1, 0);
g_string_truncate(con->remote_addr_str, 0);
g_string_truncate(con->local_addr_str, 0);
con->keep_alive = TRUE;
chunkqueue_reset(con->raw_in);
chunkqueue_reset(con->raw_out);
chunkqueue_reset(con->in);
chunkqueue_reset(con->out);
action_stack_reset(srv, &con->action_stack);
memcpy(con->options, srv->option_def_values, srv->option_count * sizeof(*srv->option_def_values));
request_reset(&con->request);
physical_reset(&con->physical);
response_reset(&con->response);
}
@ -185,18 +199,21 @@ void connection_reset_keep_alive(server *srv, connection *con) {
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
con->raw_out->is_closed = FALSE;
chunkqueue_reset(con->in);
chunkqueue_reset(con->out);
ev_io_set_events(srv->loop, &con->sock.watcher, EV_READ);
g_string_truncate(con->remote_addr_str, 0);
g_string_truncate(con->local_addr_str, 0);
con->keep_alive = TRUE;
con->raw_out->is_closed = FALSE;
chunkqueue_reset(con->in);
chunkqueue_reset(con->out);
action_stack_reset(srv, &con->action_stack);
memcpy(con->options, srv->option_def_values, srv->option_count * sizeof(*srv->option_def_values));
request_reset(&con->request);
physical_reset(&con->physical);
response_reset(&con->response);
}
@ -205,24 +222,31 @@ void connection_free(server *srv, connection *con) {
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
chunkqueue_free(con->raw_in);
chunkqueue_free(con->raw_out);
chunkqueue_free(con->in);
chunkqueue_free(con->out);
ev_io_stop(srv->loop, &con->sock.watcher);
if (con->sock.watcher.fd != -1) {
shutdown(con->sock.watcher.fd, SHUT_RDWR);
close(con->sock.watcher.fd);
if (con->raw_in->is_closed) { /* read already shutdown */
shutdown(con->sock.watcher.fd, SHUT_WR);
close(con->sock.watcher.fd);
} else {
server_add_closing_socket(srv, con->sock.watcher.fd);
}
}
ev_io_set(&con->sock.watcher, -1, 0);
g_string_free(con->remote_addr_str, TRUE);
g_string_free(con->local_addr_str, TRUE);
con->keep_alive = TRUE;
chunkqueue_free(con->raw_in);
chunkqueue_free(con->raw_out);
chunkqueue_free(con->in);
chunkqueue_free(con->out);
action_stack_clear(srv, &con->action_stack);
g_slice_free1(srv->option_count * sizeof(*srv->option_def_values), con->options);
request_clear(&con->request);
physical_clear(&con->physical);
response_clear(&con->response);
g_slice_free(connection, con);
@ -241,19 +265,21 @@ void connection_state_machine(server *srv, connection *con) {
do {
switch (con->state) {
case CON_STATE_REQUEST_START:
/* TODO: reset some values after keep alive - or do it in CON_STATE_REQUEST_END */
connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER);
action_enter(con, srv->mainaction);
break;
case CON_STATE_READ_REQUEST_HEADER:
// TRACE(srv, "%s", "reading request header");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "reading request header");
}
switch(http_request_parse(srv, con, &con->request.parser_ctx)) {
case HANDLER_FINISHED:
case HANDLER_GO_ON:
connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER);
break;
case HANDLER_WAIT_FOR_FD:
/* TODO */
/* TODO: wait for fd */
done = TRUE;
break;
case HANDLER_WAIT_FOR_EVENT:
@ -266,13 +292,19 @@ void connection_state_machine(server *srv, connection *con) {
break;
}
break;
case CON_STATE_VALIDATE_REQUEST_HEADER:
// TRACE(srv, "%s", "validating request header");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "validating request header");
}
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER);
request_validate_header(srv, con);
break;
case CON_STATE_HANDLE_REQUEST_HEADER:
// TRACE(srv, "%s", "handle request header");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "handle request header");
}
switch (action_execute(srv, con)) {
case ACTION_WAIT_FOR_EVENT:
done = TRUE;
@ -285,17 +317,29 @@ void connection_state_machine(server *srv, connection *con) {
connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE);
break;
case ACTION_ERROR:
/* action return error */
/* TODO: return 500 instead ? */
connection_set_state(srv, con, CON_STATE_ERROR);
internal_error(srv, con);
break;
}
break;
case CON_STATE_READ_REQUEST_CONTENT:
case CON_STATE_HANDLE_RESPONSE_HEADER:
// TRACE(srv, "%s", "read request/handle response header");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "read request/handle response header");
}
if (con->expect_100_cont) {
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "send 100 Continue");
}
chunkqueue_append_mem(con->raw_out, CONST_STR_LEN("HTTP/1.1 100 Continue\r\n\r\n"));
con->expect_100_cont = FALSE;
ev_io_add_events(srv->loop, &con->sock.watcher, EV_WRITE);
}
parse_request_body(srv, con);
/* TODO: call plugin content_handler */
if (con->content_handler)
con->content_handler->handle_content(srv, con, con->content_handler);
switch (action_execute(srv, con)) {
case ACTION_WAIT_FOR_EVENT:
done = TRUE;
@ -305,12 +349,11 @@ void connection_state_machine(server *srv, connection *con) {
connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE);
break;
case ACTION_ERROR:
/* action return error */
/* TODO: return 500 instead ? */
connection_set_state(srv, con, CON_STATE_ERROR);
internal_error(srv, con);
break;
}
break;
case CON_STATE_WRITE_RESPONSE:
if (con->in->is_closed && con->raw_out->is_closed) {
connection_set_state(srv, con, CON_STATE_RESPONSE_END);
@ -319,12 +362,21 @@ void connection_state_machine(server *srv, connection *con) {
if (!con->response_headers_sent) {
con->response_headers_sent = TRUE;
// TRACE(srv, "%s", "write response headers");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "write response headers");
}
response_send_headers(srv, con);
}
// TRACE(srv, "%s", "write response");
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "write response");
}
parse_request_body(srv, con);
/* TODO: call plugin content_handler */
if (con->content_handler)
con->content_handler->handle_content(srv, con, con->content_handler);
forward_response_body(srv, con);
if (con->in->is_closed && con->raw_out->is_closed) {
@ -333,9 +385,14 @@ void connection_state_machine(server *srv, connection *con) {
}
if (con->state == CON_STATE_WRITE_RESPONSE) done = TRUE;
break;
case CON_STATE_RESPONSE_END:
// TRACE(srv, "%s", "response end");
/* TODO: call plugin callbacks */
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "response end (keep_alive = %i)", con->keep_alive);
}
plugins_handle_close(srv, con);
if (con->keep_alive) {
connection_reset_keep_alive(srv, con);
} else {
@ -343,15 +400,25 @@ void connection_state_machine(server *srv, connection *con) {
done = TRUE;
}
break;
case CON_STATE_CLOSE:
// TRACE(srv, "%s", "connection closed");
/* TODO: call plugin callbacks */
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "connection closed");
}
plugins_handle_close(srv, con);
con_put(srv, con);
done = TRUE;
break;
case CON_STATE_ERROR:
// TRACE(srv, "%s", "connection closed (error)");
/* TODO: call plugin callbacks */
if (CORE_OPTION(CORE_OPTION_DEBUG_REQUEST_HANDLING)) {
TRACE(srv, "%s", "connection closed (error)");
}
plugins_handle_close(srv, con);
con_put(srv, con);
done = TRUE;
break;
@ -365,6 +432,13 @@ void connection_handle_direct(server *srv, connection *con) {
}
void connection_handle_indirect(server *srv, connection *con, plugin *p) {
connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT);
con->content_handler = p;
if (!p) {
connection_handle_direct(srv, con);
} else if (p->handle_content) {
connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT);
con->content_handler = p;
} else {
CON_ERROR(srv, con, "Indirect plugin '%s' handler has no handle_content callback", p->name);
internal_error(srv, con);
}
}

View File

@ -78,7 +78,7 @@ struct connection {
action_stack action_stack;
gpointer *options; /* TODO */
gpointer *options;
request request;
physical physical;

View File

@ -1,4 +1,5 @@
#include "base.h"
#include "http_headers.h"
static void _string_free(gpointer p) {
@ -154,3 +155,15 @@ gboolean http_header_is(http_headers *headers, const gchar *key, size_t keylen,
}
return FALSE;
}
void http_header_get_fast(GString *dest, http_headers *headers, const gchar *key, size_t keylen) {
http_header *h = http_header_lookup_fast(headers, key, keylen);
GList *iter;
g_string_truncate(dest, 0);
if (!h) return;
for (iter = g_queue_peek_head_link(&h->values); NULL != iter; iter = g_list_next(iter)) {
if (dest->len) g_string_append_len(dest, CONST_STR_LEN(", "));
g_string_append_len(dest, GSTR_LEN((GString*)iter->data));
}
}

View File

@ -41,4 +41,7 @@ LI_API http_header* http_header_lookup_fast(http_headers *headers, const gchar *
/** Use lowercase keys! values are compared case-insensitive */
LI_API gboolean http_header_is(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen);
/** concats all headers with key with ', ' - empty if no header exists - use lowercase key*/
LI_API void http_header_get_fast(GString *dest, http_headers *headers, const gchar *key, size_t keylen);
#endif

View File

@ -20,7 +20,7 @@
action done { fbreak; }
action method { getStringTo(fpc, ctx->request->http_method_str); }
action uri { getStringTo(fpc, ctx->request->uri.uri_raw); }
action uri { getStringTo(fpc, ctx->request->uri.raw); }
action header_key {
getStringTo(fpc, ctx->h_key);

View File

@ -53,8 +53,6 @@ int main(int argc, char *argv[]) {
srv = server_new();
log_init(srv);
plugin_register(srv, "core", plugin_core_init);
/* if no path is specified for the config, read lighttpd.conf from current directory */
@ -115,19 +113,9 @@ int main(int argc, char *argv[]) {
log_warning(srv, NULL, "test %s", "foo1"); /* duplicate won't be logged */
log_warning(srv, NULL, "test %s", "foo2");
log_debug(srv, NULL, "test %s", "message");
log_thread_start(srv);
server_start(srv);
log_error(srv, NULL, "error %d", 23);
g_atomic_int_set(&srv->rotate_logs, TRUE);
log_warning(srv, NULL, "test %s", "foo3");
log_warning(srv, NULL, "test %s", "foo4");
g_atomic_int_set(&srv->exiting, TRUE);
log_thread_wakeup(srv);
g_thread_join(srv->log_thread);
server_free(srv);
return 0;

View File

@ -16,6 +16,9 @@ LI_API const char *remove_path(const char *path);
#define ERROR(srv, fmt, ...) \
log_write(srv, NULL, "%s.%d: (error) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define INFO(srv, ...) \
log_write(srv, NULL, __VA_ARGS__)
#define TRACE(srv, fmt, ...) \
log_write(srv, NULL, "%s.%d: (trace) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)

View File

@ -2,7 +2,7 @@
#include "network.h"
/** repeats write after EINTR */
static ssize_t net_write(int fd, void *buf, ssize_t nbyte) {
ssize_t net_write(int fd, void *buf, ssize_t nbyte) {
ssize_t r;
while (-1 == (r = write(fd, buf, nbyte))) {
switch (errno) {
@ -18,52 +18,8 @@ static ssize_t net_write(int fd, void *buf, ssize_t nbyte) {
return r;
}
network_status_t network_write(server *srv, connection *con, int fd, chunkqueue *cq) {
const ssize_t blocksize = 16*1024; /* 16k */
const off_t max_write = 16 * blocksize; /* 256k */
char *block_data;
off_t block_len;
ssize_t r;
off_t len = 0;
chunkiter ci;
do {
ci = chunkqueue_iter(cq);
switch (chunkiter_read(srv, con, ci, 0, blocksize, &block_data, &block_len)) {
case HANDLER_GO_ON:
break;
case HANDLER_WAIT_FOR_FD:
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
case HANDLER_ERROR:
default:
return NETWORK_STATUS_FATAL_ERROR;
}
if (-1 == (r = net_write(fd, block_data, block_len))) {
switch (errno) {
case EAGAIN:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK
#endif
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
case ECONNRESET:
return NETWORK_STATUS_CONNECTION_CLOSE;
default:
CON_ERROR(srv, con, "oops, read from fd=%d failed: %s (%d)", fd, strerror(errno), errno );
return NETWORK_STATUS_FATAL_ERROR;
}
} else if (0 == r) {
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
}
chunkqueue_skip(cq, r);
len += r;
} while (r == block_len && len < max_write);
return NETWORK_STATUS_SUCCESS;
}
/** repeats read after EINTR */
static ssize_t net_read(int fd, void *buf, ssize_t nbyte) {
ssize_t net_read(int fd, void *buf, ssize_t nbyte) {
ssize_t r;
while (-1 == (r = read(fd, buf, nbyte))) {
switch (errno) {
@ -79,6 +35,34 @@ static ssize_t net_read(int fd, void *buf, ssize_t nbyte) {
return r;
}
network_status_t network_write(server *srv, connection *con, int fd, chunkqueue *cq) {
network_status_t res;
#ifdef TCP_CORK
int corked = 0;
#endif
#ifdef TCP_CORK
/* Linux: put a cork into the socket as we want to combine the write() calls
* but only if we really have multiple chunks
*/
if (cq->queue->length > 1) {
corked = 1;
setsockopt(fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
}
#endif
res = network_backend_writev(srv, con, fd, cq);
#ifdef TCP_CORK
if (corked) {
corked = 0;
setsockopt(fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
}
#endif
return res;
}
network_status_t network_read(server *srv, connection *con, int fd, chunkqueue *cq) {
const ssize_t blocksize = 16*1024; /* 16k */
const off_t max_read = 16 * blocksize; /* 256k */

View File

@ -4,15 +4,35 @@
#include "base.h"
typedef enum {
NETWORK_STATUS_SUCCESS,
NETWORK_STATUS_SUCCESS, /**< some IO was actually done (read/write) or cq was empty for write */
NETWORK_STATUS_FATAL_ERROR,
NETWORK_STATUS_CONNECTION_CLOSE,
NETWORK_STATUS_WAIT_FOR_EVENT,
NETWORK_STATUS_WAIT_FOR_AIO_EVENT,
NETWORK_STATUS_WAIT_FOR_FD,
NETWORK_STATUS_WAIT_FOR_EVENT, /**< read/write returned -1 with errno=EAGAIN/EWOULDBLOCK; no real IO was done */
NETWORK_STATUS_WAIT_FOR_AIO_EVENT, /**< nothing done yet, read/write will be done somewhere else */
NETWORK_STATUS_WAIT_FOR_FD, /**< need a fd to open a file */
} network_status_t;
/** repeats write after EINTR */
LI_API ssize_t net_write(int fd, void *buf, ssize_t nbyte);
/** repeats read after EINTR */
LI_API ssize_t net_read(int fd, void *buf, ssize_t nbyte);
LI_API network_status_t network_write(server *srv, connection *con, int fd, chunkqueue *cq);
LI_API network_status_t network_read(server *srv, connection *con, int fd, chunkqueue *cq);
/* write backends */
LI_API network_status_t network_backend_write(server *srv, connection *con, int fd, chunkqueue *cq);
LI_API network_status_t network_backend_writev(server *srv, connection *con, int fd, chunkqueue *cq);
#define NETWORK_FALLBACK(f) do { \
network_status_t res; \
switch(res = f(srv, con, fd, cq)) { \
case NETWORK_STATUS_SUCCESS: \
break; \
default: \
return res; \
} \
} while(0)
#endif

49
src/network_write.c Normal file
View File

@ -0,0 +1,49 @@
#include "network.h"
network_status_t network_backend_write(server *srv, connection *con, int fd, chunkqueue *cq) {
const ssize_t blocksize = 16*1024; /* 16k */
const off_t max_write = 16 * blocksize; /* 256k */
char *block_data;
off_t block_len;
ssize_t r;
off_t len = 0;
chunkiter ci;
do {
if (0 == cq->length) return NETWORK_STATUS_SUCCESS;
ci = chunkqueue_iter(cq);
switch (chunkiter_read(srv, con, ci, 0, blocksize, &block_data, &block_len)) {
case HANDLER_GO_ON:
break;
case HANDLER_WAIT_FOR_FD:
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
case HANDLER_ERROR:
default:
return NETWORK_STATUS_FATAL_ERROR;
}
if (-1 == (r = net_write(fd, block_data, block_len))) {
switch (errno) {
case EAGAIN:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK
#endif
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
case ECONNRESET:
case EPIPE:
return NETWORK_STATUS_CONNECTION_CLOSE;
default:
CON_ERROR(srv, con, "oops, write to fd=%d failed: %s (%d)", fd, strerror(errno), errno );
return NETWORK_STATUS_FATAL_ERROR;
}
} else if (0 == r) {
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
}
chunkqueue_skip(cq, r);
len += r;
} while (r == block_len && len < max_write);
return NETWORK_STATUS_SUCCESS;
}

104
src/network_writev.c Normal file
View File

@ -0,0 +1,104 @@
#include "network.h"
#include <sys/uio.h>
#ifndef UIO_MAXIOV
# if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__)
/* FreeBSD 4.7 defines it in sys/uio.h only if _KERNEL is specified */
# define UIO_MAXIOV 1024
# elif defined(__sgi)
/* IRIX 6.5 has sysconf(_SC_IOV_MAX) which might return 512 or bigger */
# define UIO_MAXIOV 512
# elif defined(__sun)
/* Solaris (and SunOS?) defines IOV_MAX instead */
# ifndef IOV_MAX
# define UIO_MAXIOV 16
# else
# define UIO_MAXIOV IOV_MAX
# endif
# elif defined(IOV_MAX)
# define UIO_MAXIOV IOV_MAX
# else
# error UIO_MAXIOV nor IOV_MAX are defined
# endif
#endif
network_status_t network_backend_writev(server *srv, connection *con, int fd, chunkqueue *cq) {
const off_t max_write = 256 * 1024; /* 256k */
off_t min_cq_len, max_chunks_len, we_have;
ssize_t r;
gboolean did_write_something = FALSE;
chunkiter ci;
chunk *c;
GArray *chunks = g_array_sized_new(FALSE, TRUE, sizeof(struct iovec), UIO_MAXIOV);
/* stop if chunkqueue length gets less or equal than min_cq_len */
min_cq_len = cq->length - max_write;
if (min_cq_len < 0) min_cq_len = 0;
do {
if (0 == cq->length) return NETWORK_STATUS_SUCCESS;
ci = chunkqueue_iter(cq);
if (MEM_CHUNK != (c = chunkiter_chunk(ci))->type) {
NETWORK_FALLBACK(network_backend_write);
did_write_something = TRUE;
continue;
}
max_chunks_len = cq->length - min_cq_len;
we_have = 0;
do {
guint i = chunks->len;
off_t len = c->mem->len - c->offset;
struct iovec *v;
g_array_set_size(chunks, i + 1);
v = &g_array_index(chunks, struct iovec, i);
v->iov_base = c->mem->str + c->offset;
if (len > max_write) len = max_write;
v->iov_len = len;
we_have += len;
} while (we_have < max_chunks_len &&
chunkiter_next(&ci) &&
MEM_CHUNK == (c = chunkiter_chunk(ci))->type &&
chunks->len < UIO_MAXIOV);
while (-1 == (r = writev(fd, (struct iovec*) chunks->data, chunks->len))) {
switch (errno) {
case EAGAIN:
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK
#endif
g_array_free(chunks, TRUE);
return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
case ECONNRESET:
case EPIPE:
g_array_free(chunks, TRUE);
return NETWORK_STATUS_CONNECTION_CLOSE;
case EINTR:
break; /* try again */
default:
g_array_free(chunks, TRUE);
CON_ERROR(srv, con, "oops, write to fd=%d failed: %s (%d)", fd, strerror(errno), errno );
return NETWORK_STATUS_FATAL_ERROR;
}
}
if (0 == r) {
g_array_free(chunks, TRUE);
return did_write_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
}
chunkqueue_skip(cq, r);
if (r != we_have) {
g_array_free(chunks, TRUE);
return NETWORK_STATUS_SUCCESS;
}
did_write_something = TRUE;
g_array_set_size(chunks, 0);
} while (cq->length > min_cq_len);
g_array_free(chunks, TRUE);
return NETWORK_STATUS_SUCCESS;
}

View File

@ -122,7 +122,7 @@ void option_list_free(GArray *optlist) {
g_array_free(optlist, TRUE);
}
/* Extract value from option, destroy option */
/* Extract value from option, option set to none */
gpointer option_extract_value(option *opt) {
gpointer val = NULL;
if (!opt) return NULL;
@ -153,6 +153,5 @@ gpointer option_extract_value(option *opt) {
break;
}
opt->type = OPTION_NONE;
g_slice_free(option, opt);
return val;
}

View File

@ -62,7 +62,7 @@ LI_API const char* option_type_string(option_type type);
LI_API void option_list_free(GArray *optlist);
/* Extract value from option, destroy option */
/* Extract value from option, option set to none */
LI_API gpointer option_extract_value(option *opt);
#endif

View File

@ -50,6 +50,11 @@ static void plugin_free_setups(server *srv, plugin *p) {
void plugin_free(server *srv, plugin *p) {
if (!p) return;
if (srv->state == SERVER_RUNNING) {
ERROR(srv, "Cannot free plugin '%s' while server is running", p->name);
return;
}
g_hash_table_remove(srv->plugins, p->name);
plugin_free_options(srv, p);
plugin_free_actions(srv, p);
@ -66,6 +71,11 @@ gboolean plugin_register(server *srv, const gchar *name, PluginInit init) {
return FALSE;
}
if (srv->state != SERVER_STARTING) {
ERROR(srv, "Cannot register plugin '%s' after server was started", name);
return FALSE;
}
if (g_hash_table_lookup(srv->plugins, name)) {
ERROR(srv, "Module '%s' already registered", name);
return FALSE;
@ -75,6 +85,7 @@ gboolean plugin_register(server *srv, const gchar *name, PluginInit init) {
g_hash_table_insert(srv->plugins, (gchar*) p->name, p);
init(srv, p);
p->opt_base_index = g_hash_table_size(srv->options);
if (p->options) {
size_t i;
@ -259,3 +270,23 @@ gboolean call_setup(server *srv, const char *name, option *opt) {
return TRUE;
}
void plugins_prepare_callbacks(server *srv) {
GHashTableIter iter;
plugin *p;
g_hash_table_iter_init(&iter, srv->plugins);
while (g_hash_table_iter_next(&iter, NULL, (gpointer*) &p)) {
if (p->handle_close)
g_array_append_val(srv->plugins_handle_close, p);
}
}
void plugins_handle_close(server *srv, connection *con) {
GArray *a = srv->plugins_handle_close;
guint i, len = a->len;
for (i = 0; i < len; i++) {
plugin *p = g_array_index(a, plugin*, i);
p->handle_close(srv, con, p);
}
}

View File

@ -41,13 +41,31 @@ typedef void (*PluginFreeOption) (server *srv, plugin *p, size_t ndx, gpo
typedef action* (*PluginCreateAction) (server *srv, plugin *p, option *opt);
typedef gboolean (*PluginSetup) (server *srv, plugin *p, option *opt);
typedef void (*PluginHandleContent) (server *srv, connection *con, plugin *p);
typedef void (*PluginHandleClose) (server *srv, connection *con, plugin *p);
struct plugin {
size_t version;
const gchar *name; /**< name of the plugin */
gpointer data; /**< private plugin data */
gpointer data; /**< private plugin data */
PluginFree free; /**< called before plugin is unloaded */
size_t opt_base_index;
PluginFree free; /**< called before plugin is unloaded */
/** called if plugin registered as indirect handler with connection_handle_indirect(srv, con, p)
* - after response headers are created:
* connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER)
* - after content is generated close output queue:
* con->out->is_closed = TRUE
*/
PluginHandleContent handle_content;
/** called for every plugin after connection got closed (response end, reset by peer, error)
* the plugins code must not depend on any order of plugins loaded
*/
PluginHandleClose handle_close;
const plugin_option *options;
const plugin_action *actions;
@ -102,17 +120,28 @@ struct server_setup {
/* Needed my modules to register their plugin(s) */
LI_API gboolean plugin_register(server *srv, const gchar *name, PluginInit init);
/* Internal needed functions */
LI_API void plugin_free(server *srv, plugin *p);
LI_API gboolean parse_option(server *srv, const char *name, option *opt, option_set *mark);
LI_API void release_option(server *srv, option_set *mark); /**< Does not free the option_set memory */
LI_API void plugins_prepare_callbacks(server *srv);
LI_API void plugins_handle_close(server *srv, connection *con);
/* Needed for config frontends */
/** For parsing 'somemod.option = "somevalue"' */
LI_API action* option_action(server *srv, const gchar *name, option *value);
/** For parsing 'somemod.action value', e.g. 'rewrite "/url" => "/destination"' */
/** For parsing 'somemod.action value', e.g. 'rewrite "/url" => "/destination"'
* You need to free the option after it (it should be of type NONE then)
*/
LI_API action* create_action(server *srv, const gchar *name, option *value);
/** For setup function, e.g. 'listen "127.0.0.1:8080"' */
LI_API gboolean call_setup(server *srv, const char *name, option *opt);
/* needs connection *con and plugin *p */
#define OPTION(idx) _OPTION(con, p, idx)
#define _OPTION(con, p, idx) (con->options[p->opt_base_index + idx])
#define _OPTION_ABS(con, idx) (con->options[idx])
#endif

View File

@ -1,11 +1,17 @@
#include "base.h"
#include "plugin_core.h"
static action* core_list(server *srv, plugin* p, option *opt) {
action *a;
guint i;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type == OPTION_ACTION) {
a = opt->value.opt_action.action;
action_acquire(a);
@ -38,6 +44,10 @@ static action* core_when(server *srv, plugin* p, option *opt) {
action *a;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_LIST) {
ERROR(srv, "expected list, got %s", option_type_string(opt->type));
return NULL;
@ -63,11 +73,93 @@ static action* core_when(server *srv, plugin* p, option *opt) {
return a;
}
static action* core_set(server *srv, plugin* p, option *opt) {
option *value, *opt_name;
action *a;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_LIST) {
ERROR(srv, "expected list, got %s", option_type_string(opt->type));
return NULL;
}
if (opt->value.opt_list->len != 2) {
ERROR(srv, "expected list with length 2, has length %u", opt->value.opt_list->len);
return NULL;
}
opt_name = g_array_index(opt->value.opt_list, option*, 0);
value = g_array_index(opt->value.opt_list, option*, 1);
if (opt_name->type != OPTION_STRING) {
ERROR(srv, "expected string as first parameter, got %s", option_type_string(opt_name->type));
return NULL;
}
a = option_action(srv, opt_name->value.opt_string->str, value);
option_free(opt);
return a;
}
static action_result core_handle_physical(server *srv, connection *con, gpointer param) {
GString *docroot = (GString*) param;
UNUSED(srv);
if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON;
g_string_truncate(con->physical.path, 0);
g_string_append_len(con->physical.path, GSTR_LEN(docroot));
g_string_append_len(con->physical.path, GSTR_LEN(con->request.uri.path));
return ACTION_GO_ON;
}
static void core_physical_free(struct server *srv, gpointer param) {
UNUSED(srv);
g_string_free((GString*) param, TRUE);
}
static action* core_physical(server *srv, plugin* p, option *opt) {
UNUSED(p);
GString *docroot;
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_STRING) {
ERROR(srv, "expected string as parameter, got %s", option_type_string(opt->type));
return NULL;
}
docroot = (GString*) option_extract_value(opt);
return action_new_function(core_handle_physical, core_physical_free, docroot);
}
static action_result core_handle_static(server *srv, connection *con, gpointer param) {
UNUSED(param);
/* TODO */
CON_ERROR(srv, con, "%s", "Not implemented yet");
return ACTION_ERROR;
int fd;
if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON;
if (con->physical.path->len == 0) return ACTION_GO_ON;
fd = open(con->physical.path->str, O_RDONLY);
if (fd == -1) {
con->response.http_status = 404;
} else {
struct stat st;
fstat(fd, &st);
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
con->response.http_status = 200;
chunkqueue_append_file_fd(con->out, NULL, 0, st.st_size, fd);
}
connection_handle_direct(srv, con);
return ACTION_GO_ON;
}
static action* core_static(server *srv, plugin* p, option *opt) {
@ -86,7 +178,7 @@ static action_result core_handle_test(server *srv, connection *con, gpointer par
if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON;
con->response.http_status = 200;
chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.uri));
chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.path));
chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n"));
connection_handle_direct(srv, con);
@ -154,18 +246,24 @@ static gboolean core_listen(server *srv, plugin* p, option *opt) {
}
TRACE(srv, "will listen to &#