merged from lp

personal/stbuehler/wip
Thomas Porzelt 15 years ago

@ -55,14 +55,14 @@ action *action_new_setting(option_set setting) {
return a;
}
action *action_new_function(ActionFunc func, ActionFree free, gpointer param) {
action *action_new_function(ActionFunc func, ActionFree ffree, gpointer param) {
action *a;
a = g_slice_new(action);
a->refcount = 1;
a->type = ACTION_TFUNCTION;
a->value.function.func = func;
a->value.function.free = free;
a->value.function.free = ffree;
a->value.function.param = param;
return a;

@ -3,9 +3,9 @@
#include "settings.h"
#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
#define CONST_STR_LEN(x) (x), (x) ? sizeof(x) - 1 : 0
#define GSTR_LEN(x) x ? x->str : "", x ? x->len : 0
#define GSTR_LEN(x) (x) ? (x)->str : "", (x) ? (x)->len : 0
typedef enum {
HTTP_TRANSFER_ENCODING_IDENTITY,

@ -254,7 +254,9 @@ void chunkqueue_free(chunkqueue *cq) {
* you may modify the data (not the length) if you are sure it isn't sent before.
*/
void chunkqueue_append_string(chunkqueue *cq, GString *str) {
chunk *c = chunk_new();
chunk *c;
if (!str->len) return;
c = chunk_new();
c->type = MEM_CHUNK;
c->mem = str;
g_queue_push_tail(cq->queue, c);
@ -264,7 +266,9 @@ void chunkqueue_append_string(chunkqueue *cq, GString *str) {
/* memory gets copied */
void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len) {
chunk *c = chunk_new();
chunk *c;
if (!len) return;
c = chunk_new();
c->type = MEM_CHUNK;
c->mem = g_string_new_len(mem, len);
g_queue_push_tail(cq->queue, c);
@ -284,23 +288,27 @@ static void __chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t st
}
/* pass ownership of filename, do not free it */
void chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length) {
__chunkqueue_append_file(cq, filename, start, length, -1, FALSE);
if (length)
__chunkqueue_append_file(cq, filename, start, length, -1, FALSE);
}
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_file_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd) {
__chunkqueue_append_file(cq, filename, start, length, fd, FALSE);
if (length)
__chunkqueue_append_file(cq, filename, start, length, fd, FALSE);
}
/* temp files get deleted after usage */
/* pass ownership of filename, do not free it */
void chunkqueue_append_tempfile(chunkqueue *cq, GString *filename, off_t start, off_t length) {
__chunkqueue_append_file(cq, filename, start, length, -1, TRUE);
if (length)
__chunkqueue_append_file(cq, filename, start, length, -1, TRUE);
}
/* if you already opened the file, you can pass the fd here - do not close it */
void chunkqueue_append_tempfile_fd(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd) {
__chunkqueue_append_file(cq, filename, start, length, fd, TRUE);
if (length)
__chunkqueue_append_file(cq, filename, start, length, fd, TRUE);
}
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
@ -404,8 +412,7 @@ goffset chunkqueue_skip(chunkqueue *cq, goffset length) {
goffset bytes = 0;
goffset we_have;
while ( (NULL != (c = chunkqueue_first_chunk(cq))) && length > 0 ) {
we_have = chunk_length(c);
while ( (NULL != (c = chunkqueue_first_chunk(cq))) && (0 == (we_have = chunk_length(c)) || length > 0) ) {
if (we_have <= length) {
/* skip (delete) complete chunk */
chunk_free(c);

@ -18,8 +18,6 @@ typedef struct chunkiter chunkiter;
struct server;
struct connection;
#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
#include "settings.h"
/* Open a file only once, so it shouldn't get lost;

@ -45,10 +45,6 @@ static gboolean condition_ip_from_socket(condition_rvalue *val, sock_addr *addr)
return FALSE;
}
// static condition* condition_new(comp_operator_t op, condition_lvalue *lvalue);
// static condition* cond_new_string(comp_operator_t op, condition_lvalue *lvalue, GString *str);
// static void condition_free(condition *c);
condition_lvalue* condition_lvalue_new(cond_lvalue_t type, GString *key) {
condition_lvalue *lvalue = g_slice_new0(condition_lvalue);
lvalue->type = type;
@ -290,10 +286,10 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
switch (cond->op) {
case CONFIG_COND_EQ:
result = 0 == strcmp(value, cond->rvalue.string->str);
result = (0 == strcmp(value, cond->rvalue.string->str));
break;
case CONFIG_COND_NE:
result = 0 != strcmp(value, cond->rvalue.string->str);
result = (0 != strcmp(value, cond->rvalue.string->str));
break;
case CONFIG_COND_MATCH:
case CONFIG_COND_NOMATCH:

@ -18,13 +18,14 @@ static option* lua_params_to_option(server *srv, lua_State *L) {
return option_from_lua(srv, L);
default:
opt = option_new_list();
g_array_set_size(opt->value.opt_list, lua_gettop(L));
while (lua_gettop(L) > 0) {
if (NULL == (subopt = option_from_lua(srv, L))) {
ERROR(srv, "%s", "Couldn't convert option to lua");
option_free(opt);
return NULL;
}
g_array_append_val(opt->value.opt_list, subopt);
g_array_index(opt->value.opt_list, option*, lua_gettop(L)) = subopt;
}
return opt;
}

@ -61,7 +61,7 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
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);
ev_io_rem_events(loop, w, EV_READ);
} else {
switch (network_read(srv, con, w->fd, con->raw_in)) {
case NETWORK_STATUS_SUCCESS:
@ -79,12 +79,10 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
break;
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
/* TODO ? */
CON_TRACE(srv, con, "%s", "stop read");
ev_io_rem_events(loop, w, EV_READ);
break;
case NETWORK_STATUS_WAIT_FOR_FD:
/* TODO */
CON_TRACE(srv, con, "%s", "stop read");
ev_io_rem_events(loop, w, EV_READ);
break;
}
@ -93,12 +91,33 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
if (revents & EV_WRITE) {
if (con->raw_out->length > 0) {
network_write(srv, con, w->fd, con->raw_out);
CON_TRACE(srv, con, "cq->len: raw_out=%i, out=%i", (int) con->raw_out->length, (int) con->out->length);
dojoblist = TRUE;
switch (network_write(srv, con, w->fd, con->raw_out)) {
case NETWORK_STATUS_SUCCESS:
dojoblist = TRUE;
break;
case NETWORK_STATUS_FATAL_ERROR:
connection_set_state(srv, con, CON_STATE_ERROR);
dojoblist = TRUE;
break;
case NETWORK_STATUS_CONNECTION_CLOSE:
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 ? */
ev_io_rem_events(loop, w, EV_WRITE);
break;
case NETWORK_STATUS_WAIT_FOR_FD:
/* TODO */
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");
// CON_TRACE(srv, con, "%s", "stop write");
ev_io_rem_events(loop, w, EV_WRITE);
dojoblist = TRUE;
}
@ -114,6 +133,7 @@ connection* connection_new(server *srv) {
con->state = CON_STATE_REQUEST_START;
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
con->raw_in = chunkqueue_new();
con->raw_out = chunkqueue_new();
@ -137,6 +157,7 @@ connection* connection_new(server *srv) {
void connection_reset(server *srv, connection *con) {
con->state = CON_STATE_REQUEST_START;
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
chunkqueue_reset(con->raw_in);
chunkqueue_reset(con->raw_out);
@ -162,9 +183,8 @@ void connection_reset(server *srv, connection *con) {
void connection_reset_keep_alive(server *srv, connection *con) {
con->state = CON_STATE_REQUEST_START;
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
// chunkqueue_reset(con->raw_in);
// chunkqueue_reset(con->raw_out);
con->raw_out->is_closed = FALSE;
chunkqueue_reset(con->in);
chunkqueue_reset(con->out);
@ -183,6 +203,7 @@ void connection_reset_keep_alive(server *srv, connection *con) {
void connection_free(server *srv, connection *con) {
con->state = CON_STATE_REQUEST_START;
con->response_headers_sent = FALSE;
con->expect_100_cont = FALSE;
chunkqueue_free(con->raw_in);
chunkqueue_free(con->raw_out);
@ -225,7 +246,7 @@ void connection_state_machine(server *srv, connection *con) {
action_enter(con, srv->mainaction);
break;
case CON_STATE_READ_REQUEST_HEADER:
TRACE(srv, "%s", "reading request header");
// TRACE(srv, "%s", "reading request header");
switch(http_request_parse(srv, con, &con->request.parser_ctx)) {
case HANDLER_FINISHED:
case HANDLER_GO_ON:
@ -246,12 +267,12 @@ void connection_state_machine(server *srv, connection *con) {
}
break;
case CON_STATE_VALIDATE_REQUEST_HEADER:
TRACE(srv, "%s", "validating request header");
// 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");
// TRACE(srv, "%s", "handle request header");
switch (action_execute(srv, con)) {
case ACTION_WAIT_FOR_EVENT:
done = TRUE;
@ -272,7 +293,7 @@ void connection_state_machine(server *srv, connection *con) {
break;
case CON_STATE_READ_REQUEST_CONTENT:
case CON_STATE_HANDLE_RESPONSE_HEADER:
TRACE(srv, "%s", "read request/handle response header");
// TRACE(srv, "%s", "read request/handle response header");
parse_request_body(srv, con);
/* TODO: call plugin content_handler */
switch (action_execute(srv, con)) {
@ -298,10 +319,10 @@ void connection_state_machine(server *srv, connection *con) {
if (!con->response_headers_sent) {
con->response_headers_sent = TRUE;
TRACE(srv, "%s", "write response headers");
// TRACE(srv, "%s", "write response headers");
response_send_headers(srv, con);
}
TRACE(srv, "%s", "write response");
// TRACE(srv, "%s", "write response");
parse_request_body(srv, con);
/* TODO: call plugin content_handler */
forward_response_body(srv, con);
@ -313,7 +334,7 @@ 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");
// TRACE(srv, "%s", "response end");
/* TODO: call plugin callbacks */
if (con->keep_alive) {
connection_reset_keep_alive(srv, con);
@ -323,13 +344,13 @@ void connection_state_machine(server *srv, connection *con) {
}
break;
case CON_STATE_CLOSE:
TRACE(srv, "%s", "connection closed");
// TRACE(srv, "%s", "connection closed");
/* TODO: call plugin callbacks */
con_put(srv, con);
done = TRUE;
break;
case CON_STATE_ERROR:
TRACE(srv, "%s", "connection closed (error)");
// TRACE(srv, "%s", "connection closed (error)");
/* TODO: call plugin callbacks */
con_put(srv, con);
done = TRUE;

@ -66,7 +66,7 @@ struct connection_socket {
struct connection {
guint idx; /** index in connection table */
connection_state_t state;
gboolean response_headers_sent;
gboolean response_headers_sent, expect_100_cont;
chunkqueue *raw_in, *raw_out;
chunkqueue *in, *out;

@ -5,11 +5,31 @@ static void _string_free(gpointer p) {
g_string_free((GString*) p, TRUE);
}
static void _string_queue_string_free(gpointer data, gpointer userdata) {
UNUSED(userdata);
g_string_free((GString*) data, TRUE);
}
static void _http_header_free(gpointer p) {
http_header *h = (http_header*) p;
g_queue_foreach(&h->values, _string_queue_string_free, NULL);
g_queue_clear(&h->values);
g_string_free(h->key, TRUE);
g_slice_free(http_header, h);
}
static http_header* _http_header_new(const gchar *key, size_t keylen) {
http_header *h = g_slice_new0(http_header);
g_queue_init(&h->values);
h->key = g_string_new_len(key, keylen);
return h;
}
http_headers* http_headers_new() {
http_headers* headers = g_slice_new0(http_headers);
headers->table = g_hash_table_new_full(
(GHashFunc) g_string_hash, (GEqualFunc) g_string_equal,
_string_free, _string_free);
_string_free, _http_header_free);
return headers;
}
@ -27,24 +47,25 @@ void http_headers_free(http_headers* headers) {
*/
static void header_insert(http_headers *headers, GString *lokey,
const gchar *key, size_t keylen, const gchar *value, size_t valuelen) {
GString *newval = g_string_sized_new(keylen + valuelen + 2);
g_string_append_len(newval, key, keylen);
g_string_append_len(newval, ": ", 2);
g_string_append_len(newval, value, valuelen);
http_header *h = _http_header_new(key, keylen);
g_queue_push_tail(&h->values, g_string_new_len(value, valuelen));
g_hash_table_insert(headers->table, lokey, newval);
g_hash_table_insert(headers->table, lokey, h);
}
/** If header does not exist, just insert normal header. If it exists, append (", %s", value) */
void http_header_append(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen) {
GString *lokey, *tval;
http_header *h;
lokey = g_string_new_len(key, keylen);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
h = (http_header*) g_hash_table_lookup(headers->table, lokey);
if (NULL == h) {
header_insert(headers, lokey, key, keylen, value, valuelen);
} else if (NULL == (tval = g_queue_peek_tail(&h->values))) {
g_string_free(lokey, TRUE);
g_queue_push_tail(&h->values, g_string_new_len(value, valuelen));
} else {
g_string_free(lokey, TRUE);
g_string_append_len(tval, ", ", 2);
@ -54,41 +75,42 @@ void http_header_append(http_headers *headers, const gchar *key, size_t keylen,
/** If header does not exist, just insert normal header. If it exists, append ("\r\n%s: %s", key, value) */
void http_header_insert(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen) {
GString *lokey, *tval;
GString *lokey;
http_header *h;
lokey = g_string_new_len(key, keylen);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
h = (http_header*) g_hash_table_lookup(headers->table, lokey);
if (NULL == h) {
header_insert(headers, lokey, key, keylen, value, valuelen);
} else {
g_string_free(lokey, TRUE);
g_string_append_len(tval, "\r\n", 2);
g_string_append_len(tval, key, keylen);
g_string_append_len(tval, ": ", 2);
g_string_append_len(tval, value, valuelen);
g_queue_push_tail(&h->values, g_string_new_len(value, valuelen));
}
}
/** If header does not exist, just insert normal header. If it exists, overwrite the value */
void http_header_overwrite(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen) {
GString *lokey, *tval;
GString *lokey;
http_header *h;
lokey = g_string_new_len(key, keylen);
g_string_ascii_down(lokey);
tval = (GString*) g_hash_table_lookup(headers->table, lokey);
if (!tval) {
h = (http_header*) g_hash_table_lookup(headers->table, lokey);
if (NULL == h) {
header_insert(headers, lokey, key, keylen, value, valuelen);
} else {
g_string_free(lokey, TRUE);
g_string_truncate(tval, 0);
g_string_append_len(tval, key, keylen);
g_string_append_len(tval, ": ", 2);
g_string_append_len(tval, value, valuelen);
g_string_truncate(h->key, 0);
g_string_append_len(h->key, key, keylen);
/* kill old headers */
g_queue_foreach(&h->values, _string_queue_string_free, NULL);
/* new header */
g_queue_push_tail(&h->values, g_string_new_len(value, valuelen));
}
}
LI_API gboolean http_header_remove(http_headers *headers, const gchar *key, size_t keylen) {
gboolean http_header_remove(http_headers *headers, const gchar *key, size_t keylen) {
GString *lokey;
gboolean res;
@ -98,3 +120,37 @@ LI_API gboolean http_header_remove(http_headers *headers, const gchar *key, size
g_string_free(lokey, TRUE);
return res;
}
http_header* http_header_lookup(http_headers *headers, const gchar *key, size_t keylen) {
GString *lokey;
http_header *h;
lokey = g_string_new_len(key, keylen);
g_string_ascii_down(lokey);
h = (http_header*) g_hash_table_lookup(headers->table, lokey);
g_string_free(lokey, TRUE);
return h;
}
http_header* http_header_lookup_fast(http_headers *headers, const gchar *key, size_t keylen) {
GString lokey;
http_header *h;
/* Fake GString */
lokey.str = (gchar*) key;
lokey.len = lokey.allocated_len = keylen;
h = (http_header*) g_hash_table_lookup(headers->table, &lokey);
return h;
}
gboolean http_header_is(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen) {
http_header *h = http_header_lookup_fast(headers, key, keylen);
GList *iter;
UNUSED(valuelen);
if (!h) return FALSE;
for (iter = g_queue_peek_head_link(&h->values); NULL != iter; iter = g_list_next(iter)) {
if (0 == strcasecmp( ((GString*)iter->data)->str, value )) return TRUE;
}
return FALSE;
}

@ -1,13 +1,21 @@
#ifndef _LIGHTTPD_HTTP_HEADERS_H_
#define _LIGHTTPD_HTTP_HEADERS_H_
struct http_header;
typedef struct http_header http_header;
struct http_headers;
typedef struct http_headers http_headers;
#include "settings.h"
struct http_header {
GString *key;
GQueue values; /**< queue of GString* */
};
struct http_headers {
/** keys are lowercase header name, values contain the complete header */
/** keys are lowercase header name (GString*), values are http_header* */
GHashTable *table;
};
@ -25,4 +33,12 @@ LI_API void http_header_insert(http_headers *headers, const gchar *key, size_t k
LI_API void http_header_overwrite(http_headers *headers, const gchar *key, size_t keylen, const gchar *value, size_t valuelen);
LI_API gboolean http_header_remove(http_headers *headers, const gchar *key, size_t keylen);
LI_API http_header* http_header_lookup(http_headers *headers, const gchar *key, size_t keylen);
/** Use lowercase keys! */
LI_API http_header* http_header_lookup_fast(http_headers *headers, const gchar *key, size_t keylen);
/** 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);
#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); }
action uri { getStringTo(fpc, ctx->request->uri.uri_raw); }
action header_key {
getStringTo(fpc, ctx->h_key);
@ -65,9 +65,9 @@
Quoted_String = DQUOTE ( QDText | Quoted_Pair )* DQUOTE;
HTTP_Version = (
"HTTP/1.0"
| "HTTP/1.1"
| "HTTP" "/" DIGIT+ "." DIGIT+ );
"HTTP/1.0" %{ ctx->request->http_version = HTTP_VERSION_1_0; }
| "HTTP/1.1" %{ ctx->request->http_version = HTTP_VERSION_1_1; }
| "HTTP" "/" DIGIT+ "." DIGIT+ ) >{ ctx->request->http_version = HTTP_VERSION_UNSET; };
#HTTP_URL = "http:" "//" Host ( ":" Port )? ( abs_path ( "?" query )? )?;
# RFC 2396

@ -110,7 +110,7 @@ int main(int argc, char *argv[]) {
TRACE(srv, "%s", "Test!");
//srv->log_stderr = log_new(srv, LOG_TYPE_FILE, g_string_new("lightytest.log"));
/* srv->log_stderr = log_new(srv, LOG_TYPE_FILE, g_string_new("lightytest.log")); */
log_write_(srv, NULL, LOG_LEVEL_WARNING, "test %s", "foo1");
log_warning(srv, NULL, "test %s", "foo1"); /* duplicate won't be logged */
log_warning(srv, NULL, "test %s", "foo2");

@ -24,7 +24,7 @@ network_status_t network_write(server *srv, connection *con, int fd, chunkqueue
char *block_data;
off_t block_len;
ssize_t r;
off_t len;
off_t len = 0;
chunkiter ci;
do {
@ -57,7 +57,7 @@ network_status_t network_write(server *srv, connection *con, int fd, chunkqueue
}
chunkqueue_skip(cq, r);
len += r;
} while (r == blocksize && len < max_write);
} while (r == block_len && len < max_write);
return NETWORK_STATUS_SUCCESS;
}
@ -108,7 +108,6 @@ network_status_t network_read(server *srv, connection *con, int fd, chunkqueue *
}
g_string_truncate(buf, r);
chunkqueue_append_string(cq, buf);
CON_TRACE(srv, con, "read (%i) '%s'", (int) r, buf->str);
len += r;
} while (r == blocksize && len < max_read);

@ -48,14 +48,16 @@ static action* core_when(server *srv, plugin* p, option *opt) {
}
opt_cond = g_array_index(opt->value.opt_list, option*, 0);
opt_act = g_array_index(opt->value.opt_list, option*, 1);
if (opt_act->type != OPTION_ACTION) {
ERROR(srv, "expected action as second parameter, got %s", option_type_string(opt->type));
if (opt_cond->type != OPTION_CONDITION) {
ERROR(srv, "expected condition as first parameter, got %s", option_type_string(opt_cond->type));
return NULL;
}
if (opt_cond->type != OPTION_CONDITION) {
ERROR(srv, "expected condition as first parameter, got %s", option_type_string(opt->type));
if (opt_act->type != OPTION_ACTION) {
ERROR(srv, "expected action as second parameter, got %s", option_type_string(opt_act->type));
return NULL;
}
condition_acquire(opt_cond->value.opt_cond.cond);
action_acquire(opt_act->value.opt_action.action);
a = action_new_condition(opt_cond->value.opt_cond.cond, opt_act->value.opt_action.action);
option_free(opt);
return a;
@ -84,10 +86,10 @@ 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_string(con->out, con->request.uri.uri);
chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.uri));
chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n"));
connection_handle_direct(srv, con);
CON_ERROR(srv, con, "%s", "Not implemented yet");
return ACTION_GO_ON;
}

@ -8,6 +8,7 @@ void request_init(request *req, chunkqueue *in) {
req->uri.uri = g_string_sized_new(0);
req->uri.orig_uri = g_string_sized_new(0);
req->uri.uri_raw = 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);
@ -27,6 +28,7 @@ void request_reset(request *req) {
g_string_truncate(req->uri.uri, 0);
g_string_truncate(req->uri.orig_uri, 0);
g_string_truncate(req->uri.uri_raw, 0);
g_string_truncate(req->uri.scheme, 0);
g_string_truncate(req->uri.path, 0);
g_string_truncate(req->uri.query, 0);
@ -46,6 +48,7 @@ void request_clear(request *req) {
g_string_free(req->uri.uri, TRUE);
g_string_free(req->uri.orig_uri, TRUE);
g_string_free(req->uri.uri_raw, TRUE);
g_string_free(req->uri.scheme, TRUE);
g_string_free(req->uri.path, TRUE);
g_string_free(req->uri.query, TRUE);
@ -58,18 +61,122 @@ void request_clear(request *req) {
http_request_parser_clear(&req->parser_ctx);
}
/* closes connection after response */
static void bad_request(server *srv, connection *con, int status) {
con->keep_alive = FALSE;
con->response.http_status = status;
connection_handle_direct(srv, con);
}
void request_validate_header(server *srv, connection *con) {
request *req = &con->request;
response *resp = &con->response;
http_header *hh;
switch (req->http_version) {
case HTTP_VERSION_1_0:
if (!http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("keep-alive")))
con->keep_alive = 0;
break;
case HTTP_VERSION_1_1:
if (http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("close")))
con->keep_alive = 0;
break;
case HTTP_VERSION_UNSET:
bad_request(srv, con, 505); /* Version not Supported */
return;
}
if (req->uri.uri_raw->len == 0) {
bad_request(srv, con, 400); /* bad request */
return;
}
/* get hostname */
hh = http_header_lookup_fast(req->headers, CONST_STR_LEN("host"));
if ((!hh && req->http_version == HTTP_VERSION_1_1) || (hh && hh->values.length != 1)) {
bad_request(srv, con, 400); /* bad request */
return;
} else if (hh) {
g_string_append_len(req->host, GSTR_LEN((GString*) g_queue_peek_head(&hh->values)));
// CON_TRACE(srv, con, "hostname: '%s'", req->host->str);
}
/* content-length */
hh = http_header_lookup_fast(req->headers, CONST_STR_LEN("content-length"));
if (hh) {
GString *val = (GString*) g_queue_peek_head(&hh->values);
off_t r;
char *err;
r = str_to_off_t(val->str, &err, 10);
if (*err != '\0') {
CON_TRACE(srv, con, "content-length is not a number: %s (Status: 400)", err);
bad_request(srv, con, 400); /* bad request */
return;
}
/**
* negative content-length is not supported
* and is a bad request
*/
if (r < 0) {
bad_request(srv, con, 400); /* bad request */
return;
}
/**
* check if we had a over- or underrun in the string conversion
*/
if (r == STR_OFF_T_MIN ||
r == STR_OFF_T_MAX) {
if (errno == ERANGE) {
bad_request(srv, con, 413); /* Request Entity Too Large */
return;
}
}
con->request.content_length = r;
}
/* Expect: 100-continue */
hh = http_header_lookup_fast(req->headers, CONST_STR_LEN("expect"));
if (hh) {
GList *iter;
gboolean expect_100_cont = FALSE;
for (iter = g_queue_peek_head_link(&hh->values); NULL != iter; iter = g_list_next(iter)) {
if (0 == strcasecmp( ((GString*)iter->data)->str, "100-continue" )) {
expect_100_cont = TRUE;
} else {
/* we only support 100-continue */
bad_request(srv, con, 417); /* Expectation Failed */
return;
}
}
if (expect_100_cont && req->http_version == HTTP_VERSION_1_0) {
/* only HTTP/1.1 clients can send us this header */
bad_request(srv, con, 417); /* Expectation Failed */
return;
}
con->expect_100_cont = expect_100_cont;
}
/* TODO: headers:
* - If-Modified-Since (different duplicate check)
* - If-None-Match (different duplicate check)
* - Range (duplicate check)
*/
switch(con->request.http_method) {
case HTTP_METHOD_GET:
case HTTP_METHOD_HEAD:
/* content-length is forbidden for those */
if (con->request.content_length > 0) {
/* content-length is missing */
CON_ERROR(srv, con, "%s", "GET/HEAD with content-length -> 400");
con->keep_alive = FALSE;
con->response.http_status = 400;
connection_handle_direct(srv, con);
bad_request(srv, con, 400); /* bad request */
return;
}
con->request.content_length = 0;
@ -80,9 +187,7 @@ void request_validate_header(server *srv, connection *con) {
/* content-length is missing */
CON_ERROR(srv, con, "%s", "POST-request, but content-length missing -> 411");
con->keep_alive = FALSE;
con->response.http_status = 411;
connection_handle_direct(srv, con);
bad_request(srv, con, 411); /* Length Required */
return;
}
break;

@ -47,7 +47,7 @@ typedef struct physical physical;
#include "http_request_parser.h"
struct request_uri {
GString *uri, *orig_uri;
GString *uri, *orig_uri, *uri_raw;
GString *scheme;
GString *path;

@ -20,13 +20,7 @@ void response_clear(response *resp) {
}
void response_send_headers(server *srv, connection *con) {
GString *head = g_string_sized_new(4*1024);
if (con->request.http_version == HTTP_VERSION_1_1) {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 "));
} else {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.0 "));
}
GString *head = g_string_sized_new(8*1024);
if (con->response.http_status < 100 || con->response.http_status > 999) {
con->response.http_status = 500;
@ -37,16 +31,90 @@ void response_send_headers(server *srv, connection *con) {
if (0 == con->out->length && con->content_handler == NULL
&& con->response.http_status >= 400 && con->response.http_status < 600) {
chunkqueue_append_mem(con->out, CONST_STR_LEN("Custom error"));
chunkqueue_append_mem(con->out, CONST_STR_LEN("Custom error\r\n"));
}
if (con->content_handler == NULL) {
con->out->is_closed = TRUE;
}
g_string_append_printf(head, "%i XXX\r\n", con->response.http_status);
if ((con->response.http_status >= 100 && con->response.http_status < 200) ||
con->response.http_status == 204 ||
con->response.http_status == 205 ||
con->response.http_status == 304) {
/* They never have a content-body/length */
chunkqueue_reset(con->out);
con->out->is_closed = TRUE;
} else if (con->out->is_closed) {
g_string_printf(srv->tmp_str, "%"L_GOFFSET_FORMAT, con->out->length);
http_header_overwrite(con->response.headers, CONST_STR_LEN("Content-Length"), GSTR_LEN(srv->tmp_str));
} else if (con->keep_alive && con->request.http_version == HTTP_VERSION_1_1) {
con->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED;
http_header_overwrite(con->response.headers, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked"));
} else {
/* Unknown content length, no chunked encoding */
con->keep_alive = FALSE;
}
if (con->request.http_method == HTTP_METHOD_HEAD) {
/* content-length is set, but no body */
chunkqueue_reset(con->out);
con->out->is_closed = TRUE;
}
/* Status line */
if (con->request.http_version == HTTP_VERSION_1_1) {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.1 "));
if (!con->keep_alive)
http_header_overwrite(con->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
} else {
g_string_append_len(head, CONST_STR_LEN("HTTP/1.0 "));
if (con->keep_alive)
http_header_overwrite(con->response.headers, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
}
g_string_append_printf(head, "%i XXX\r\n", con->response.http_status); /* TODO: use status name */
/* TODO: append headers */
/* Append headers */
{
GHashTableIter iter;
GString *key;
http_header *header;
GList *valiter;
gboolean have_date = FALSE, have_server = FALSE;
g_hash_table_iter_init (&iter, con->response.headers->table);
while (g_hash_table_iter_next (&iter, (gpointer*) &key, (gpointer*) &header)) {
if (!header) continue;
valiter = g_queue_peek_head_link(&header->values);
if (!valiter) continue;
do {
g_string_append_len(head, GSTR_LEN(header->key));
g_string_append_len(head, CONST_STR_LEN(": "));
g_string_append_len(head, GSTR_LEN((GString*) valiter->data));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
} while (NULL != (valiter = g_list_next(valiter)));
/* key is lowercase */
if (0 == strcmp(key->str, "date")) have_date = TRUE;
else if (0 == strcmp(key->str, "server")) have_server = TRUE;
}
if (!have_date) {
GString *d = server_current_timestamp(srv);
/* HTTP/1.1 requires a Date: header */
g_string_append_len(head, CONST_STR_LEN("Date: "));
g_string_append_len(head, GSTR_LEN(d));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
}
if (!have_server) {
/* TODO: use option for this */
g_string_append_len(head, CONST_STR_LEN("Server: "));
g_string_append_len(head, CONST_STR_LEN("lighttpd-2.0~sandbox"));
g_string_append_len(head, CONST_STR_LEN("\r\n"));
}
}
g_string_append_len(head, CONST_STR_LEN("\r\n"));
chunkqueue_append_string(con->raw_out, head);

@ -37,6 +37,11 @@ server* server_new() {
srv->mainaction = NULL;
srv->exiting = FALSE;
srv->tmp_str = g_string_sized_new(255);
srv->cur_ts = time(0);
srv->last_generated_date_ts = 0;
srv->ts_date_str = g_string_sized_new(255);
return srv;
}
@ -52,6 +57,9 @@ void server_free(server* srv) {
action_release(srv, srv->mainaction);
g_string_free(srv->tmp_str, TRUE);
g_string_free(srv->ts_date_str, TRUE);
/* free logs */
GHashTableIter iter;
gpointer k, v;
@ -147,6 +155,27 @@ void server_listen(server *srv, int fd) {
g_array_append_val(srv->sockets, sock);
}
static void sigint_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
UNUSED(w); UNUSED(revents);
ev_unloop (loop, EVUNLOOP_ALL);
}
static void sigpipe_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
/* ignore */
UNUSED(loop); UNUSED(w); UNUSED(revents);
}
static struct ev_signal
sig_w_INT,
sig_w_TERM,
sig_w_PIPE;
#define CATCH_SIGNAL(loop, cb, n) do {\
ev_signal_init(&sig_w_##n, cb, SIG##n); \
ev_signal_start(loop, &sig_w_##n); \
} while (0)
void server_start(server *srv) {
guint i;
if (srv->state == SERVER_STOPPING || srv->state == SERVER_RUNNING) return; /* no restart after stop */
@ -163,6 +192,10 @@ void server_start(server *srv) {
ev_io_start(srv->loop, &sock->watcher);
}
CATCH_SIGNAL(srv->loop, sigint_cb, INT);
CATCH_SIGNAL(srv->loop, sigint_cb, TERM);
CATCH_SIGNAL(srv->loop, sigpipe_cb, PIPE);
ev_loop(srv->loop, 0);
}
@ -180,3 +213,17 @@ void server_stop(server *srv) {
void joblist_append(server *srv, connection *con) {
connection_state_machine(srv, con);
}
GString *server_current_timestamp(server *srv) {
srv->cur_ts = time(0); /* TODO: update cur_ts somewhere else */
if (srv->cur_ts != srv->last_generated_date_ts) {
g_string_set_size(srv->ts_date_str, 255);
strftime(srv->ts_date_str->str, srv->ts_date_str->allocated_len,
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts)));
g_string_set_size(srv->ts_date_str, strlen(srv->ts_date_str->str));
srv->last_generated_date_ts = srv->cur_ts;
}
return srv->ts_date_str;
}

@ -41,6 +41,11 @@ struct server {
gboolean exiting;
GString *tmp_str; /**< can be used everywhere for local temporary needed strings */
time_t cur_ts, last_generated_date_ts;
GString *ts_date_str; /**< use server_current_timestamp(srv) */
/* logs */
gboolean rotate_logs;
GHashTable *logs;
@ -62,4 +67,6 @@ LI_API void server_stop(server *srv);
LI_API void joblist_append(server *srv, connection *con);
LI_API GString *server_current_timestamp(server *srv);
#endif

@ -23,7 +23,7 @@ __int64 _strtoi64(
);
#endif
#else /** we are a unix */
#include <stdlib.h>
/**
* we use strtoll() for parsing the ranges into a off_t
*

Loading…
Cancel
Save