merged from lp
This commit is contained in:
commit
3964ce82b7
|
@ -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,
|
||||
|
|
23
src/chunk.c
23
src/chunk.c
|
@ -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);
|
||||
http_header *h = _http_header_new(key, keylen);
|
||||
g_queue_push_tail(&h->values, g_string_new_len(value, valuelen));
|
||||
|
||||
g_string_append_len(newval, key, keylen);
|
||||
g_string_append_len(newval, ": ", 2);
|
||||
g_string_append_len(newval, 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));
|
||||
return NULL;
|
||||
}
|
||||
if (opt_cond->type != OPTION_CONDITION) {
|
||||
ERROR(srv, "expected condition as first parameter, got %s", option_type_string(opt->type));
|
||||
ERROR(srv, "expected condition as first parameter, got %s", option_type_string(opt_cond->type));
|
||||
return NULL;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
119
src/request.c
119
src/request.c
|
@ -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;
|
||||
}
|
||||
|
||||
/* TODO: append headers */
|
||||
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 */
|
||||
|
||||
/* 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);
|
||||
|
|
47
src/server.c
47
src/server.c
|
@ -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…
Reference in New Issue