mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-12 13:58:58 -04:00
Introduce absolute URI parsing helpers.
See evhttp_uri_parse(), evhttp_uri_free() and evhttp_uri_join() for details.
This commit is contained in:
parent
f13e449b53
commit
86dd720a66
160
http.c
160
http.c
@ -3334,3 +3334,163 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
|
||||
return (fd);
|
||||
}
|
||||
|
||||
struct evhttp_uri *evhttp_uri_parse(const char *source_uri)
|
||||
{
|
||||
char *readbuf = 0, *readp = 0, *token = 0, *query = 0, *host = 0, *port = 0;
|
||||
|
||||
struct evhttp_uri *uri = calloc(1, sizeof(*uri));
|
||||
if (uri == NULL) {
|
||||
event_err(1, "%s: calloc", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
readbuf = strdup(source_uri);
|
||||
if (readbuf == NULL) {
|
||||
event_err(1, "%s: strdup", __func__);
|
||||
free(uri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
readp = readbuf;
|
||||
token = NULL;
|
||||
|
||||
/* 1. scheme:// */
|
||||
token = strstr(readp, "://");
|
||||
if (!token) {
|
||||
/* unsupported uri */
|
||||
free(readbuf);
|
||||
free(uri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*token = '\0';
|
||||
uri->scheme = strdup(readp);
|
||||
|
||||
readp = token;
|
||||
readp += 3; /* eat :// */
|
||||
|
||||
/* 2. query */
|
||||
query = strchr(readp, '/');
|
||||
if (query) {
|
||||
char *fragment = strchr(query, '#');
|
||||
if (fragment) {
|
||||
*fragment++ = '\0'; /* eat '#' */
|
||||
uri->fragment = strdup(fragment);
|
||||
}
|
||||
|
||||
uri->query = strdup(query);
|
||||
*query = '\0'; /* eat '/' */
|
||||
}
|
||||
|
||||
/* 3. user:pass@host:port */
|
||||
host = strchr(readp, '@');
|
||||
if (host) {
|
||||
char *pass = 0;
|
||||
/* got user:pass@host:port */
|
||||
*host++ = '\0'; /* eat @ */;
|
||||
pass = strchr(readp, ':');
|
||||
if (pass) {
|
||||
*pass++ = '\0'; /* eat ':' */
|
||||
uri->pass = strdup(pass);
|
||||
}
|
||||
|
||||
uri->user = strdup(readp);
|
||||
readp = host;
|
||||
}
|
||||
|
||||
/* 4. host:port */
|
||||
port = strchr(readp, ':');
|
||||
if (port) {
|
||||
*port++ = '\0'; /* eat ':' */
|
||||
uri->port = atoi(port);
|
||||
}
|
||||
|
||||
/* 5. host */
|
||||
uri->host = strdup(readp);
|
||||
|
||||
free(readbuf);
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
void evhttp_uri_free(struct evhttp_uri *uri)
|
||||
{
|
||||
if (uri == NULL)
|
||||
return;
|
||||
|
||||
#define _URI_FREE_STR(f) \
|
||||
if (uri->f) { \
|
||||
free(uri->f); \
|
||||
}
|
||||
|
||||
_URI_FREE_STR(scheme);
|
||||
_URI_FREE_STR(user);
|
||||
_URI_FREE_STR(pass);
|
||||
_URI_FREE_STR(host);
|
||||
_URI_FREE_STR(query);
|
||||
_URI_FREE_STR(fragment);
|
||||
|
||||
free(uri);
|
||||
|
||||
#undef _URI_FREE_STR
|
||||
}
|
||||
|
||||
char *evhttp_uri_join(struct evhttp_uri *uri, void *buf, size_t limit)
|
||||
{
|
||||
struct evbuffer *tmp = 0;
|
||||
unsigned char *joined = 0;
|
||||
size_t joined_size = 0;
|
||||
|
||||
#define _URI_ADD(f) evbuffer_add(tmp, uri->f, strlen(uri->f))
|
||||
if (!uri || !uri->scheme || !buf || !limit)
|
||||
return NULL;
|
||||
|
||||
tmp = evbuffer_new();
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
_URI_ADD(scheme);
|
||||
evbuffer_add(tmp, "://", 3);
|
||||
if (uri->host && *uri->host) {
|
||||
if (uri->user && *uri->user) {
|
||||
_URI_ADD(user);
|
||||
if (uri->pass && *uri->pass) {
|
||||
evbuffer_add(tmp, ":", 1);
|
||||
_URI_ADD(pass);
|
||||
}
|
||||
evbuffer_add(tmp, "@", 1);
|
||||
}
|
||||
|
||||
_URI_ADD(host);
|
||||
|
||||
if (uri->port > 0)
|
||||
evbuffer_add_printf(tmp,":%u", uri->port);
|
||||
}
|
||||
|
||||
if (uri->query && *uri->query)
|
||||
_URI_ADD(query);
|
||||
|
||||
if (uri->fragment && *uri->fragment) {
|
||||
if (!uri->query || !*uri->query)
|
||||
evbuffer_add(tmp, "/", 1);
|
||||
|
||||
evbuffer_add(tmp, "#", 1);
|
||||
_URI_ADD(fragment);
|
||||
}
|
||||
|
||||
evbuffer_add(tmp, "\0", 1); /* NUL */
|
||||
|
||||
joined = evbuffer_pullup(tmp, -1);
|
||||
joined_size = evbuffer_get_length(tmp);
|
||||
|
||||
if (joined_size < limit)
|
||||
memcpy(buf, joined, joined_size);
|
||||
else {
|
||||
memcpy(buf, joined, limit-1);
|
||||
*((char *)buf+ limit - 1) = '\0';
|
||||
}
|
||||
evbuffer_free(tmp);
|
||||
|
||||
return (char *)buf;
|
||||
#undef _URI_ADD
|
||||
}
|
||||
|
@ -607,6 +607,38 @@ int evhttp_parse_query__checked_20(const char *uri, struct evkeyvalq *headers);
|
||||
*/
|
||||
char *evhttp_htmlescape(const char *html);
|
||||
|
||||
struct evhttp_uri;
|
||||
|
||||
/**
|
||||
Helper function to parse out uri.
|
||||
|
||||
Parsing a uri like
|
||||
|
||||
scheme://[[user[:pass]@]foo.com[:port]]/[path][?q=test&s=some+thing][#fragment]
|
||||
|
||||
@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 the memory allocated for the uri and parsed data
|
||||
* @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
|
||||
* @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, void *buf, size_t limit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -118,6 +118,20 @@ struct {
|
||||
void (*chunk_cb)(struct evhttp_request *, void *);
|
||||
};
|
||||
|
||||
/**
|
||||
* structure to hold parsed uri
|
||||
*/
|
||||
struct evhttp_uri {
|
||||
char *scheme; /* scheme; e.g http, ftp etc */
|
||||
char *host; /* hostname, or NULL */
|
||||
char *user; /* usename, or NULL */
|
||||
char *pass; /* password, or NULL */
|
||||
int port; /* port, or zero */
|
||||
char *query; /* path + query: e.g. /path/to?param=foo, or NULL */
|
||||
char *fragment; /* fragment or NULL */
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1707,6 +1707,81 @@ 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); \
|
||||
tt_want(strcmp(ret, want) == 0); \
|
||||
} while(0)
|
||||
|
||||
tt_want(evhttp_uri_join(0, 0, 0) == NULL);
|
||||
tt_want(evhttp_uri_join(0, url_tmp, 0) == NULL);
|
||||
tt_want(evhttp_uri_join(0, url_tmp, sizeof(url_tmp)) == NULL);
|
||||
tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL);
|
||||
|
||||
tt_want(evhttp_uri_parse("mailto:foo@bar") == NULL);
|
||||
|
||||
uri = evhttp_uri_parse("http://www.test.com/?q=test");
|
||||
tt_want(strcmp(uri->scheme, "http") == 0);
|
||||
tt_want(strcmp(uri->host, "www.test.com") == 0);
|
||||
tt_want(strcmp(uri->query, "/?q=test") == 0);
|
||||
tt_want(uri->user == NULL);
|
||||
tt_want(uri->pass == NULL);
|
||||
tt_want(uri->port == 0);
|
||||
tt_want(uri->fragment == NULL);
|
||||
TT_URI("http://www.test.com/?q=test");
|
||||
evhttp_uri_free(uri);
|
||||
|
||||
uri = evhttp_uri_parse("ftp://www.test.com/?q=test");
|
||||
tt_want(strcmp(uri->scheme, "ftp") == 0);
|
||||
tt_want(strcmp(uri->host, "www.test.com") == 0);
|
||||
tt_want(strcmp(uri->query, "/?q=test") == 0);
|
||||
tt_want(uri->user == NULL);
|
||||
tt_want(uri->pass == NULL);
|
||||
tt_want(uri->port == 0);
|
||||
tt_want(uri->fragment == NULL);
|
||||
TT_URI("ftp://www.test.com/?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(uri->scheme, "scheme") == 0);
|
||||
tt_want(strcmp(uri->user, "user") == 0);
|
||||
tt_want(strcmp(uri->pass, "pass") == 0);
|
||||
tt_want(strcmp(uri->host, "foo.com") == 0);
|
||||
tt_want(uri->port == 42);
|
||||
tt_want(strcmp(uri->query, "/?q=test&s=some+thing") == 0);
|
||||
tt_want(strcmp(uri->fragment, "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(uri->scheme, "scheme") == 0);
|
||||
tt_want(strcmp(uri->user, "user") == 0);
|
||||
tt_want(uri->pass == NULL);
|
||||
tt_want(strcmp(uri->host, "foo.com") == 0);
|
||||
tt_want(uri->port == 0);
|
||||
tt_want(strcmp(uri->query, "/") == 0);
|
||||
tt_want(strcmp(uri->fragment, "fragment") == 0);
|
||||
TT_URI("scheme://user@foo.com/#fragment");
|
||||
evhttp_uri_free(uri);
|
||||
uri = evhttp_uri_parse("file:///some/path/to/the/file");
|
||||
tt_want(strcmp(uri->scheme, "file") == 0);
|
||||
tt_want(uri->user == NULL);
|
||||
tt_want(uri->pass == NULL);
|
||||
tt_want(strcmp(uri->host, "") == 0);
|
||||
tt_want(uri->port == 0);
|
||||
tt_want(strcmp(uri->query, "/some/path/to/the/file") == 0);
|
||||
tt_want(uri->fragment == NULL);
|
||||
TT_URI("file:///some/path/to/the/file");
|
||||
evhttp_uri_free(uri);
|
||||
}
|
||||
|
||||
static void
|
||||
http_uriencode_test(void *ptr)
|
||||
{
|
||||
@ -2801,6 +2876,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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user