2008-07-01 18:56:59 +00:00
|
|
|
|
2008-08-05 15:08:32 +00:00
|
|
|
#include "base.h"
|
2008-08-09 15:20:12 +00:00
|
|
|
#include "url_parser.h"
|
|
|
|
#include "utils.h"
|
2008-07-01 18:56:59 +00:00
|
|
|
|
2008-09-29 15:07:53 +00:00
|
|
|
void request_init(request *req) {
|
2008-08-04 22:25:42 +00:00
|
|
|
req->http_method = HTTP_METHOD_UNSET;
|
|
|
|
req->http_method_str = g_string_sized_new(0);
|
|
|
|
req->http_version = HTTP_VERSION_UNSET;
|
|
|
|
|
2008-08-09 15:20:12 +00:00
|
|
|
req->uri.raw = g_string_sized_new(0);
|
2008-08-04 22:25:42 +00:00
|
|
|
req->uri.scheme = g_string_sized_new(0);
|
2008-08-09 15:20:12 +00:00
|
|
|
req->uri.authority = g_string_sized_new(0);
|
2008-08-04 22:25:42 +00:00
|
|
|
req->uri.path = g_string_sized_new(0);
|
|
|
|
req->uri.query = g_string_sized_new(0);
|
2008-08-09 15:20:12 +00:00
|
|
|
req->uri.host = g_string_sized_new(0);
|
2008-08-04 22:25:42 +00:00
|
|
|
|
|
|
|
req->headers = http_headers_new();
|
|
|
|
|
|
|
|
req->content_length = -1;
|
2008-07-01 18:56:59 +00:00
|
|
|
}
|
|
|
|
|
2008-08-05 15:08:32 +00:00
|
|
|
void request_reset(request *req) {
|
|
|
|
req->http_method = HTTP_METHOD_UNSET;
|
|
|
|
g_string_truncate(req->http_method_str, 0);
|
|
|
|
req->http_version = HTTP_VERSION_UNSET;
|
|
|
|
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_truncate(req->uri.raw, 0);
|
2008-08-05 15:08:32 +00:00
|
|
|
g_string_truncate(req->uri.scheme, 0);
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_truncate(req->uri.authority, 0);
|
2008-08-05 15:08:32 +00:00
|
|
|
g_string_truncate(req->uri.path, 0);
|
|
|
|
g_string_truncate(req->uri.query, 0);
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_truncate(req->uri.host, 0);
|
2008-08-05 15:08:32 +00:00
|
|
|
|
2008-09-29 13:46:58 +00:00
|
|
|
http_headers_reset(req->headers);
|
2008-08-05 15:08:32 +00:00
|
|
|
|
|
|
|
req->content_length = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void request_clear(request *req) {
|
|
|
|
req->http_method = HTTP_METHOD_UNSET;
|
|
|
|
g_string_free(req->http_method_str, TRUE);
|
|
|
|
req->http_version = HTTP_VERSION_UNSET;
|
|
|
|
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_free(req->uri.raw, TRUE);
|
2008-08-05 15:08:32 +00:00
|
|
|
g_string_free(req->uri.scheme, TRUE);
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_free(req->uri.authority, TRUE);
|
2008-08-05 15:08:32 +00:00
|
|
|
g_string_free(req->uri.path, TRUE);
|
|
|
|
g_string_free(req->uri.query, TRUE);
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_free(req->uri.host, TRUE);
|
2008-08-05 15:08:32 +00:00
|
|
|
|
2008-09-29 13:46:58 +00:00
|
|
|
http_headers_free(req->headers);
|
2008-08-05 15:08:32 +00:00
|
|
|
|
|
|
|
req->content_length = -1;
|
2008-07-01 18:56:59 +00:00
|
|
|
}
|
2008-08-06 18:46:42 +00:00
|
|
|
|
2008-08-06 23:44:09 +00:00
|
|
|
/* closes connection after response */
|
2008-09-08 00:20:55 +00:00
|
|
|
static void bad_request(connection *con, int status) {
|
2008-08-06 23:44:09 +00:00
|
|
|
con->keep_alive = FALSE;
|
|
|
|
con->response.http_status = status;
|
2008-09-08 00:20:55 +00:00
|
|
|
connection_handle_direct(con);
|
2008-08-06 23:44:09 +00:00
|
|
|
}
|
|
|
|
|
2008-09-08 00:20:55 +00:00
|
|
|
gboolean request_parse_url(connection *con) {
|
2008-08-07 12:12:51 +00:00
|
|
|
request *req = &con->request;
|
|
|
|
|
2008-08-09 15:20:12 +00:00
|
|
|
g_string_truncate(req->uri.query, 0);
|
|
|
|
g_string_truncate(req->uri.path, 0);
|
|
|
|
|
|
|
|
if (!parse_raw_url(&req->uri))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* "*" only allowed for method OPTIONS */
|
|
|
|
if (0 == strcmp(req->uri.path->str, "*") && req->http_method != HTTP_METHOD_OPTIONS)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
url_decode(req->uri.path);
|
|
|
|
path_simplify(req->uri.path);
|
|
|
|
|
2008-08-07 12:12:51 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2008-09-08 00:20:55 +00:00
|
|
|
void request_validate_header(connection *con) {
|
2008-08-06 23:44:09 +00:00
|
|
|
request *req = &con->request;
|
|
|
|
http_header *hh;
|
2008-09-09 14:38:40 +00:00
|
|
|
GList *l;
|
2008-08-06 23:44:09 +00:00
|
|
|
|
|
|
|
switch (req->http_version) {
|
|
|
|
case HTTP_VERSION_1_0:
|
|
|
|
if (!http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("keep-alive")))
|
2008-08-07 14:50:36 +00:00
|
|
|
con->keep_alive = FALSE;
|
2008-08-06 23:44:09 +00:00
|
|
|
break;
|
|
|
|
case HTTP_VERSION_1_1:
|
|
|
|
if (http_header_is(req->headers, CONST_STR_LEN("connection"), CONST_STR_LEN("close")))
|
2008-08-07 14:50:36 +00:00
|
|
|
con->keep_alive = FALSE;
|
2008-08-06 23:44:09 +00:00
|
|
|
break;
|
|
|
|
case HTTP_VERSION_UNSET:
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 505); /* Version not Supported */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-09 15:20:12 +00:00
|
|
|
if (req->uri.raw->len == 0) {
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get hostname */
|
2008-09-09 14:38:40 +00:00
|
|
|
l = http_header_find_first(req->headers, CONST_STR_LEN("host"));
|
|
|
|
if (NULL != l && NULL != http_header_find_next(l, CONST_STR_LEN("host"))) {
|
|
|
|
/* more than one "host" header */
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
2008-09-09 14:38:40 +00:00
|
|
|
} else {
|
|
|
|
hh = (http_header*) l->data;
|
|
|
|
g_string_append_len(req->uri.authority, HEADER_VALUE_LEN(hh));
|
2008-08-13 17:52:57 +00:00
|
|
|
if (!parse_hostname(&req->uri)) {
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-09 15:20:12 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-08-06 23:44:09 +00:00
|
|
|
}
|
|
|
|
|
2008-08-10 19:31:56 +00:00
|
|
|
/* Need hostname in HTTP/1.1 */
|
|
|
|
if (req->uri.host->len == 0 && req->http_version == HTTP_VERSION_1_1) {
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-09 15:20:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-10 19:31:56 +00:00
|
|
|
/* may override hostname */
|
2008-09-08 00:20:55 +00:00
|
|
|
if (!request_parse_url(con)) {
|
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-07 12:12:51 +00:00
|
|
|
return;
|
2008-08-10 19:31:56 +00:00
|
|
|
}
|
2008-08-07 12:12:51 +00:00
|
|
|
|
2008-08-06 23:44:09 +00:00
|
|
|
/* content-length */
|
2008-09-09 14:38:40 +00:00
|
|
|
hh = http_header_lookup(req->headers, CONST_STR_LEN("content-length"));
|
2008-08-06 23:44:09 +00:00
|
|
|
if (hh) {
|
2008-09-09 14:38:40 +00:00
|
|
|
const gchar *val = HEADER_VALUE(hh);
|
2008-08-06 23:44:09 +00:00
|
|
|
off_t r;
|
|
|
|
char *err;
|
|
|
|
|
2008-09-09 14:38:40 +00:00
|
|
|
r = str_to_off_t(val, &err, 10);
|
2008-08-06 23:44:09 +00:00
|
|
|
if (*err != '\0') {
|
2008-09-08 00:20:55 +00:00
|
|
|
CON_TRACE(con, "content-length is not a number: %s (Status: 400)", err);
|
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* negative content-length is not supported
|
|
|
|
* and is a bad request
|
|
|
|
*/
|
|
|
|
if (r < 0) {
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-06 23:44:09 +00:00
|
|
|
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) {
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 413); /* Request Entity Too Large */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
con->request.content_length = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expect: 100-continue */
|
2008-09-09 14:38:40 +00:00
|
|
|
l = http_header_find_first(req->headers, CONST_STR_LEN("expect"));
|
|
|
|
if (l) {
|
2008-08-06 23:44:09 +00:00
|
|
|
gboolean expect_100_cont = FALSE;
|
|
|
|
|
2008-09-09 14:38:40 +00:00
|
|
|
for ( ; l ; l = http_header_find_next(l, CONST_STR_LEN("expect")) ) {
|
|
|
|
hh = (http_header*) l->data;
|
|
|
|
if (0 == strcasecmp( HEADER_VALUE(hh), "100-continue" )) {
|
2008-08-06 23:44:09 +00:00
|
|
|
expect_100_cont = TRUE;
|
|
|
|
} else {
|
|
|
|
/* we only support 100-continue */
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 417); /* Expectation Failed */
|
2008-08-06 23:44:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expect_100_cont && req->http_version == HTTP_VERSION_1_0) {
|
|
|
|
/* only HTTP/1.1 clients can send us this header */
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 417); /* Expectation Failed */
|
2008-08-06 23:44:09 +00:00
|
|
|
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)
|
|
|
|
*/
|
|
|
|
|
2008-08-06 18:46:42 +00:00
|
|
|
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) {
|
2008-09-08 00:20:55 +00:00
|
|
|
CON_ERROR(con, "%s", "GET/HEAD with content-length -> 400");
|
2008-08-06 18:46:42 +00:00
|
|
|
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 400); /* bad request */
|
2008-08-06 18:46:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
con->request.content_length = 0;
|
|
|
|
break;
|
|
|
|
case HTTP_METHOD_POST:
|
|
|
|
/* content-length is required for them */
|
|
|
|
if (con->request.content_length == -1) {
|
|
|
|
/* content-length is missing */
|
2008-09-08 00:20:55 +00:00
|
|
|
CON_ERROR(con, "%s", "POST-request, but content-length missing -> 411");
|
2008-08-06 18:46:42 +00:00
|
|
|
|
2008-09-08 00:20:55 +00:00
|
|
|
bad_request(con, 411); /* Length Required */
|
2008-08-06 18:46:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* the may have a content-length */
|
|
|
|
break;
|
|
|
|
}
|
2008-08-07 12:12:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void physical_init(physical *phys) {
|
|
|
|
phys->path = g_string_sized_new(512);
|
|
|
|
phys->basedir = g_string_sized_new(256);
|
|
|
|
phys->doc_root = g_string_sized_new(256);
|
|
|
|
phys->rel_path = g_string_sized_new(256);
|
|
|
|
phys->pathinfo = g_string_sized_new(256);
|
|
|
|
phys->size = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void physical_reset(physical *phys) {
|
|
|
|
g_string_truncate(phys->path, 0);
|
|
|
|
g_string_truncate(phys->basedir, 0);
|
|
|
|
g_string_truncate(phys->doc_root, 0);
|
|
|
|
g_string_truncate(phys->rel_path, 0);
|
|
|
|
g_string_truncate(phys->pathinfo, 0);
|
|
|
|
phys->size = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void physical_clear(physical *phys) {
|
|
|
|
g_string_free(phys->path, TRUE);
|
|
|
|
g_string_free(phys->basedir, TRUE);
|
|
|
|
g_string_free(phys->doc_root, TRUE);
|
|
|
|
g_string_free(phys->rel_path, TRUE);
|
|
|
|
g_string_free(phys->pathinfo, TRUE);
|
|
|
|
phys->size = -1;
|
2008-08-06 18:46:42 +00:00
|
|
|
}
|