merged from lp
commit
b0615d2198
|
@ -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
|
||||
|
|
11
src/chunk.c
11
src/chunk.c
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
182
src/connection.c
182
src/connection.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ struct connection {
|
|||
|
||||
action_stack action_stack;
|
||||
|
||||
gpointer *options; /* TODO */
|
||||
gpointer *options;
|
||||
|
||||
request request;
|
||||
physical physical;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
31
src/plugin.c
31
src/plugin.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
35
src/plugin.h
35
src/plugin.h
|
@ -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
|
||||
|
|
|
@ -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 |