2
0
Fork 0

merged from lp

personal/stbuehler/wip
Thomas Porzelt 2008-08-05 18:45:03 +02:00
commit 1844a53dd3
22 changed files with 712 additions and 73 deletions

8
src/angel.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _LIGHTTPD_ANGEL_H_
#define _LIGHTTPD_ANGEL_H_
/* interface to the angel; implementation needs to work without angel too */
#endif

View File

@ -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 *

View File

@ -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;
@ -29,7 +34,7 @@ handler_t chunk_parser_next(server *srv, connection *con, chunk_parser_ctx *ctx,
/* Wait at the end of the last chunk if it gets extended */
if (!chunkiter_next(&i)) return HANDLER_WAIT_FOR_EVENT;
ctx->curi = i;
ctx->start = 0;
ctx->start -= l;
}
if (NULL == ctx->curi.element) return HANDLER_WAIT_FOR_EVENT;
@ -48,8 +53,9 @@ void chunk_parser_done(chunk_parser_ctx *ctx, goffset len) {
ctx->start += len;
}
GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to) {
GString *str = g_string_sized_new(0);
gboolean chunk_extract_to(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to, GString *dest) {
g_string_set_size(dest, 0);
chunk_parser_mark i;
for ( i = from; i.ci.element != to.ci.element; chunkiter_next(&i.ci) ) {
goffset len = chunkiter_length(i.ci);
@ -57,7 +63,7 @@ GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chu
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, len - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
g_string_append_len(dest, buf, we_have);
i.pos += we_have;
}
i.pos = 0;
@ -66,13 +72,20 @@ GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chu
char *buf;
off_t we_have;
if (HANDLER_GO_ON != chunkiter_read(srv, con, i.ci, i.pos, to.pos - i.pos, &buf, &we_have)) goto error;
g_string_append_len(str, buf, we_have);
g_string_append_len(dest, buf, we_have);
i.pos += we_have;
}
return str;
return TRUE;
error:
g_string_assign(dest, "");
return FALSE;
}
GString* chunk_extract(server *srv, connection *con, chunk_parser_mark from, chunk_parser_mark to) {
GString *str = g_string_sized_new(0);
if (chunk_extract_to(srv, con, from, to, str)) return str;
g_string_free(str, TRUE);
return NULL;
}

View File

@ -30,11 +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 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);

127
src/connection.c Normal file
View File

@ -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) {
}

View File

@ -1,23 +1,54 @@
#ifndef _LIGHTTPD_CONNECTION_H_
#define _LIGHTTPD_CONNECTION_H_
struct connection {
#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

View File

@ -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);

View File

@ -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) */

View File

@ -5,18 +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;
struct request *request;
chunk_parser_mark mark;
request *request;
GString *h_key, *h_value;
};
LI_API void http_request_parser_init(http_request_ctx *ctx, request *req, chunkqueue *cq);
LI_API handler_t http_request_parse(server *srv, connection *con, 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(struct server *srv, struct connection *con, http_request_ctx *ctx);
#endif

View File

@ -1,4 +1,5 @@
#include "base.h"
#include "http_request_parser.h"
/** Machine **/
@ -6,6 +7,10 @@
#define _getString(M, FPC) (chunk_extract(srv, con, ctx->M, GETMARK(FPC)))
#define getString(FPC) _getString(mark, FPC)
#define _getStringTo(M, FPC, s) (chunk_extract_to(srv, con, ctx->M, GETMARK(FPC), s))
#define getStringTo(FPC, s) _getStringTo(mark, FPC, s)
%%{
machine http_request_parser;
@ -14,8 +19,19 @@
action mark { ctx->mark = GETMARK(fpc); }
action done { fbreak; }
action method { ctx->request->http_method_str = getString(fpc); }
action uri { ctx->request->uri.uri = getString(fpc); }
action method { getStringTo(fpc, ctx->request->http_method_str); }
action uri { getStringTo(fpc, ctx->request->uri.uri); }
action header_key {
getStringTo(fpc, ctx->h_key);
g_string_truncate(ctx->h_value, 0);
}
action header_value {
getStringTo(fpc, ctx->h_value);
}
action header {
http_header_insert(ctx->request->headers, ctx->h_key, ctx->h_value);
}
# RFC 2616
OCTET = any;
@ -48,7 +64,10 @@
QDText = TEXT - DQUOTE;
Quoted_String = DQUOTE ( QDText | Quoted_Pair )* DQUOTE;
HTTP_Version = "HTTP" "/" DIGIT+ "." DIGIT+;
HTTP_Version = (
"HTTP/1.0"
| "HTTP/1.1"
| "HTTP" "/" DIGIT+ "." DIGIT+ );
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
# RFC 2396
@ -62,13 +81,37 @@
Path_Segments = Segment ("/" Segment)*;
Abs_Path = "/" Path_Segments;
Method = Token >mark %method;
Request_URI = ("*" | ( any - CTL - SP )) >mark %uri;
Method = (
"GET" %{ ctx->request->http_method = HTTP_METHOD_GET; }
| "POST" %{ ctx->request->http_method = HTTP_METHOD_POST; }
| "HEAD" %{ ctx->request->http_method = HTTP_METHOD_HEAD; }
| "OPTIONS" %{ ctx->request->http_method = HTTP_METHOD_OPTIONS; }
| "PROPFIND" %{ ctx->request->http_method = HTTP_METHOD_PROPFIND; }
| "MKCOL" %{ ctx->request->http_method = HTTP_METHOD_MKCOL; }
| "PUT" %{ ctx->request->http_method = HTTP_METHOD_PUT; }
| "DELETE" %{ ctx->request->http_method = HTTP_METHOD_DELETE; }
| "COPY" %{ ctx->request->http_method = HTTP_METHOD_COPY; }
| "MOVE" %{ ctx->request->http_method = HTTP_METHOD_MOVE; }
| "PROPPATCH" %{ ctx->request->http_method = HTTP_METHOD_PROPPATCH; }
| "REPORT" %{ ctx->request->http_method = HTTP_METHOD_REPORT; }
| "CHKECOUT" %{ ctx->request->http_method = HTTP_METHOD_CHECKOUT; }
| "CHECKIN" %{ ctx->request->http_method = HTTP_METHOD_CHECKIN; }
| "VERSION-CONTROL" %{ ctx->request->http_method = HTTP_METHOD_VERSION_CONTROL; }
| "UNCHECKOUT" %{ ctx->request->http_method = HTTP_METHOD_UNCHECKOUT; }
| "MKACTIVITY" %{ ctx->request->http_method = HTTP_METHOD_MKACTIVITY; }
| "MERGE" %{ ctx->request->http_method = HTTP_METHOD_MERGE; }
| "LOCK" %{ ctx->request->http_method = HTTP_METHOD_LOCK; }
| "UNLOCK" %{ ctx->request->http_method = HTTP_METHOD_UNLOCK; }
| "LABEL" %{ ctx->request->http_method = HTTP_METHOD_LABEL; }
| "CONNECT" %{ ctx->request->http_method = HTTP_METHOD_CONNECT; }
| Token ) >mark >{ ctx->request->http_method = HTTP_METHOD_UNSET; } %method;
Request_URI = ("*" | ( any - CTL - SP )+) >mark %uri;
Request_Line = Method " " Request_URI " " HTTP_Version CRLF;
Field_Content = ( TEXT+ | ( Token | Separators | Quoted_String )+ );
Field_Value = " "* (Field_Content+ ( Field_Content | LWS )*)? >mark;
Message_Header = Token ":" Field_Value?;
Field_Value = " "* (Field_Content+ ( Field_Content | LWS )*)? >mark %header_value;
Message_Header = Token >mark %header_key ":" Field_Value? % header;
main := (CRLF)* Request_Line (Message_Header CRLF)* CRLF @ done;
}%%
@ -83,15 +126,33 @@ static int http_request_parser_is_finished(http_request_ctx *ctx) {
return ctx->chunk_ctx.cs >= http_request_parser_first_final;
}
void http_request_parser_init(http_request_ctx *ctx, request *req, chunkqueue *cq) {
%% 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);
%% write init;
}
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);
}
handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx) {
handler_t res;
if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON;
if (HANDLER_GO_ON != (res = chunk_parser_prepare(&ctx->chunk_ctx))) return res;
while (!http_request_parser_has_error(ctx) && !http_request_parser_is_finished(ctx)) {
@ -101,10 +162,13 @@ handler_t http_request_parse(server *srv, connection *con, http_request_ctx *ctx
%% write exec;
chunk_parser_done(&ctx->chunk_ctx, pe - p);
chunk_parser_done(&ctx->chunk_ctx, p - ctx->chunk_ctx.buf);
}
if (http_request_parser_has_error(ctx)) return HANDLER_ERROR;
if (http_request_parser_is_finished(ctx)) return HANDLER_GO_ON;
if (http_request_parser_is_finished(ctx)) {
chunkqueue_skip(ctx->chunk_ctx.cq, ctx->chunk_ctx.bytes_in);
return HANDLER_GO_ON;
}
return HANDLER_ERROR;
}

114
src/network.c Normal file
View File

@ -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;
}

18
src/network.h Normal file
View File

@ -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

View File

@ -1,12 +1,60 @@
#include "base.h"
#include "request.h"
request* request_new() {
request *req = g_slice_new0(request);
return req;
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;
req->uri.uri = g_string_sized_new(0);
req->uri.orig_uri = g_string_sized_new(0);
req->uri.scheme = g_string_sized_new(0);
req->uri.path = g_string_sized_new(0);
req->uri.query = g_string_sized_new(0);
req->headers = http_headers_new();
req->host = g_string_sized_new(0);
req->content_length = -1;
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);
}

View File

@ -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

View File

@ -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,11 +16,28 @@ 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->mainaction = NULL;
srv->exiting = FALSE;
return srv;
}
@ -49,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 */
}

View File

@ -1,19 +1,42 @@
#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;
@ -29,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

View File

@ -134,13 +134,14 @@
#endif
typedef enum { HANDLER_UNSET,
HANDLER_GO_ON,
HANDLER_FINISHED,
HANDLER_COMEBACK,
HANDLER_WAIT_FOR_EVENT,
HANDLER_ERROR,
HANDLER_WAIT_FOR_FD
typedef enum {
HANDLER_UNSET,
HANDLER_GO_ON,
HANDLER_FINISHED,
HANDLER_COMEBACK,
HANDLER_WAIT_FOR_EVENT,
HANDLER_ERROR,
HANDLER_WAIT_FOR_FD
} handler_t;
/* Shared library support */

View File

@ -9,7 +9,7 @@
#ifndef FD_SETSIZE
/* By default this is 64 */
#define FD_SETSIZE 4096
#endif
#endif /* FD_SETSIZE */
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <wspiapi.h>
@ -30,7 +30,7 @@
#define STDERR_FILENO 2
#ifndef __MINGW32__
#define ssize_t int
#endif
#endif /* __MINGW32__ */
#define sockread( fd, buf, bytes ) recv( fd, buf, bytes, 0 )
@ -39,7 +39,7 @@ int inet_aton(const char *cp, struct in_addr *inp);
#define HAVE_INET_ADDR
#undef HAVE_INET_ATON
#else
#else /* _WIN32 */
#include <sys/types.h> /* required by netinet/tcp.h on FreeBSD */
#include <sys/socket.h>
#include <sys/ioctl.h>
@ -51,7 +51,7 @@ int inet_aton(const char *cp, struct in_addr *inp);
#ifndef SUN_LEN
#define SUN_LEN(su) \
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
#endif /* SUN_LEN */
#define sockread( fd, buf, bytes ) read( fd, buf, bytes )
#define closesocket(x) close(x)
@ -63,8 +63,8 @@ int inet_aton(const char *cp, struct in_addr *inp);
/* only define it if it isn't defined yet */
#ifndef HAVE_IPV6
#define HAVE_IPV6
#endif
#endif
#endif /* HAVE_IPV6 */
#endif /* HAVE_INET_NTOP */
typedef union {
#ifdef HAVE_IPV6

View File

@ -9,27 +9,31 @@
int request_test() {
chunkqueue *cq;
request *req;
http_request_ctx ctx;
request req;
handler_t res;
cq = chunkqueue_new();
req = request_new();
http_request_parser_init(&ctx, req, cq);
request_init(&req, cq);
chunkqueue_append_mem(cq, CONST_STR_LEN(
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"\r\n"
"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);
return -1;
}
return 0;
assert(req.http_method == HTTP_METHOD_GET);
assert(cq->length == 3);
request_clear(&req);
chunkqueue_free(cq);
return res == HANDLER_GO_ON ? 0 : 1;
}
int main() {
@ -47,10 +51,10 @@ int main() {
s = ipv6_tostring(ipv6);
printf("parsed ipv6: %s/%u\n", s->str, network);
request_test();
return 0;
srv = server_new();
return request_test();
return 0;
}

25
src/utils.c Normal file
View File

@ -0,0 +1,25 @@
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
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
}

11
src/utils.h Normal file
View File

@ -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

View File

@ -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