Browse Source

handle requests now :) - still many TODOs for error handling/keep alive/validating headers

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent
commit
e216fc5168
  1. 4
      src/base.h
  2. 2
      src/chunk.h
  3. 8
      src/condition.c
  4. 3
      src/config_lua.c
  5. 70
      src/http_headers.c
  6. 10
      src/http_headers.h
  7. 6
      src/http_request_parser.rl
  8. 14
      src/plugin_core.c
  9. 88
      src/response.c
  10. 22
      src/server.c
  11. 7
      src/server.h

4
src/base.h

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

2
src/chunk.h

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

8
src/condition.c

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

3
src/config_lua.c

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

70
src/http_headers.c

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

10
src/http_headers.h

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

6
src/http_request_parser.rl

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

14
src/plugin_core.c

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

88
src/response.c

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

22
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;
@ -180,3 +188,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;
}

7
src/server.h

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

Loading…
Cancel
Save