the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
6.2 KiB
236 lines
6.2 KiB
|
|
#include "base.h" |
|
|
|
void request_init(request *req, chunkqueue *in) { |
|
req->http_method = HTTP_METHOD_UNSET; |
|
req->http_method_str = g_string_sized_new(0); |
|
req->http_version = HTTP_VERSION_UNSET; |
|
|
|
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); |
|
|
|
req->headers = http_headers_new(); |
|
|
|
req->host = g_string_sized_new(0); |
|
req->content_length = -1; |
|
|
|
http_request_parser_init(&req->parser_ctx, req, in); |
|
} |
|
|
|
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; |
|
|
|
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); |
|
|
|
http_headers_reset(req->headers); |
|
|
|
g_string_truncate(req->host, 0); |
|
req->content_length = -1; |
|
|
|
http_request_parser_reset(&req->parser_ctx); |
|
} |
|
|
|
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; |
|
|
|
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); |
|
|
|
http_headers_free(req->headers); |
|
|
|
g_string_free(req->host, TRUE); |
|
req->content_length = -1; |
|
|
|
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); |
|
} |
|
|
|
gboolean request_parse_url(server *srv, connection *con) { |
|
request *req = &con->request; |
|
|
|
/* TODO: parse url */ |
|
return TRUE; |
|
} |
|
|
|
void request_validate_header(server *srv, connection *con) { |
|
request *req = &con->request; |
|
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))); |
|
} |
|
|
|
/* may override hostname */ |
|
if (!request_parse_url(srv, con)) |
|
return; |
|
|
|
/* 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) { |
|
CON_ERROR(srv, con, "%s", "GET/HEAD with content-length -> 400"); |
|
|
|
bad_request(srv, con, 400); /* bad request */ |
|
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 */ |
|
CON_ERROR(srv, con, "%s", "POST-request, but content-length missing -> 411"); |
|
|
|
bad_request(srv, con, 411); /* Length Required */ |
|
return; |
|
} |
|
break; |
|
default: |
|
/* the may have a content-length */ |
|
break; |
|
} |
|
|
|
/* TODO: check hostname */ |
|
} |
|
|
|
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; |
|
}
|
|
|