mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-08 11:53:00 -04:00
Merge branch 'http_uri_parse'
This commit is contained in:
commit
bf11e7ddf7
594
http.c
594
http.c
@ -2353,6 +2353,9 @@ static const char uri_chars[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
#define CHAR_IS_UNRESERVED(c) \
|
||||
(uri_chars[(unsigned char)(c)])
|
||||
|
||||
/*
|
||||
* Helper functions to encode/decode a string for inclusion in a URI.
|
||||
* The returned string must be freed by the caller.
|
||||
@ -2373,7 +2376,7 @@ evhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
|
||||
end = uri+strlen(uri);
|
||||
|
||||
for (p = uri; p < end; p++) {
|
||||
if (uri_chars[(unsigned char)(*p)]) {
|
||||
if (CHAR_IS_UNRESERVED(*p)) {
|
||||
evbuffer_add(buf, p, 1);
|
||||
} else if (*p == ' ' && space_as_plus) {
|
||||
evbuffer_add(buf, "+", 1);
|
||||
@ -2482,30 +2485,39 @@ evhttp_uridecode(const char *uri, int decode_plus, size_t *size_out)
|
||||
*/
|
||||
|
||||
int
|
||||
evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers)
|
||||
evhttp_parse_query__checked_20(const char *str, struct evkeyvalq *headers,
|
||||
int is_whole_uri)
|
||||
{
|
||||
char *line;
|
||||
char *line=NULL;
|
||||
char *argument;
|
||||
char *p;
|
||||
const char *query_part;
|
||||
int result = -1;
|
||||
struct evhttp_uri *uri=NULL;
|
||||
|
||||
TAILQ_INIT(headers);
|
||||
|
||||
/* No arguments - we are done */
|
||||
if (strchr(uri, '?') == NULL)
|
||||
return 0;
|
||||
|
||||
if ((line = mm_strdup(uri)) == NULL) {
|
||||
event_warn("%s: strdup", __func__);
|
||||
return -1;
|
||||
if (is_whole_uri) {
|
||||
uri = evhttp_uri_parse(str);
|
||||
if (!uri)
|
||||
goto error;
|
||||
query_part = evhttp_uri_get_query(uri);
|
||||
} else {
|
||||
query_part = str;
|
||||
}
|
||||
|
||||
argument = line;
|
||||
/* No arguments - we are done */
|
||||
if (!query_part || !strlen(query_part)) {
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We already know that there has to be a ? */
|
||||
strsep(&argument, "?");
|
||||
if ((line = mm_strdup(query_part)) == NULL) {
|
||||
event_warn("%s: strdup", __func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
p = argument;
|
||||
p = argument = line;
|
||||
while (p != NULL && *p != '\0') {
|
||||
char *key, *value, *decoded_value;
|
||||
argument = strsep(&p, "&");
|
||||
@ -2532,7 +2544,10 @@ evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers)
|
||||
error:
|
||||
evhttp_clear_headers(headers);
|
||||
done:
|
||||
mm_free(line);
|
||||
if (line)
|
||||
mm_free(line);
|
||||
if (uri)
|
||||
evhttp_uri_free(uri);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2545,11 +2560,9 @@ void evhttp_parse_query(const char *uri, struct evkeyvalq *headers);
|
||||
void
|
||||
evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
|
||||
{
|
||||
evhttp_parse_query__checked_20(uri, headers);
|
||||
evhttp_parse_query__checked_20(uri, headers, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct evhttp_cb *
|
||||
evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req)
|
||||
{
|
||||
@ -3376,3 +3389,548 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
|
||||
return (fd);
|
||||
}
|
||||
|
||||
struct evhttp_uri {
|
||||
char *scheme; /* scheme; e.g http, ftp etc */
|
||||
char *userinfo; /* userinfo (typically username:pass), or NULL */
|
||||
char *host; /* hostname, IP address, or NULL */
|
||||
int port; /* port, or zero */
|
||||
char *path; /* path, or "". */
|
||||
char *query; /* query, or NULL */
|
||||
char *fragment; /* fragment or NULL */
|
||||
};
|
||||
|
||||
struct evhttp_uri *
|
||||
evhttp_uri_new(void)
|
||||
{
|
||||
struct evhttp_uri *uri = mm_calloc(sizeof(struct evhttp_uri), 1);
|
||||
if (uri)
|
||||
uri->port = -1;
|
||||
return uri;
|
||||
}
|
||||
|
||||
/* Return true of the string starting at s and ending immediately before eos
|
||||
* is a valid URI scheme according to RFC3986
|
||||
*/
|
||||
static int
|
||||
scheme_ok(const char *s, const char *eos)
|
||||
{
|
||||
/* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
|
||||
EVUTIL_ASSERT(eos >= s);
|
||||
if (s == eos)
|
||||
return 0;
|
||||
if (!EVUTIL_ISALPHA(*s))
|
||||
return 0;
|
||||
while (++s < eos) {
|
||||
if (! EVUTIL_ISALNUM(*s) &&
|
||||
*s != '+' && *s != '-' && *s != '.')
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SUBDELIMS "!$&'()*+,;="
|
||||
|
||||
/* Return true iff [s..eos) is a valid userinfo */
|
||||
static int
|
||||
userinfo_ok(const char *s, const char *eos)
|
||||
{
|
||||
while (s < eos) {
|
||||
if (CHAR_IS_UNRESERVED(*s) ||
|
||||
strchr(SUBDELIMS, *s) ||
|
||||
*s == ':')
|
||||
++s;
|
||||
else if (*s == '%' && s+2 < eos &&
|
||||
EVUTIL_ISXDIGIT(s[1]) &&
|
||||
EVUTIL_ISXDIGIT(s[2]))
|
||||
s += 3;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
regname_ok(const char *s, const char *eos)
|
||||
{
|
||||
while (s && s<eos) {
|
||||
if (CHAR_IS_UNRESERVED(*s) ||
|
||||
strchr(SUBDELIMS, *s))
|
||||
++s;
|
||||
else if (*s == '%' &&
|
||||
EVUTIL_ISXDIGIT(s[1]) &&
|
||||
EVUTIL_ISXDIGIT(s[2]))
|
||||
s += 3;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_port(const char *s, const char *eos)
|
||||
{
|
||||
int portnum = 0;
|
||||
while (s < eos) {
|
||||
if (! EVUTIL_ISDIGIT(*s))
|
||||
return -1;
|
||||
portnum = (portnum * 10) + (*s - '0');
|
||||
if (portnum < 0)
|
||||
return -1;
|
||||
++s;
|
||||
}
|
||||
return portnum;
|
||||
}
|
||||
|
||||
/* returns 0 for bad, 1 for ipv6, 2 for IPvFuture */
|
||||
static int
|
||||
bracket_addr_ok(const char *s, const char *eos)
|
||||
{
|
||||
if (s + 3 > eos || *s != '[' || *(eos-1) != ']')
|
||||
return 0;
|
||||
if (s[1] == 'v') {
|
||||
/* IPvFuture, or junk.
|
||||
"v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
|
||||
*/
|
||||
s += 2; /* skip [v */
|
||||
--eos;
|
||||
if (!EVUTIL_ISXDIGIT(*s)) /*require at least one*/
|
||||
return 0;
|
||||
while (s < eos && *s != '.') {
|
||||
if (EVUTIL_ISXDIGIT(*s))
|
||||
++s;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
if (*s != '.')
|
||||
return 0;
|
||||
++s;
|
||||
while (s < eos) {
|
||||
if (CHAR_IS_UNRESERVED(*s) ||
|
||||
strchr(SUBDELIMS, *s) ||
|
||||
*s == ':')
|
||||
++s;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 2;
|
||||
} else {
|
||||
/* IPv6, or junk */
|
||||
char buf[64];
|
||||
int n_chars = eos-s-2;
|
||||
struct in6_addr in6;
|
||||
if (n_chars >= 64) /* way too long */
|
||||
return 0;
|
||||
memcpy(buf, s+1, n_chars);
|
||||
buf[n_chars]='\0';
|
||||
return (evutil_inet_pton(AF_INET6,buf,&in6)==1) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
parse_authority(struct evhttp_uri *uri, char *s, char *eos)
|
||||
{
|
||||
char *cp, *port;
|
||||
EVUTIL_ASSERT(eos);
|
||||
if (eos == s) {
|
||||
uri->host = mm_strdup("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Optionally, we start with "userinfo@" */
|
||||
|
||||
cp = strchr(s, '@');
|
||||
if (cp && cp < eos) {
|
||||
if (! userinfo_ok(s,cp))
|
||||
return -1;
|
||||
*cp++ = '\0';
|
||||
uri->userinfo = mm_strdup(s);
|
||||
} else {
|
||||
cp = s;
|
||||
}
|
||||
/* Optionally, we end with ":port" */
|
||||
for (port=eos-1; port >= cp && EVUTIL_ISDIGIT(*port); --port)
|
||||
;
|
||||
if (port >= cp && *port == ':') {
|
||||
if (port+1 == eos) /* Leave port unspecified; the RFC allows a
|
||||
* nil port */
|
||||
uri->port = -1;
|
||||
else if ((uri->port = parse_port(port+1, eos))<0)
|
||||
return -1;
|
||||
eos = port;
|
||||
}
|
||||
/* Now, cp..eos holds the "host" port, which can be an IPv4Address,
|
||||
* an IP-Literal, or a reg-name */
|
||||
EVUTIL_ASSERT(eos >= cp);
|
||||
if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
|
||||
/* IPv6address, IP-Literal, or junk. */
|
||||
if (! bracket_addr_ok(cp, eos))
|
||||
return -1;
|
||||
} else {
|
||||
/* Make sure the host part is ok. */
|
||||
if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */
|
||||
return -1;
|
||||
}
|
||||
uri->host = mm_malloc(eos-cp+1);
|
||||
memcpy(uri->host, cp, eos-cp);
|
||||
uri->host[eos-cp] = '\0';
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static char *
|
||||
end_of_authority(char *cp)
|
||||
{
|
||||
while (*cp) {
|
||||
if (*cp == '?' || *cp == '#' || *cp == '/')
|
||||
return cp;
|
||||
++cp;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Return the character after the longest prefix of 'cp' that matches...
|
||||
* *pchar / "/" if allow_qchars is false, or
|
||||
* *(pchar / "/" / "?") if allow_chars is true.
|
||||
*/
|
||||
static char *
|
||||
end_of_path(char *cp, int allow_qchars)
|
||||
{
|
||||
while (*cp) {
|
||||
if (CHAR_IS_UNRESERVED(*cp) ||
|
||||
strchr(SUBDELIMS, *cp) ||
|
||||
*cp == ':' || *cp == '@' || *cp == '/')
|
||||
++cp;
|
||||
else if (*cp == '%' && EVUTIL_ISXDIGIT(cp[1]) &&
|
||||
EVUTIL_ISXDIGIT(cp[2]))
|
||||
cp += 3;
|
||||
else if (*cp == '?' && allow_qchars)
|
||||
++cp;
|
||||
else
|
||||
return cp;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
static int
|
||||
path_matches_noscheme(const char *cp)
|
||||
{
|
||||
while (*cp) {
|
||||
if (*cp == ':')
|
||||
return 0;
|
||||
else if (*cp == '/')
|
||||
return 1;
|
||||
++cp;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct evhttp_uri *
|
||||
evhttp_uri_parse(const char *source_uri)
|
||||
{
|
||||
char *readbuf = NULL, *readp = NULL, *token = NULL, *query = NULL;
|
||||
char *path = NULL, *fragment = NULL;
|
||||
int got_authority = 0;
|
||||
|
||||
struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
|
||||
if (uri == NULL) {
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
goto err;
|
||||
}
|
||||
uri->port = -1;
|
||||
|
||||
readbuf = mm_strdup(source_uri);
|
||||
if (readbuf == NULL) {
|
||||
event_err(1, "%s: strdup", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
readp = readbuf;
|
||||
token = NULL;
|
||||
|
||||
/* We try to follow RFC3986 here as much as we can, and match
|
||||
the productions
|
||||
|
||||
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
||||
|
||||
relative-ref = relative-part [ "?" query ] [ "#" fragment ]
|
||||
|
||||
*/
|
||||
|
||||
/* 1. scheme: */
|
||||
token = strchr(readp, ':');
|
||||
if (token && scheme_ok(readp,token)) {
|
||||
*token = '\0';
|
||||
uri->scheme = mm_strdup(readp);
|
||||
|
||||
readp = token+1; /* eat : */
|
||||
}
|
||||
|
||||
/* 2. Optionally, "//" then an 'authority' part. */
|
||||
if (readp[0]=='/' && readp[1] == '/') {
|
||||
char *authority;
|
||||
readp += 2;
|
||||
authority = readp;
|
||||
path = end_of_authority(readp);
|
||||
if (parse_authority(uri, authority, path) < 0)
|
||||
goto err;
|
||||
readp = path;
|
||||
got_authority = 1;
|
||||
}
|
||||
|
||||
/* 3. Query: path-abempty, path-absolute, path-rootless, or path-empty
|
||||
*/
|
||||
path = readp;
|
||||
readp = end_of_path(path, 0);
|
||||
|
||||
/* Query */
|
||||
if (*readp == '?') {
|
||||
*readp = '\0';
|
||||
++readp;
|
||||
query = readp;
|
||||
readp = end_of_path(readp, 1);
|
||||
}
|
||||
/* fragment */
|
||||
if (*readp == '#') {
|
||||
*readp = '\0';
|
||||
++readp;
|
||||
fragment = readp;
|
||||
readp = end_of_path(readp, 1);
|
||||
}
|
||||
if (*readp != '\0') {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* These next two cases may be unreachable; I'm leaving them
|
||||
* in to be defensive. */
|
||||
/* If you didn't get an authority, the path can't begin with "//" */
|
||||
if (!got_authority && path[0]=='/' && path[1]=='/')
|
||||
goto err;
|
||||
/* If you did get an authority, the path must begin with "/" or be
|
||||
* empty. */
|
||||
if (got_authority && path[0] != '/' && path[0] != '\0')
|
||||
goto err;
|
||||
/* (End of maybe-unreachable cases) */
|
||||
|
||||
/* If there was no scheme, the first part of the path (if any) must
|
||||
* have no colon in it. */
|
||||
if (! uri->scheme && !path_matches_noscheme(path))
|
||||
goto err;
|
||||
|
||||
EVUTIL_ASSERT(path);
|
||||
uri->path = mm_strdup(path);
|
||||
|
||||
if (query)
|
||||
uri->query = mm_strdup(query);
|
||||
if (fragment)
|
||||
uri->fragment = mm_strdup(fragment);
|
||||
|
||||
mm_free(readbuf);
|
||||
|
||||
return uri;
|
||||
err:
|
||||
if (uri)
|
||||
evhttp_uri_free(uri);
|
||||
if (readbuf)
|
||||
mm_free(readbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
evhttp_uri_free(struct evhttp_uri *uri)
|
||||
{
|
||||
#define _URI_FREE_STR(f) \
|
||||
if (uri->f) { \
|
||||
mm_free(uri->f); \
|
||||
}
|
||||
|
||||
_URI_FREE_STR(scheme);
|
||||
_URI_FREE_STR(userinfo);
|
||||
_URI_FREE_STR(host);
|
||||
_URI_FREE_STR(query);
|
||||
_URI_FREE_STR(fragment);
|
||||
|
||||
mm_free(uri);
|
||||
#undef _URI_FREE_STR
|
||||
}
|
||||
|
||||
char *
|
||||
evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
|
||||
{
|
||||
struct evbuffer *tmp = 0;
|
||||
size_t joined_size = 0;
|
||||
char *output = NULL;
|
||||
|
||||
#define _URI_ADD(f) evbuffer_add(tmp, uri->f, strlen(uri->f))
|
||||
|
||||
if (!uri || !buf || !limit)
|
||||
return NULL;
|
||||
|
||||
tmp = evbuffer_new();
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
if (uri->scheme) {
|
||||
_URI_ADD(scheme);
|
||||
evbuffer_add(tmp, ":", 1);
|
||||
}
|
||||
if (uri->host) {
|
||||
evbuffer_add(tmp, "//", 2);
|
||||
if (uri->userinfo)
|
||||
evbuffer_add_printf(tmp,"%s@", uri->userinfo);
|
||||
_URI_ADD(host);
|
||||
if (uri->port >= 0)
|
||||
evbuffer_add_printf(tmp,":%d", uri->port);
|
||||
|
||||
if (uri->path && uri->path[0] != '/' && uri->path[0] != '\0')
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (uri->path)
|
||||
_URI_ADD(path);
|
||||
|
||||
if (uri->query) {
|
||||
evbuffer_add(tmp, "?", 1);
|
||||
_URI_ADD(query);
|
||||
}
|
||||
|
||||
if (uri->fragment) {
|
||||
evbuffer_add(tmp, "#", 1);
|
||||
_URI_ADD(fragment);
|
||||
}
|
||||
|
||||
evbuffer_add(tmp, "\0", 1); /* NUL */
|
||||
|
||||
joined_size = evbuffer_get_length(tmp);
|
||||
|
||||
if (joined_size > limit) {
|
||||
/* It doesn't fit. */
|
||||
evbuffer_free(tmp);
|
||||
return NULL;
|
||||
}
|
||||
evbuffer_remove(tmp, buf, joined_size);
|
||||
|
||||
output = buf;
|
||||
err:
|
||||
evbuffer_free(tmp);
|
||||
|
||||
return output;
|
||||
#undef _URI_ADD
|
||||
}
|
||||
|
||||
const char *
|
||||
evhttp_uri_get_scheme(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->scheme;
|
||||
}
|
||||
const char *
|
||||
evhttp_uri_get_userinfo(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->userinfo;
|
||||
}
|
||||
const char *
|
||||
evhttp_uri_get_host(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->host;
|
||||
}
|
||||
int
|
||||
evhttp_uri_get_port(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->port;
|
||||
}
|
||||
const char *
|
||||
evhttp_uri_get_path(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->path;
|
||||
}
|
||||
const char *
|
||||
evhttp_uri_get_query(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->query;
|
||||
}
|
||||
const char *
|
||||
evhttp_uri_get_fragment(const struct evhttp_uri *uri)
|
||||
{
|
||||
return uri->fragment;
|
||||
}
|
||||
|
||||
#define _URI_SET_STR(f) do { \
|
||||
if (uri->f) \
|
||||
mm_free(uri->f); \
|
||||
if (f) { \
|
||||
if ((uri->f = mm_strdup(f)) == NULL) { \
|
||||
event_warn("%s: strdup()", __func__); \
|
||||
return -1; \
|
||||
} \
|
||||
} else { \
|
||||
uri->f = NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
int
|
||||
evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme)
|
||||
{
|
||||
if (scheme && !scheme_ok(scheme, scheme+strlen(scheme)))
|
||||
return -1;
|
||||
|
||||
_URI_SET_STR(scheme);
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo)
|
||||
{
|
||||
if (userinfo && !userinfo_ok(userinfo, userinfo+strlen(userinfo)))
|
||||
return -1;
|
||||
_URI_SET_STR(userinfo);
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
|
||||
{
|
||||
if (host) {
|
||||
if (host[0] == '[') {
|
||||
if (! bracket_addr_ok(host, host+strlen(host)))
|
||||
return -1;
|
||||
} else {
|
||||
if (! regname_ok(host, host+strlen(host)))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_URI_SET_STR(host);
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
evhttp_uri_set_port(struct evhttp_uri *uri, int port)
|
||||
{
|
||||
if (port < -1)
|
||||
return -1;
|
||||
uri->port = port;
|
||||
return 0;
|
||||
}
|
||||
#define end_of_cpath(cp,aq) ((const char*)(end_of_path(((char*)(cp)), (aq))))
|
||||
|
||||
int
|
||||
evhttp_uri_set_path(struct evhttp_uri *uri, const char *path)
|
||||
{
|
||||
if (path && end_of_cpath(path, 0) != path+strlen(path))
|
||||
return -1;
|
||||
|
||||
_URI_SET_STR(path);
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
evhttp_uri_set_query(struct evhttp_uri *uri, const char *query)
|
||||
{
|
||||
if (query && end_of_cpath(query, 1) != query+strlen(query))
|
||||
return -1;
|
||||
_URI_SET_STR(query);
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment)
|
||||
{
|
||||
if (fragment && end_of_cpath(fragment, 1) != fragment+strlen(fragment))
|
||||
return -1;
|
||||
_URI_SET_STR(fragment);
|
||||
return 0;
|
||||
}
|
||||
|
@ -578,7 +578,7 @@ char *evhttp_uridecode(const char *uri, int decode_plus,
|
||||
/**
|
||||
Helper function to parse out arguments in a query.
|
||||
|
||||
Parsing a uri like
|
||||
Parsing a URI like
|
||||
|
||||
http://foo.com/?q=test&s=some+thing
|
||||
|
||||
@ -587,17 +587,41 @@ char *evhttp_uridecode(const char *uri, int decode_plus,
|
||||
The first entry is: key="q", value="test"
|
||||
The second entry is: key="s", value="some thing"
|
||||
|
||||
@deprecated This function is deprecated as of Libevent 2.0.9. Use
|
||||
evhttp_uri_parse and evhttp_parse_query_str instead.
|
||||
|
||||
@param uri the request URI
|
||||
@param headers the head of the evkeyval queue
|
||||
@return 0 on success, -1 on failure
|
||||
*/
|
||||
#define evhttp_parse_query(uri, headers) \
|
||||
evhttp_parse_query__checked_20((uri), (headers))
|
||||
evhttp_parse_query__checked_20((uri), (headers), 1)
|
||||
|
||||
/**
|
||||
Helper function to parse out arguments from the query portion of an
|
||||
HTTP URI.
|
||||
|
||||
Parsing a query string like
|
||||
|
||||
q=test&s=some+thing
|
||||
|
||||
will result in two entries in the key value queue.
|
||||
|
||||
The first entry is: key="q", value="test"
|
||||
The second entry is: key="s", value="some thing"
|
||||
|
||||
@param query_parse the query portion of the URI
|
||||
@param headers the head of the evkeyval queue
|
||||
@return 0 on success, -1 on failure
|
||||
*/
|
||||
#define evhttp_parse_query_str(query, headers) \
|
||||
evhttp_parse_query__checked_20((uri), (headers), 0)
|
||||
|
||||
/* Do not call this function directly; it is a temporary alias introduced
|
||||
* to avoid changing the old signature for evhttp_parse_query
|
||||
*/
|
||||
int evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers);
|
||||
int evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers,
|
||||
int is_whole_url);
|
||||
|
||||
/**
|
||||
* Escape HTML character entities in a string.
|
||||
@ -612,6 +636,130 @@ int evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers);
|
||||
*/
|
||||
char *evhttp_htmlescape(const char *html);
|
||||
|
||||
/**
|
||||
* A structure to hold a parsed URI or Relative-Ref conforming to RFC3986.
|
||||
*/
|
||||
struct evhttp_uri;
|
||||
|
||||
/**
|
||||
* Return a new empty evhttp_uri with no fields set.
|
||||
*/
|
||||
struct evhttp_uri *evhttp_uri_new(void);
|
||||
|
||||
/** Return the scheme of an evhttp_uri, or NULL if there is no scheme has
|
||||
* been set and the evhttp_uri contains a Relative-Ref. */
|
||||
const char *evhttp_uri_get_scheme(const struct evhttp_uri *uri);
|
||||
/**
|
||||
* Return the userinfo part of an evhttp_uri, or NULL if it has no userinfo
|
||||
* set.
|
||||
*/
|
||||
const char *evhttp_uri_get_userinfo(const struct evhttp_uri *uri);
|
||||
/**
|
||||
* Return the host part of an evhttp_uri, or NULL if it has no host set.
|
||||
* The host may either be a regular hostname (conforming to the RFC 3986
|
||||
* "regname" production), or an IPv4 address, or the empty string, or a
|
||||
* bracketed IPv6 address, or a bracketed 'IP-Future' address.
|
||||
*
|
||||
* Note that having a NULL host means that the URI has no authority
|
||||
* section, but having an empty-string host means that the URI has an
|
||||
* authority section with no host part. For example,
|
||||
* "mailto:user@example.com" has a host of NULL, but "file:///etc/motd"
|
||||
* has a host of "".
|
||||
*/
|
||||
const char *evhttp_uri_get_host(const struct evhttp_uri *uri);
|
||||
/** Return the port part of an evhttp_uri, or -1 if there is no port set. */
|
||||
int evhttp_uri_get_port(const struct evhttp_uri *uri);
|
||||
/** Return the path part of an evhttp_uri, or NULL if it has no path set */
|
||||
const char *evhttp_uri_get_path(const struct evhttp_uri *uri);
|
||||
/** Return the query part of an evhttp_uri (excluding the leading "?"), or
|
||||
* NULL if it has no query set */
|
||||
const char *evhttp_uri_get_query(const struct evhttp_uri *uri);
|
||||
/** Return the fragment part of an evhttp_uri (excluding the leading "#"),
|
||||
* or NULL if it has no fragment set */
|
||||
const char *evhttp_uri_get_fragment(const struct evhttp_uri *uri);
|
||||
|
||||
/** Set the scheme of an evhttp_uri, or clear the scheme if scheme==NULL.
|
||||
* Returns 0 on success, -1 if scheme is not well-formed. */
|
||||
int evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme);
|
||||
/** Set the userinfo of an evhttp_uri, or clear the userinfo if userinfo==NULL.
|
||||
* Returns 0 on success, -1 if userinfo is not well-formed. */
|
||||
int evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo);
|
||||
/** Set the host of an evhttp_uri, or clear the host if host==NULL.
|
||||
* Returns 0 on success, -1 if host is not well-formed. */
|
||||
int evhttp_uri_set_host(struct evhttp_uri *uri, const char *host);
|
||||
/** Set the port of an evhttp_uri, or clear the port if port==-1.
|
||||
* Returns 0 on success, -1 if port is not well-formed. */
|
||||
int evhttp_uri_set_port(struct evhttp_uri *uri, int port);
|
||||
/** Set the path of an evhttp_uri, or clear the path if path==NULL.
|
||||
* Returns 0 on success, -1 if path is not well-formed. */
|
||||
int evhttp_uri_set_path(struct evhttp_uri *uri, const char *path);
|
||||
/** Set the query of an evhttp_uri, or clear the query if query==NULL.
|
||||
* The query should not include a leading "?".
|
||||
* Returns 0 on success, -1 if query is not well-formed. */
|
||||
int evhttp_uri_set_query(struct evhttp_uri *uri, const char *query);
|
||||
/** Set the fragment of an evhttp_uri, or clear the fragment if fragment==NULL.
|
||||
* The fragment should not include a leading "#".
|
||||
* Returns 0 on success, -1 if fragment is not well-formed. */
|
||||
int evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment);
|
||||
|
||||
/**
|
||||
* Helper function to parse a URI-Reference as specified by RFC3986.
|
||||
*
|
||||
* This function matches the URI-Reference production from RFC3986,
|
||||
* which includes both URIs like
|
||||
*
|
||||
* scheme://[[userinfo]@]foo.com[:port]]/[path][?query][#fragment]
|
||||
*
|
||||
* and relative-refs like
|
||||
*
|
||||
* [path][?query][#fragment]
|
||||
*
|
||||
* Any optional elements portions not present in the original URI are
|
||||
* left set to NULL in the resulting evhttp_uri. If no port is
|
||||
* specified, the port is set to -1.
|
||||
*
|
||||
* Note that no decoding is performed on percent-escaped characters in
|
||||
* the string; if you want to parse them, use evhttp_uridecode or
|
||||
* evhttp_parse_query_str as appropriate.
|
||||
*
|
||||
* Note also that most URI schemes will have additional constraints that
|
||||
* this function does not know about, and cannot check. For example,
|
||||
* mailto://www.example.com/cgi-bin/fortune.pl is not a reasonable
|
||||
* mailto url, http://www.example.com:99999/ is not a reasonable HTTP
|
||||
* URL, and ftp:username@example.com is not a reasonable FTP URL.
|
||||
* Nevertheless, all of these URLs conform to RFC3986, and this function
|
||||
* accepts all of them as valid.
|
||||
*
|
||||
* @param source_uri the request URI
|
||||
* @return uri container to hold parsed data, or NULL if there is error
|
||||
* @see evhttp_uri_free()
|
||||
*/
|
||||
struct evhttp_uri *evhttp_uri_parse(const char *source_uri);
|
||||
|
||||
/**
|
||||
* Free all memory allocated for a parsed uri. Only use this for URIs
|
||||
* generated by evhttp_uri_parse.
|
||||
*
|
||||
* @param uri container with parsed data
|
||||
* @see evhttp_uri_parse()
|
||||
*/
|
||||
void evhttp_uri_free(struct evhttp_uri *uri);
|
||||
|
||||
/**
|
||||
* Join together the uri parts from parsed data to form a URI-Reference.
|
||||
*
|
||||
* Note that no escaping of reserved characters is done on the members
|
||||
* of the evhttp_uri, so the generated string might not be a valid URI
|
||||
* unless the members of evhttp_uri are themselves valid.
|
||||
*
|
||||
* @param uri container with parsed data
|
||||
* @param buf destination buffer
|
||||
* @param limit destination buffer size
|
||||
* @return an joined uri as string or NULL on error
|
||||
@see evhttp_uri_parse()
|
||||
*/
|
||||
char *evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1741,6 +1741,365 @@ end:
|
||||
evhttp_clear_headers(&headers);
|
||||
}
|
||||
|
||||
static void
|
||||
http_parse_uri_test(void *ptr)
|
||||
{
|
||||
struct evhttp_uri *uri = NULL;
|
||||
char url_tmp[4096];
|
||||
|
||||
#define TT_URI(want) do { \
|
||||
char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \
|
||||
tt_want(ret != NULL); \
|
||||
tt_want(ret == url_tmp); \
|
||||
if (strcmp(ret,want) != 0) \
|
||||
TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \
|
||||
} while(0)
|
||||
|
||||
tt_want(evhttp_uri_join(NULL, 0, 0) == NULL);
|
||||
tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL);
|
||||
tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL);
|
||||
|
||||
/* bad URIs: parsing */
|
||||
#define BAD(s) do { \
|
||||
if (evhttp_uri_parse(s) != NULL) \
|
||||
TT_FAIL(("Expected error parsing \"%s\"",s)); \
|
||||
} while(0)
|
||||
BAD("http://www.test.com/ why hello");
|
||||
BAD("http://www.test.com/why-hello\x01");
|
||||
BAD("http://www.test.com/why-hello?\x01");
|
||||
BAD("http://www.test.com/why-hello#\x01");
|
||||
BAD("http://www.\x01.test.com/why-hello");
|
||||
BAD("http://www.%7test.com/why-hello");
|
||||
BAD("http://www.test.com/why-hell%7o");
|
||||
BAD("h%3ttp://www.test.com/why-hello");
|
||||
BAD("http://www.test.com/why-hello%7");
|
||||
BAD("http://www.test.com/why-hell%7o");
|
||||
BAD("http://www.test.com/foo?ba%r");
|
||||
BAD("http://www.test.com/foo#ba%r");
|
||||
BAD("99:99/foo");
|
||||
BAD("http://www.test.com:999x/");
|
||||
BAD("http://www.test.com:x/");
|
||||
BAD("http://[hello-there]/");
|
||||
BAD("http://[::1]]/");
|
||||
BAD("http://[::1/");
|
||||
BAD("http://[foob/");
|
||||
BAD("http://[/");
|
||||
BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:"
|
||||
"ffff:ffff:ffff:ffff:ffff:ffff:ffff]/");
|
||||
BAD("http://[vX.foo]/");
|
||||
BAD("http://[vX.foo]/");
|
||||
BAD("http://[v.foo]/");
|
||||
BAD("http://[v5.fo%o]/");
|
||||
BAD("http://[v5X]/");
|
||||
BAD("http://[v5]/");
|
||||
BAD("http://[]/");
|
||||
BAD("http://f\x01red@www.example.com/");
|
||||
BAD("http://f%0red@www.example.com/");
|
||||
BAD("http://www.example.com:9999999999999999999999999999999999999/");
|
||||
BAD("http://www.example.com:hihi/");
|
||||
BAD("://www.example.com/");
|
||||
|
||||
/* bad URIs: joining */
|
||||
uri = evhttp_uri_new();
|
||||
tt_want(0==evhttp_uri_set_host(uri, "www.example.com"));
|
||||
tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL);
|
||||
/* not enough space: */
|
||||
tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL);
|
||||
/* host is set, but path doesn't start with "/": */
|
||||
tt_want(0==evhttp_uri_set_path(uri, "hi_mom"));
|
||||
tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL);
|
||||
tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL);
|
||||
tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL);
|
||||
evhttp_uri_free(uri);
|
||||
uri = evhttp_uri_parse("mailto:foo@bar");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_host(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto"));
|
||||
tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar"));
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("mailto:foo@bar");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_new();
|
||||
/* Bad URI usage: setting invalid values */
|
||||
tt_want(-1 == evhttp_uri_set_scheme(uri,""));
|
||||
tt_want(-1 == evhttp_uri_set_scheme(uri,"33"));
|
||||
tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!"));
|
||||
tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@"));
|
||||
tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]"));
|
||||
tt_want(-1 == evhttp_uri_set_host(uri,"["));
|
||||
tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com"));
|
||||
tt_want(-1 == evhttp_uri_set_port(uri,-3));
|
||||
tt_want(-1 == evhttp_uri_set_path(uri,"hello?world"));
|
||||
tt_want(-1 == evhttp_uri_set_query(uri,"hello#world"));
|
||||
tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world"));
|
||||
/* Valid URI usage: setting valid values */
|
||||
tt_want(0 == evhttp_uri_set_scheme(uri,"http"));
|
||||
tt_want(0 == evhttp_uri_set_scheme(uri,NULL));
|
||||
tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass"));
|
||||
tt_want(0 == evhttp_uri_set_userinfo(uri,NULL));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,"www.example.com"));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4"));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]"));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]"));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,NULL));
|
||||
tt_want(0 == evhttp_uri_set_host(uri,""));
|
||||
tt_want(0 == evhttp_uri_set_port(uri, -1));
|
||||
tt_want(0 == evhttp_uri_set_port(uri, 80));
|
||||
tt_want(0 == evhttp_uri_set_port(uri, 65535));
|
||||
tt_want(0 == evhttp_uri_set_path(uri, ""));
|
||||
tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html"));
|
||||
tt_want(0 == evhttp_uri_set_path(uri, NULL));
|
||||
tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2"));
|
||||
tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg"));
|
||||
tt_want(0 == evhttp_uri_set_query(uri, ""));
|
||||
tt_want(0 == evhttp_uri_set_query(uri, NULL));
|
||||
tt_want(0 == evhttp_uri_set_fragment(uri, ""));
|
||||
tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am"));
|
||||
tt_want(0 == evhttp_uri_set_fragment(uri, NULL));
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
/* Valid parsing */
|
||||
uri = evhttp_uri_parse("http://www.test.com/?q=t%33est");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://www.test.com/?q=t%33est");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://%77ww.test.com");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://%77ww.test.com");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://www.test.com?q=test");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://www.test.com?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://www.test.com#fragment");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment");
|
||||
TT_URI("http://www.test.com#fragment");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://8000/");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://8000/");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://:8000/");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == 8000);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://:8000/");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://www.test.com:/"); /* empty port */
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want_str_op(evhttp_uri_get_path(uri), ==, "/");
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://www.test.com/");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("http://www.test.com:"); /* empty port 2 */
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("http://www.test.com");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("ftp://www.test.com/?q=test");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("ftp://www.test.com/?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("ftp://[::1]:999/?q=test");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == 999);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("ftp://[::1]:999/?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("ftp://[ff00::127.0.0.1]/?q=test");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("ftp://[ff00::127.0.0.1]/?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("ftp://[v99.not_(any:time)_soon]/?q=test");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
|
||||
tt_want(evhttp_uri_get_port(uri) == 42);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
|
||||
TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("scheme://user@foo.com/#fragment");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
|
||||
TT_URI("scheme://user@foo.com/#fragment");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("scheme://%75ser@foo.com/#frag@ment");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0);
|
||||
TT_URI("scheme://%75ser@foo.com/#frag@ment");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("file:///some/path/to/the/file");
|
||||
tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("file:///some/path/to/the/file");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("///some/path/to/the-file");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_scheme(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("///some/path/to/the-file");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("/s:ome/path/to/the-file?q=99#fred");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_scheme(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_host(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0);
|
||||
TT_URI("/s:ome/path/to/the-file?q=99#fred");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("relative/path/with/co:lon");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_scheme(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_host(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_fragment(uri) == NULL);
|
||||
TT_URI("relative/path/with/co:lon");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("bob?q=99&q2=q?33#fr?ed");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_scheme(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_host(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
|
||||
TT_URI("bob?q=99&q2=q?33#fr?ed");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("#fr?ed");
|
||||
tt_want(uri != NULL);
|
||||
tt_want(evhttp_uri_get_scheme(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_host(uri) == NULL);
|
||||
tt_want(evhttp_uri_get_port(uri) == -1);
|
||||
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
|
||||
tt_want(evhttp_uri_get_query(uri) == NULL);
|
||||
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
|
||||
TT_URI("#fr?ed");
|
||||
evhttp_uri_free(uri);
|
||||
}
|
||||
|
||||
static void
|
||||
http_uriencode_test(void *ptr)
|
||||
{
|
||||
@ -2840,6 +3199,7 @@ struct testcase_t http_testcases[] = {
|
||||
{ "base", http_base_test, TT_FORK|TT_NEED_BASE, NULL, NULL },
|
||||
{ "bad_headers", http_bad_header_test, 0, NULL, NULL },
|
||||
{ "parse_query", http_parse_query_test, 0, NULL, NULL },
|
||||
{ "parse_uri", http_parse_uri_test, 0, NULL, NULL },
|
||||
{ "uriencode", http_uriencode_test, 0, NULL, NULL },
|
||||
HTTP_LEGACY(basic),
|
||||
HTTP_LEGACY(cancel),
|
||||
|
@ -90,10 +90,10 @@
|
||||
TT_STMT_BEGIN \
|
||||
if (!(b)) { \
|
||||
_tinytest_set_test_failed(); \
|
||||
TT_GRIPE((msg)); \
|
||||
TT_GRIPE(("%s",msg)); \
|
||||
fail; \
|
||||
} else { \
|
||||
TT_BLATHER((msg)); \
|
||||
TT_BLATHER(("%s",msg)); \
|
||||
} \
|
||||
TT_STMT_END
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
#define tt_assert(b) tt_assert_msg((b), "assert("#b")")
|
||||
|
||||
#define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \
|
||||
setup_block,cleanup_block) \
|
||||
setup_block,cleanup_block,die_on_fail) \
|
||||
TT_STMT_BEGIN \
|
||||
type _val1 = (type)(a); \
|
||||
type _val2 = (type)(b); \
|
||||
@ -135,33 +135,50 @@
|
||||
cleanup_block; \
|
||||
if (!_tt_status) { \
|
||||
_tinytest_set_test_failed(); \
|
||||
TT_EXIT_TEST_FUNCTION; \
|
||||
die_on_fail ; \
|
||||
} \
|
||||
} \
|
||||
TT_STMT_END
|
||||
|
||||
#define tt_assert_test_type(a,b,str_test,type,test,fmt) \
|
||||
#define tt_assert_test_type(a,b,str_test,type,test,fmt,die_on_fail) \
|
||||
tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \
|
||||
{_print=_value;},{})
|
||||
{_print=_value;},{},die_on_fail)
|
||||
|
||||
/* Helper: assert that a op b, when cast to type. Format the values with
|
||||
* printf format fmt on failure. */
|
||||
#define tt_assert_op_type(a,op,b,type,fmt) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt)
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt, \
|
||||
TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
#define tt_int_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld")
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2), \
|
||||
"%ld",TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
#define tt_uint_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
|
||||
(_val1 op _val2),"%lu")
|
||||
(_val1 op _val2),"%lu",TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
#define tt_ptr_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
|
||||
(_val1 op _val2),"%p")
|
||||
(_val1 op _val2),"%p",TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
#define tt_str_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
|
||||
(strcmp(_val1,_val2) op 0),"<%s>")
|
||||
(strcmp(_val1,_val2) op 0),"<%s>",TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
#define tt_want_int_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld",(void)0)
|
||||
|
||||
#define tt_want_uint_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
|
||||
(_val1 op _val2),"%lu",(void)0)
|
||||
|
||||
#define tt_want_ptr_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
|
||||
(_val1 op _val2),"%p",(void)0)
|
||||
|
||||
#define tt_want_str_op(a,op,b) \
|
||||
tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
|
||||
(strcmp(_val1,_val2) op 0),"<%s>",(void)0)
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user