Add some request header checks.

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent 52c5b05713
commit 3b4e8f6f8d
  1. 4
      src/actions.c
  2. 30
      src/connection.c
  3. 2
      src/connection.h
  4. 2
      src/http_request_parser.rl
  5. 119
      src/request.c
  6. 2
      src/request.h
  7. 2
      src/sys-strings.h

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

@ -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;
}
@ -94,11 +92,11 @@ 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);
// CON_TRACE(srv, con, "cq->len: raw_out=%i, out=%i", (int) con->raw_out->length, (int) con->out->length);
dojoblist = TRUE;
}
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 +112,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 +136,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 +162,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 +182,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 +225,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 +246,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 +272,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 +298,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 +313,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 +323,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;

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

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

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