2
0
Fork 0

Added url/authority parsing, enabled simple static() action.

personal/stbuehler/wip
Stefan Bühler 2008-08-09 17:20:12 +02:00
parent 754ee742b2
commit a863b6f7e9
11 changed files with 378 additions and 28 deletions

View File

@ -10,7 +10,11 @@
static chunkfile *chunkfile_new(GString *name, int fd, gboolean is_temp) {
chunkfile *cf = g_slice_new(chunkfile);
cf->refcount = 1;
cf->name = name;
if (name) {
cf->name = g_string_new_len(GSTR_LEN(name));
} else {
cf->name = NULL;
}
cf->fd = fd;
cf->is_temp = is_temp;
return cf;
@ -29,7 +33,7 @@ static void chunkfile_release(chunkfile *cf) {
cf->fd = -1;
if (cf->is_temp) unlink(cf->name->str);
cf->is_temp = FALSE;
g_string_free(cf->name, TRUE);
if (cf->name) g_string_free(cf->name, TRUE);
cf->name = NULL;
g_slice_free(chunkfile, cf);
}
@ -278,6 +282,7 @@ void chunkqueue_append_mem(chunkqueue *cq, void *mem, gssize len) {
static void __chunkqueue_append_file(chunkqueue *cq, GString *filename, off_t start, off_t length, int fd, gboolean is_temp) {
chunk *c = chunk_new();
c->type = FILE_CHUNK;
c->file.file = chunkfile_new(filename, fd, is_temp);
c->file.start = start;
c->file.length = length;

View File

@ -253,7 +253,7 @@ static gboolean condition_check_eval_string(server *srv, connection *con, condit
value = con->request.uri.path->str;
break;
case COMP_REQUEST_HOST:
value = con->request.host->str;
value = con->request.uri.host->str;
break;
case COMP_REQUEST_SCHEME:
value = con->is_ssl ? "https" : "http";
@ -420,7 +420,7 @@ static gboolean condition_check_eval_ip(server *srv, connection *con, condition
return (cond->op == CONFIG_COND_NOTIP);
break;
case COMP_REQUEST_HOST:
value = con->request.host->str;
value = con->request.uri.host->str;
break;
case COMP_REQUEST_SCHEME:
ERROR(srv, "%s", "Cannot parse request.scheme as ip");

View File

@ -20,7 +20,7 @@
action done { fbreak; }
action method { getStringTo(fpc, ctx->request->http_method_str); }
action uri { getStringTo(fpc, ctx->request->uri.uri_raw); }
action uri { getStringTo(fpc, ctx->request->uri.raw); }
action header_key {
getStringTo(fpc, ctx->h_key);

View File

@ -7,6 +7,11 @@ static action* core_list(server *srv, plugin* p, option *opt) {
guint i;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type == OPTION_ACTION) {
a = opt->value.opt_action.action;
action_acquire(a);
@ -39,6 +44,10 @@ static action* core_when(server *srv, plugin* p, option *opt) {
action *a;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_LIST) {
ERROR(srv, "expected list, got %s", option_type_string(opt->type));
return NULL;
@ -69,6 +78,10 @@ static action* core_set(server *srv, plugin* p, option *opt) {
action *a;
UNUSED(p);
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_LIST) {
ERROR(srv, "expected list, got %s", option_type_string(opt->type));
return NULL;
@ -88,11 +101,65 @@ static action* core_set(server *srv, plugin* p, option *opt) {
return a;
}
static action_result core_handle_physical(server *srv, connection *con, gpointer param) {
GString *docroot = (GString*) param;
UNUSED(srv);
if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON;
g_string_truncate(con->physical.path, 0);
g_string_append_len(con->physical.path, GSTR_LEN(docroot));
g_string_append_len(con->physical.path, GSTR_LEN(con->request.uri.path));
return ACTION_GO_ON;
}
static void core_physical_free(struct server *srv, gpointer param) {
UNUSED(srv);
g_string_free((GString*) param, TRUE);
}
static action* core_physical(server *srv, plugin* p, option *opt) {
UNUSED(p);
GString *docroot;
if (!opt) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
if (opt->type != OPTION_STRING) {
ERROR(srv, "expected string as parameter, got %s", option_type_string(opt->type));
return NULL;
}
docroot = (GString*) option_extract_value(opt);
return action_new_function(core_handle_physical, core_physical_free, docroot);
}
static action_result core_handle_static(server *srv, connection *con, gpointer param) {
UNUSED(param);
/* TODO: handle static files */
CON_ERROR(srv, con, "%s", "Not implemented yet");
return ACTION_ERROR;
int fd;
if (con->state != CON_STATE_HANDLE_REQUEST_HEADER) return ACTION_GO_ON;
if (con->physical.path->len == 0) return ACTION_GO_ON;
fd = open(con->physical.path->str, O_RDONLY);
if (fd == -1) {
con->response.http_status = 404;
} else {
struct stat st;
fstat(fd, &st);
#ifdef FD_CLOEXEC
fcntl(fd, F_SETFD, FD_CLOEXEC);
#endif
con->response.http_status = 200;
chunkqueue_append_file_fd(con->out, NULL, 0, st.st_size, fd);
}
connection_handle_direct(srv, con);
return ACTION_GO_ON;
}
static action* core_static(server *srv, plugin* p, option *opt) {
@ -111,7 +178,7 @@ 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_mem(con->out, GSTR_LEN(con->request.uri.uri));
chunkqueue_append_mem(con->out, GSTR_LEN(con->request.uri.path));
chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n"));
connection_handle_direct(srv, con);
@ -195,6 +262,8 @@ static const plugin_action actions[] = {
{ "list", core_list },
{ "when", core_when },
{ "set", core_set },
{ "physical", core_physical },
{ "static", core_static },
{ "test", core_test },
{ NULL, NULL }

View File

@ -1,21 +1,22 @@
#include "base.h"
#include "url_parser.h"
#include "utils.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.raw = g_string_sized_new(0);
req->uri.scheme = g_string_sized_new(0);
req->uri.authority = g_string_sized_new(0);
req->uri.path = g_string_sized_new(0);
req->uri.query = g_string_sized_new(0);
req->uri.host = 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);
@ -26,16 +27,15 @@ void request_reset(request *req) {
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.raw, 0);
g_string_truncate(req->uri.scheme, 0);
g_string_truncate(req->uri.authority, 0);
g_string_truncate(req->uri.path, 0);
g_string_truncate(req->uri.query, 0);
g_string_truncate(req->uri.host, 0);
http_headers_reset(req->headers);
g_string_truncate(req->host, 0);
req->content_length = -1;
http_request_parser_reset(&req->parser_ctx);
@ -46,16 +46,15 @@ void request_clear(request *req) {
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.raw, TRUE);
g_string_free(req->uri.scheme, TRUE);
g_string_free(req->uri.authority, TRUE);
g_string_free(req->uri.path, TRUE);
g_string_free(req->uri.query, TRUE);
g_string_free(req->uri.host, TRUE);
http_headers_free(req->headers);
g_string_free(req->host, TRUE);
req->content_length = -1;
http_request_parser_clear(&req->parser_ctx);
@ -72,7 +71,19 @@ gboolean request_parse_url(server *srv, connection *con) {
request *req = &con->request;
UNUSED(srv); UNUSED(req);
/* TODO: parse url */
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);
return TRUE;
}
@ -94,23 +105,35 @@ void request_validate_header(server *srv, connection *con) {
return;
}
if (req->uri.uri_raw->len == 0) {
if (req->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)) {
if (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)));
g_string_append_len(req->uri.host, GSTR_LEN((GString*) g_queue_peek_head(&hh->values)));
if (parse_authority(&req->uri)) {
bad_request(srv, con, 400); /* bad request */
return;
}
}
/* may override hostname */
if (!request_parse_url(srv, con))
if (!request_parse_url(srv, con)) {
bad_request(srv, con, 400); /* bad request */
return;
}
/* Need hostname in HTTP/1.1 */
if (req->uri.host->len == 0 && req->http_version == HTTP_VERSION_1_1) {
bad_request(srv, con, 400); /* bad request */
return;
}
/* content-length */
hh = http_header_lookup_fast(req->headers, CONST_STR_LEN("content-length"));

View File

@ -47,11 +47,14 @@ typedef struct physical physical;
#include "http_request_parser.h"
struct request_uri {
GString *uri, *orig_uri, *uri_raw;
GString *raw;
GString *scheme;
GString *authority;
GString *path;
GString *query;
GString *host; /* without userinfo and port */
};
struct physical {
@ -75,12 +78,14 @@ struct request {
http_headers *headers;
/* Parsed headers: */
GString *host;
goffset content_length;
http_request_ctx parser_ctx;
};
#include "base.h"
LI_API void request_init(request *req, chunkqueue *in);
LI_API void request_reset(request *req);
LI_API void request_clear(request *req);

10
src/url_parser.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _LIGHTTPD_URL_PARSER_H_
#define _LIGHTTPD_URL_PARSER_H_
#include "settings.h"
#include "request.h"
LI_API gboolean parse_raw_url(request_uri *uri);
LI_API gboolean parse_authority(request_uri *uri);
#endif

107
src/url_parser.rl Normal file
View File

@ -0,0 +1,107 @@
#include "url_parser.h"
#include <stdlib.h>
%%{
machine url_parser;
action mark { mark = fpc; }
action mark_host { host_mark = fpc; }
action save_host {
g_string_truncate(uri->host, 0);
g_string_append_len(uri->host, host_mark, fpc - host_mark);
g_string_ascii_down(uri->host);
}
action save_authority {
g_string_truncate(uri->authority, 0);
g_string_append_len(uri->authority, mark, fpc - mark);
g_string_ascii_down(uri->authority);
}
action save_path {
g_string_append_len(uri->path, mark, fpc - mark);
}
action save_query {
g_string_append_len(uri->query, mark, fpc - mark);
}
pct_encoded = "%" xdigit xdigit;
gen_delims = ":" | "/" | "?" | "#" | "[" | "]" | "@";
sub_delims = "!" | "$" | "&" | "'" | "(" | ")"
| "*" | "+" | "," | ";" | "=";
reserved = gen_delims | sub_delims;
unreserved = alpha | digit | "-" | "." | "_" | "~";
pchar = unreserved | pct_encoded | sub_delims | ":" | "@";
path = ("/" ( "/" | pchar)*) >mark %save_path;
# scheme = alpha *( alpha | digit | "+" | "-" | "." );
scheme = "http" | "https";
#simple ipv4 address
dec_octet = digit{1,3};
IPv4address = dec_octet "." dec_octet "." dec_octet "." dec_octet;
IPvFuture = "v" xdigit+ "." ( unreserved | sub_delims | ":" )+;
# simple ipv6 address
IPv6address = (":" | xdigit)+ IPv4address?;
IP_literal = "[" ( IPv6address | IPvFuture ) "]";
reg_name = ( unreserved | pct_encoded | sub_delims )+;
userinfo = ( unreserved | pct_encoded | sub_delims | ":" )*;
host = IP_literal | IPv4address | reg_name;
port = digit+;
authority = ( userinfo "@" )? (host >mark_host %save_host) ( ":" port )?;
query = ( pchar | "/" | "?" )* >mark %save_query;
fragment = ( pchar | "/" | "?" )*;
URI_path = path ( "?" query )? ( "#" fragment )?;
URI = scheme "://" (authority >mark %save_authority) URI_path;
parse_URI := URI | ("*" >mark %save_path) | URI_path;
parse_Authority := authority;
write data;
}%%
gboolean parse_raw_url(request_uri *uri) {
const char *p, *pe, *eof;
const char *mark = NULL, *host_mark = NULL;
int cs;
p = uri->raw->str;
eof = pe = uri->raw->str + uri->raw->len;
%% write init nocs;
cs = url_parser_en_parse_URI;
%% write exec;
return (cs >= url_parser_first_final);
}
gboolean parse_authority(request_uri *uri) {
const char *p, *pe, *eof;
const char *mark = NULL, *host_mark = NULL;
int cs;
g_string_ascii_down(uri->authority);
p = uri->authority->str;
eof = pe = uri->authority->str + uri->authority->len;
%% write init nocs;
cs = url_parser_en_parse_Authority;
%% write exec;
return (cs >= url_parser_first_final);
}

View File

@ -44,3 +44,127 @@ void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events) {
ev_io_set(watcher, watcher->fd, (watcher->events & ~(EV_READ | EV_WRITE)) | events);
ev_io_start(loop, watcher);
}
/* converts hex char (0-9, A-Z, a-z) to decimal.
* returns -1 on invalid input.
*/
static int hex2int(unsigned char hex) {
int res;
if (hex >= 'A') { /* 'A' < 'a': hex >= 'A' --> hex >= 'a' */
if (hex >= 'a') {
res = hex - 'a' + 10;
} else {
res = hex - 'A' + 10;
}
} else {
res = hex - '0';
}
if (res > 15)
res = -1;
return res;
}
void url_decode(GString *path) {
char *src, *dst, c;
src = dst = path->str;
for ( ; *src; src++) {
c = *src;
if (c == '%') {
if (src[1] && src[2]) {
int a = hex2int(src[1]), b = hex2int(src[2]);
if (a != -1 && b != -1) {
c = (a << 4) | b;
if (c < 32 || c == 127) c = '_';
*(dst++) = c;
}
src += 2;
} else {
/* end of string */
return;
}
} else {
if (c < 32 || c == 127) c = '_';
*(dst++) = c;
}
}
g_string_set_size(path, dst - path->str);
}
/* Remove "/../", "//", "/./" parts from path.
*
* /blah/.. gets /
* /blah/../foo gets /foo
* /abc/./xyz gets /abc/xyz
* /abc//xyz gets /abc/xyz
*
* NOTE: src and dest can point to the same buffer, in which case
* the operation is performed in-place.
*/
void path_simplify(GString *path) {
int toklen;
char c, pre1;
char *start, *slash, *walk, *out;
unsigned short pre;
if (path == NULL)
return;
walk = start = out = slash = path->str;
while (*walk == ' ') {
walk++;
}
pre1 = *(walk++);
c = *(walk++);
pre = pre1;
if (pre1 != '/') {
pre = ('/' << 8) | pre1;
*(out++) = '/';
}
*(out++) = pre1;
if (pre1 == '\0') {
g_string_set_size(path, out - start);
return;
}
while (1) {
if (c == '/' || c == '\0') {
toklen = out - slash;
if (toklen == 3 && pre == (('.' << 8) | '.')) {
out = slash;
if (out > start) {
out--;
while (out > start && *out != '/') {
out--;
}
}
if (c == '\0')
out++;
} else if (toklen == 1 || pre == (('/' << 8) | '.')) {
out = slash;
if (c == '\0')
out++;
}
slash = out;
}
if (c == '\0')
break;
pre1 = c;
pre = (pre << 8) | pre1;
c = *walk;
*out = pre1;
out++;
walk++;
}
g_string_set_size(path, out - start);
}

View File

@ -11,4 +11,10 @@ LI_API void ev_io_add_events(struct ev_loop *loop, ev_io *watcher, int events);
LI_API void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events);
LI_API void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events);
/* URL inplace decode: replace %XX with character \xXX; replace control characters with '_' (< 32 || == 127) */
LI_API void url_decode(GString *path);
LI_API void path_simplify(GString *path);
#endif

View File

@ -27,6 +27,7 @@ common_source='''
server.c
sys-files.c
sys-socket.c
url_parser.rl
utils.c
plugin_core.c