diff --git a/src/angel.h b/src/angel.h new file mode 100644 index 0000000..ad5e81b --- /dev/null +++ b/src/angel.h @@ -0,0 +1,8 @@ +#ifndef _LIGHTTPD_ANGEL_H_ +#define _LIGHTTPD_ANGEL_H_ + +/* interface to the angel; implementation needs to work without angel too */ + + + +#endif diff --git a/src/chunk.h b/src/chunk.h index 0b9dbc5..0212f22 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -15,9 +15,12 @@ typedef struct chunkqueue chunkqueue; struct chunkiter; typedef struct chunkiter chunkiter; +struct server; +struct connection; + #define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0 -#include "base.h" +#include "settings.h" /* Open a file only once, so it shouldn't get lost; * as a file may get split into many chunks, we @@ -75,7 +78,7 @@ struct chunkiter { /* open the file cf->name if it is not already opened for reading * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -LI_API handler_t chunkfile_open(server *srv, connection *con, chunkfile *cf); +LI_API handler_t chunkfile_open(struct server *srv, struct connection *con, chunkfile *cf); /****************** * chunk iterator * @@ -91,7 +94,7 @@ INLINE goffset chunkiter_length(chunkiter iter); * the data is _not_ marked as "done" * may return HANDLER_GO_ON, HANDLER_ERROR, HANDLER_WAIT_FOR_FD */ -LI_API handler_t chunkiter_read(server *srv, connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len); +LI_API handler_t chunkiter_read(struct server *srv, struct connection *con, chunkiter iter, off_t start, off_t length, char **data_start, off_t *data_len); /****************** * chunk * diff --git a/src/chunk_parser.c b/src/chunk_parser.c index a5c6ec4..6e038b2 100644 --- a/src/chunk_parser.c +++ b/src/chunk_parser.c @@ -1,8 +1,13 @@ +#include "base.h" #include "chunk_parser.h" void chunk_parser_init(chunk_parser_ctx *ctx, chunkqueue *cq) { ctx->cq = cq; + chunk_parser_reset(ctx); +} + +void chunk_parser_reset(chunk_parser_ctx *ctx) { ctx->bytes_in = 0; ctx->curi.element = NULL; ctx->start = 0; diff --git a/src/chunk_parser.h b/src/chunk_parser.h index 498f0dc..cd8f8d2 100644 --- a/src/chunk_parser.h +++ b/src/chunk_parser.h @@ -30,12 +30,13 @@ struct chunk_parser_mark { }; LI_API void chunk_parser_init(chunk_parser_ctx *ctx, chunkqueue *cq); +LI_API void chunk_parser_reset(chunk_parser_ctx *ctx); LI_API handler_t chunk_parser_prepare(chunk_parser_ctx *ctx); -LI_API handler_t chunk_parser_next(server *srv, connection *con, chunk_parser_ctx *ctx, char **p, char **pe); +LI_API handler_t chunk_parser_next(struct server *srv, struct connection *con, chunk_parser_ctx *ctx, char **p, char **pe); LI_API void chunk_parser_done(chunk_parser_ctx *ctx, goffset len); -LI_API gboolean chunk_extract_to(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest); -LI_API GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to); +LI_API gboolean chunk_extract_to(struct server *srv, struct connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest); +LI_API GString* chunk_extract(struct server *srv, struct connection *con, chunk_parser_mark from, chunk_parser_mark to); INLINE chunk_parser_mark chunk_parser_getmark(chunk_parser_ctx *ctx, const char *fpc); diff --git a/src/connection.c b/src/connection.c new file mode 100644 index 0000000..a47890f --- /dev/null +++ b/src/connection.c @@ -0,0 +1,127 @@ + +#include "connection.h" +#include "network.h" +#include "log.h" + +static void parse_request_body(server *srv, connection *con) { + if (con->state == CON_STATE_HANDLE_RESPONSE && !con->in->is_closed) { + /* TODO: parse chunked encoded request body */ + if (con->in->bytes_in < con->request.content_length) { + chunkqueue_steal_len(con->in, con->raw_in, con->request.content_length - con->in->bytes_in); + if (con->in->bytes_in == con->request.content_length) con->in->is_closed = TRUE; + } else if (con->request.content_length == -1) { + chunkqueue_steal_all(con->in, con->raw_in); + } + } +} + +static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) { + connection_socket *con_sock = (connection_socket*) w->data; + server *srv = con_sock->srv; + connection *con = con_sock->con; + + if (revents && EV_READ) { + if (con->in->is_closed) { + /* don't read the next request before current one is done */ + ev_io_set(w, w->fd, w->events && ~EV_READ); + } else { + switch(network_read(srv, con, w->fd, con->raw_in)) { + case NETWORK_STATUS_SUCCESS: + parse_request_body(srv, con); + joblist_append(srv, con); + break; + case NETWORK_STATUS_FATAL_ERROR: + connection_set_state(srv, con, CON_STATE_ERROR); + joblist_append(srv, con); + break; + case NETWORK_STATUS_CONNECTION_CLOSE: + connection_set_state(srv, con, CON_STATE_CLOSE); + joblist_append(srv, con); + break; + case NETWORK_STATUS_WAIT_FOR_EVENT: + break; + case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: + /* TODO ? */ + ev_io_set(w, w->fd, w->events && ~EV_READ); + break; + case NETWORK_STATUS_WAIT_FOR_FD: + /* TODO */ + break; + } + } + } + + if (revents && EV_WRITE) { + if (con->raw_out->length > 0) { + network_write(srv, con, w->fd, con->raw_out); + joblist_append(srv, con); + } + if (con->raw_out->length == 0) { + ev_io_set(w, w->fd, w->events && ~EV_WRITE); + } + } +} + +connection* connection_new(server *srv) { + connection *con = g_slice_new0(connection); + UNUSED(srv); + + con->raw_in = chunkqueue_new(); + con->raw_out = chunkqueue_new(); + con->in = chunkqueue_new(); + con->out = chunkqueue_new(); + + con->sock.srv = srv; con->sock.con = con; con->sock.watcher.data = con; + ev_io_init(&con->sock.watcher, connection_cb, -1, 0); + con->remote_addr_str = g_string_sized_new(0); + con->local_addr_str = g_string_sized_new(0); + + action_stack_init(&con->action_stack); + + request_init(&con->request, con->raw_in); + + return con; +} + +void connection_reset(server *srv, connection *con) { + 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); + close(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); + + action_stack_reset(srv, &con->action_stack); + + request_reset(&con->request); +} + +void connection_free(server *srv, connection *con) { + 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); + close(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); + + action_stack_clear(srv, &con->action_stack); + + request_clear(&con->request); + + g_slice_free(connection, con); +} + +void connection_set_state(server *srv, connection *con, connection_state_t state) { +} + +void connection_state_machine(server *srv, connection *con) { + +} diff --git a/src/connection.h b/src/connection.h index ff13b3d..c68ed20 100644 --- a/src/connection.h +++ b/src/connection.h @@ -1,23 +1,54 @@ #ifndef _LIGHTTPD_CONNECTION_H_ #define _LIGHTTPD_CONNECTION_H_ +#include "base.h" + +typedef enum { + CON_STATE_REQUEST_START, /** after the connect, the request is initialized, keep-alive starts here again */ + CON_STATE_READ_REQUEST_HEADER, /** loop in the read-request-header until the full header is received */ + CON_STATE_VALIDATE_REQUEST_HEADER, /** validate the request-header */ + CON_STATE_HANDLE_RESPONSE, /** find a handler for the request */ + CON_STATE_RESPONSE_END, /** successful request, connection closed */ + CON_STATE_ERROR, /** fatal error, connection closed */ + CON_STATE_CLOSE /** connection reset by peer */ +} connection_state_t; + +struct connection_socket; +typedef struct connection_socket connection_socket; + +struct connection_socket { + server *srv; + connection *con; + ev_io watcher; +}; + struct connection { + guint idx; /** index in connection table */ + connection_state_t state; + + chunkqueue *raw_in, *raw_out; + chunkqueue *in, *out; + connection_socket sock; sock_addr remote_addr, local_addr; GString *remote_addr_str, *local_addr_str; gboolean is_ssl; action_stack action_stack; - gpointer *options; + gpointer *options; /* TODO */ request request; physical physical; - GMutex *mutex; - struct log_t *log; gint log_level; }; +LI_API connection* connection_new(server *srv); +LI_API void connection_reset(server *srv, connection *con); +LI_API void connection_free(server *srv, connection *con); + +LI_API void connection_set_state(server *srv, connection *con, connection_state_t state); + #endif diff --git a/src/http_headers.c b/src/http_headers.c index b86e5e7..82e7d80 100644 --- a/src/http_headers.c +++ b/src/http_headers.c @@ -13,6 +13,10 @@ http_headers* http_headers_new() { return headers; } +void http_headers_reset(http_headers* headers) { + g_hash_table_remove_all(headers->table); +} + void http_headers_free(http_headers* headers) { if (!headers) return; g_hash_table_destroy(headers->table); diff --git a/src/http_headers.h b/src/http_headers.h index 1bbbbd0..45e13a2 100644 --- a/src/http_headers.h +++ b/src/http_headers.h @@ -14,6 +14,7 @@ struct http_headers { /* strings alweays get copied, so you should free key and value yourself */ LI_API http_headers* http_headers_new(); +LI_API void http_headers_reset(http_headers* headers); LI_API void http_headers_free(http_headers* headers); /** If header does not exist, just insert normal header. If it exists, append (", %s", value) */ diff --git a/src/http_request_parser.h b/src/http_request_parser.h index dc41adf..08e0106 100644 --- a/src/http_request_parser.h +++ b/src/http_request_parser.h @@ -5,20 +5,24 @@ struct http_request_ctx; typedef struct http_request_ctx http_request_ctx; #include "chunk_parser.h" -#include "request.h" + +struct request; struct http_request_ctx { chunk_parser_ctx chunk_ctx; - request *request; + struct request *request; chunk_parser_mark mark; GString *h_key, *h_value; }; -LI_API http_request_ctx* http_request_parser_new(request *req, chunkqueue *cq); -LI_API void http_request_parser_free(http_request_ctx *ctx); +#include "request.h" + +LI_API void http_request_parser_init(http_request_ctx* ctx, request *req, chunkqueue *cq); +LI_API void http_request_parser_reset(http_request_ctx* ctx); +LI_API void http_request_parser_clear(http_request_ctx *ctx); -LI_API handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx); +LI_API handler_t http_request_parse(struct server *srv, struct connection *con, http_request_ctx *ctx); #endif diff --git a/src/http_request_parser.rl b/src/http_request_parser.rl index 38068df..7901e73 100644 --- a/src/http_request_parser.rl +++ b/src/http_request_parser.rl @@ -1,4 +1,5 @@ +#include "base.h" #include "http_request_parser.h" /** Machine **/ @@ -125,25 +126,26 @@ static int http_request_parser_is_finished(http_request_ctx *ctx) { return ctx->chunk_ctx.cs >= http_request_parser_first_final; } -http_request_ctx* http_request_parser_new(request *req, chunkqueue *cq) { - http_request_ctx *ctx = g_slice_new0(http_request_ctx); - - %% write init; +void http_request_parser_init(http_request_ctx* ctx, request *req, chunkqueue *cq) { chunk_parser_init(&ctx->chunk_ctx, cq); ctx->request = req; ctx->h_key = g_string_sized_new(0); ctx->h_value = g_string_sized_new(0); - return ctx; + %% write init; } -void http_request_parser_free(http_request_ctx *ctx) { - if (!ctx) return; +void http_request_parser_reset(http_request_ctx* ctx) { + chunk_parser_reset(&ctx->chunk_ctx); + g_string_truncate(ctx->h_key, 0); + g_string_truncate(ctx->h_value, 0); + %% write init; +} + +void http_request_parser_clear(http_request_ctx *ctx) { g_string_free(ctx->h_key, TRUE); g_string_free(ctx->h_value, TRUE); - - g_slice_free(http_request_ctx, ctx); } handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx) { diff --git a/src/log.c b/src/log.c index a8f3a58..32de73f 100644 --- a/src/log.c +++ b/src/log.c @@ -206,7 +206,7 @@ log_t *log_new(server *srv, log_type_t type, GString *path) { if (log != NULL) { g_atomic_int_inc(&log->refcount); - g_mutex_unlock(srv->mutex); + g_mutex_unlock(srv->log_mutex); return log; } diff --git a/src/network.c b/src/network.c new file mode 100644 index 0000000..4dc5081 --- /dev/null +++ b/src/network.c @@ -0,0 +1,114 @@ + +#include "network.h" + +/** repeats write after EINTR */ +static ssize_t net_write(int fd, void *buf, ssize_t nbyte) { + ssize_t r; + while (-1 == (r = write(fd, buf, nbyte))) { + switch (errno) { + case EINTR: + /* Try again */ + break; + default: + /* report error */ + return r; + } + } + /* return bytes written */ + 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; + 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 == blocksize && 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 r; + while (-1 == (r = read(fd, buf, nbyte))) { + switch (errno) { + case EINTR: + /* Try again */ + break; + default: + /* report error */ + return r; + } + } + /* return bytes read */ + return r; +} + +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 */ + ssize_t r; + off_t len; + + do { + GString *buf = g_string_sized_new(blocksize); + if (-1 == (r = net_read(fd, buf->str, blocksize))) { + g_string_free(buf, TRUE); + 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) { + g_string_free(buf, TRUE); + return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_CONNECTION_CLOSE; + } + g_string_truncate(buf, r); + chunkqueue_append_string(cq, buf); + len += r; + } while (r == blocksize && len < max_read); + + return NETWORK_STATUS_SUCCESS; +} diff --git a/src/network.h b/src/network.h new file mode 100644 index 0000000..f76a820 --- /dev/null +++ b/src/network.h @@ -0,0 +1,18 @@ +#ifndef _LIGHTTPD_NETWORK_H_ +#define _LIGHTTPD_NETWORK_H_ + +#include "base.h" + +typedef enum { + NETWORK_STATUS_SUCCESS, + 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_t; + +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); + +#endif diff --git a/src/request.c b/src/request.c index a3f2049..ef90719 100644 --- a/src/request.c +++ b/src/request.c @@ -1,9 +1,8 @@ +#include "base.h" #include "request.h" -request* request_new() { - request *req = g_slice_new0(request); - +void request_init(request *req, chunkqueue *in) { req->http_method = HTTP_METHOD_UNSET; req->http_method_str = g_string_sized_new(0); req->http_version = HTTP_VERSION_UNSET; @@ -19,10 +18,43 @@ request* request_new() { req->host = g_string_sized_new(0); req->content_length = -1; - return req; + http_request_parser_init(&req->parser_ctx, req, in); } -void request_free(request *req) { - /* TODO */ - g_slice_free(request, req); +void request_reset(request *req) { + req->http_method = HTTP_METHOD_UNSET; + g_string_truncate(req->http_method_str, 0); + req->http_version = HTTP_VERSION_UNSET; + + g_string_truncate(req->uri.uri, 0); + g_string_truncate(req->uri.orig_uri, 0); + g_string_truncate(req->uri.scheme, 0); + g_string_truncate(req->uri.path, 0); + g_string_truncate(req->uri.query, 0); + + http_headers_reset(req->headers); + + g_string_truncate(req->host, 0); + req->content_length = -1; + + http_request_parser_reset(&req->parser_ctx); +} + +void request_clear(request *req) { + req->http_method = HTTP_METHOD_UNSET; + g_string_free(req->http_method_str, TRUE); + req->http_version = HTTP_VERSION_UNSET; + + g_string_free(req->uri.uri, TRUE); + g_string_free(req->uri.orig_uri, TRUE); + g_string_free(req->uri.scheme, TRUE); + g_string_free(req->uri.path, TRUE); + g_string_free(req->uri.query, TRUE); + + http_headers_free(req->headers); + + g_string_free(req->host, TRUE); + req->content_length = -1; + + http_request_parser_clear(&req->parser_ctx); } diff --git a/src/request.h b/src/request.h index 7674f77..81d888e 100644 --- a/src/request.h +++ b/src/request.h @@ -44,6 +44,7 @@ struct physical; typedef struct physical physical; #include "http_headers.h" +#include "http_request_parser.h" struct request_uri { GString *uri, *orig_uri; @@ -76,9 +77,12 @@ struct request { /* Parsed headers: */ GString *host; goffset content_length; + + http_request_ctx parser_ctx; }; -LI_API request* request_new(); -LI_API void request_free(request *req); +LI_API void request_init(request *req, chunkqueue *in); +LI_API void request_reset(request *req); +LI_API void request_clear(request *req); #endif diff --git a/src/server.c b/src/server.c index 605097a..429f25b 100644 --- a/src/server.c +++ b/src/server.c @@ -1,6 +1,6 @@ #include "base.h" -#include "log.h" +#include "utils.h" static void server_option_free(gpointer _so) { g_slice_free(server_option, _so); @@ -16,12 +16,27 @@ static void server_setup_free(gpointer _ss) { server* server_new() { server* srv = g_slice_new0(server); + + srv->magic = LIGHTTPD_SERVER_MAGIC; + srv->state = SERVER_STARTING; + + srv->loop = ev_default_loop (0); + if (!srv->loop) { + fatal ("could not initialise libev, bad $LIBEV_FLAGS in environment?"); + } + + srv->connections_active = 0; + srv->connections = g_array_new(FALSE, TRUE, sizeof(connection*)); + srv->sockets = g_array_new(FALSE, TRUE, sizeof(server_socket*)); + srv->plugins = g_hash_table_new(g_str_hash, g_str_equal); srv->options = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_option_free); srv->actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_action_free); srv->setups = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, server_setup_free); - srv->mutex = g_mutex_new(); - srv->mainaction = action_new_list(); + + srv->mainaction = NULL; + + srv->exiting = FALSE; return srv; } @@ -34,7 +49,6 @@ void server_free(server* srv) { g_hash_table_destroy(srv->actions); g_hash_table_destroy(srv->setups); g_hash_table_destroy(srv->plugins); - g_mutex_free(srv->mutex); action_release(srv, srv->mainaction); @@ -52,3 +66,109 @@ void server_free(server* srv) { g_slice_free(server, srv); } + +static connection* con_get(server *srv) { + connection *con; + if (srv->connections_active >= srv->connections->len) { + con = connection_new(srv); + con->idx = srv->connections_active++; + g_array_append_val(srv->connections, con); + } else { + con = g_array_index(srv->connections, connection*, srv->connections_active++); + } + return con; +} + +static void con_put(server *srv, connection *con) { + connection_reset(srv, con); + srv->connections_active--; + if (con->idx != srv->connections_active) { + /* Swap [con->idx] and [srv->connections_active] */ + connection *tmp; + assert(con->idx < srv->connections_active); /* con must be an active connection) */ + tmp = g_array_index(srv->connections, connection*, srv->connections_active); + tmp->idx = con->idx; + con->idx = srv->connections_active; + g_array_index(srv->connections, connection*, con->idx) = con; + g_array_index(srv->connections, connection*, tmp->idx) = tmp; + } +} + +static void server_listen_cb(struct ev_loop *loop, ev_io *w, int revents) { + server_socket *sock = (server_socket*) w->data; + server *srv = sock->srv; + int s; + sock_addr remote_addr; + socklen_t l = sizeof(remote_addr); + UNUSED(loop); + UNUSED(revents); + + while (-1 != (s = accept(w->fd, (struct sockaddr*) &remote_addr, &l))) { + connection *con = con_get(srv); + con->remote_addr = remote_addr; + ev_io_set(&con->sock.watcher, s, EV_READ); + ev_io_start(srv->loop, &con->sock.watcher); + } + +#ifdef _WIN32 + errno = WSAGetLastError(); +#endif + + switch (errno) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EINTR: + /* we were stopped _before_ we had a connection */ + case ECONNABORTED: /* this is a FreeBSD thingy */ + /* we were stopped _after_ we had a connection */ + break; + + case EMFILE: /* we are out of FDs */ + /* TODO: server_out_of_fds(srv, NULL); */ + break; + default: + ERROR(srv, "accept failed on fd=%d with error: (%d) %s", w->fd, errno, strerror(errno)); + break; + } +} + +void server_listen(server *srv, int fd) { + server_socket *sock; + + sock = g_slice_new0(server_socket); + sock->srv = srv; + sock->watcher.data = sock; + fd_init(fd); + ev_io_init(&sock->watcher, server_listen_cb, fd, EV_READ); + if (srv->state == SERVER_RUNNING) ev_io_start(srv->loop, &sock->watcher); + + g_array_append_val(srv->sockets, sock); +} + +void server_start(server *srv) { + guint i; + if (srv->state == SERVER_STOPPING || srv->state == SERVER_RUNNING) return; /* no restart after stop */ + srv->state = SERVER_RUNNING; + + for (i = 0; i < srv->sockets->len; i++) { + server_socket *sock = g_array_index(srv->sockets, server_socket*, i); + ev_io_start(srv->loop, &sock->watcher); + } +} + +void server_stop(server *srv) { + guint i; + if (srv->state == SERVER_STOPPING) return; + srv->state = SERVER_STOPPING; + + for (i = 0; i < srv->sockets->len; i++) { + server_socket *sock = g_array_index(srv->sockets, server_socket*, i); + ev_io_stop(srv->loop, &sock->watcher); + } +} + +void joblist_append(server *srv, connection *con) { + /* TODO */ +} diff --git a/src/server.h b/src/server.h index 9ddcf35..7378a74 100644 --- a/src/server.h +++ b/src/server.h @@ -1,23 +1,45 @@ #ifndef _LIGHTTPD_SERVER_H_ #define _LIGHTTPD_SERVER_H_ +#ifndef LIGHTTPD_SERVER_MAGIC +#define LIGHTTPD_SERVER_MAGIC ((guint)0x12AB34CD) +#endif + +typedef enum { + SERVER_STARTING, /** start up: don't write log files, don't accept connections */ + SERVER_RUNNING, /** running: write logs, accept connections */ + SERVER_STOPPING /** stopping: flush logs, don't accept new connections */ +} server_state; + +struct server_socket; +typedef struct server_socket server_socket; + +struct server_socket { + server *srv; + ev_io watcher; +}; struct server { - guint version; + guint32 magic; /** server magic version, check against LIGHTTPD_SERVER_MAGIC in plugins */ + server_state state; - GHashTable *plugins; + struct ev_loop *loop; + guint connections_active; /** 0..con_act-1: active connections, con_act..used-1: free connections */ + GArray *connections; /** array of (connection*) */ + GArray *sockets; /** array of (server_socket*) */ + + GHashTable *plugins; /**< const gchar* => (plugin*) */ /* registered by plugins */ - GHashTable *options; /**< const gchar* => server_option* */ - GHashTable *actions; /**< const gchar* => server_action* */ - GHashTable *setups; /**< const gchar* => server_setup* */ + GHashTable *options; /**< const gchar* => (server_option*) */ + GHashTable *actions; /**< const gchar* => (server_action*) */ + GHashTable *setups; /**< const gchar* => (server_setup*) */ - gpointer *option_def_values; + gpointer *option_def_values; /* TODO */ struct action *mainaction; gboolean exiting; - GMutex *mutex; /* general mutex for accessing the various members */ /* logs */ gboolean rotate_logs; @@ -30,7 +52,13 @@ struct server { }; -server* server_new(); -void server_free(server* srv); +LI_API server* server_new(); +LI_API void server_free(server* srv); + +LI_API void server_listen(server *srv, int fd); + +LI_API void server_start(server *srv); + +LI_API void joblist_append(server *srv, connection *con); #endif diff --git a/src/tests.c b/src/tests.c index cab7ddc..02b60ed 100644 --- a/src/tests.c +++ b/src/tests.c @@ -9,14 +9,11 @@ int request_test() { chunkqueue *cq; - request *req; - http_request_ctx *ctx; + request req; handler_t res; cq = chunkqueue_new(); - req = request_new(); - - ctx = http_request_parser_new(req, cq); + request_init(&req, cq); chunkqueue_append_mem(cq, CONST_STR_LEN( "GET / HTTP/1.1\r\n" @@ -25,15 +22,16 @@ int request_test() { "abc" )); - res = http_request_parse(NULL, NULL, ctx); + res = http_request_parse(NULL, NULL, &req.parser_ctx); if (res != HANDLER_GO_ON) { fprintf(stderr, "Parser return %i", res); } - assert(req->http_method == HTTP_METHOD_GET); + assert(req.http_method == HTTP_METHOD_GET); assert(cq->length == 3); - http_request_parser_free(ctx); + request_clear(&req); + chunkqueue_free(cq); return res == HANDLER_GO_ON ? 0 : 1; } diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..4f242c5 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,25 @@ + +#include "utils.h" + +#include +#include + +void fatal(const gchar* msg) { + fprintf(stderr, "%s\n", msg); + abort(); +} + +void fd_init(int fd) { +#ifdef _WIN32 + int i = 1; +#endif +#ifdef FD_CLOEXEC + /* close fd on exec (cgi) */ + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +#ifdef O_NONBLOCK + fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR); +#elif defined _WIN32 + ioctlsocket(fd, FIONBIO, &i); +#endif +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..9acecbc --- /dev/null +++ b/src/utils.h @@ -0,0 +1,11 @@ +#ifndef _LIGHTTPD_UTILS_H_ +#define _LIGHTTPD_UTILS_H_ + +#include "settings.h" + +LI_API void fatal(const gchar* msg); + +/* set O_NONBLOCK and FD_CLOEXEC */ +LI_API void fd_init(int fd); + +#endif diff --git a/src/wscript b/src/wscript index ed57f51..d0b90ba 100644 --- a/src/wscript +++ b/src/wscript @@ -14,15 +14,18 @@ common_source=''' condition.c condition_parsers.rl config_parser.rl + connection.c http_headers.c http_request_parser.rl log.c + network.c options.c plugin.c request.c server.c sys-files.c sys-socket.c + utils.c plugin_core.c ''' @@ -143,7 +146,7 @@ def build(bld): if env['LIB_lua']: tests.source += common_source_lua tests.target = 'tests' - tests.uselib += 'lighty dl openssl pcre lua ' + common_uselib + tests.uselib += 'lighty dl ev openssl pcre lua ' + common_uselib def configure(conf): env = conf.env