Added url/authority parsing, enabled simple static() action.
parent
754ee742b2
commit
a863b6f7e9
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
124
src/utils.c
124
src/utils.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,7 @@ common_source='''
|
|||
server.c
|
||||
sys-files.c
|
||||
sys-socket.c
|
||||
url_parser.rl
|
||||
utils.c
|
||||
|
||||
plugin_core.c
|
||||
|
|
Loading…
Reference in New Issue