Add some request header checks.
This commit is contained in:
parent
52c5b05713
commit
3b4e8f6f8d
|
@ -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);
|
||||
|
|
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;
|
||||
|
|
|
@ -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