Increase the number of passing tests
This commit is contained in:
@@ -18,7 +18,7 @@ int main(void)
|
||||
}
|
||||
|
||||
char buf[1<<9];
|
||||
ret = url_serialize(&parsed_url, NULL, buf, sizeof(buf));
|
||||
ret = url_serialize(parsed_url, NULL, buf, sizeof(buf));
|
||||
|
||||
// On error, url_serialize returns -1. Errors can
|
||||
// only occur when relative references are involved,
|
||||
|
||||
@@ -36,7 +36,7 @@ int main(void)
|
||||
// Now we can resolve the reference by serializing it with
|
||||
// the base URL
|
||||
char buf[1<<9];
|
||||
ret = url_serialize(&parsed_relative_reference, &parsed_base_url, buf, sizeof(buf));
|
||||
ret = url_serialize(parsed_relative_reference, &parsed_base_url, buf, sizeof(buf));
|
||||
|
||||
// Since url_serialize was called with a non-NULL base URL
|
||||
// argument, it may fail. We need to check for a negative
|
||||
|
||||
+16
-1
@@ -314,7 +314,22 @@ static int run_test(cJSON *json)
|
||||
char buf[1<<10];
|
||||
URL parsed;
|
||||
if (cJSON_IsNull(json_base)) {
|
||||
|
||||
ret = url_remove_white_space(src.ptr, src.len, tmp, (int) sizeof(tmp));
|
||||
if (ret > (int) sizeof(tmp)) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
src.ptr = tmp;
|
||||
src.len = ret;
|
||||
|
||||
ret = url_parse(src.ptr, src.len, NULL, &parsed, 0);
|
||||
|
||||
if (ret > -1)
|
||||
ret = url_serialize(parsed, NULL, buf, (int) sizeof(buf));
|
||||
|
||||
if (ret > -1)
|
||||
ret = url_parse(buf, ret, NULL, &parsed, 0);
|
||||
|
||||
} else {
|
||||
if (!cJSON_IsString(json_base)) {
|
||||
test_printf(" JSON object field \"base\" is not a string or null:\n");
|
||||
@@ -347,7 +362,7 @@ static int run_test(cJSON *json)
|
||||
ret = url_parse(src.ptr, src.len, NULL, &reference, URL_FLAG_ALLOWREF);
|
||||
|
||||
if (ret > -1)
|
||||
ret = url_serialize(&reference, &parsed_base, buf, (int) sizeof(buf));
|
||||
ret = url_serialize(reference, &parsed_base, buf, (int) sizeof(buf));
|
||||
|
||||
if (ret > -1)
|
||||
ret = url_parse(buf, ret, NULL, &parsed, 0);
|
||||
|
||||
Binary file not shown.
@@ -59,6 +59,23 @@ static URL_b32 streq(URL_String a, URL_String b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char to_lower(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A' + 'a';
|
||||
return c;
|
||||
}
|
||||
|
||||
static URL_b32 streqcase(URL_String a, URL_String b)
|
||||
{
|
||||
if (a.len != b.len)
|
||||
return 0;
|
||||
for (int i = 0; i < a.len; i++)
|
||||
if (to_lower(a.ptr[i]) != to_lower(b.ptr[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static URL_b32 is_alpha(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z')
|
||||
@@ -342,10 +359,22 @@ int url_parse_ipv6(char *src, int len, int *pcur, URL_IPv6 *out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static URL_b32 is_special_scheme(URL_String scheme)
|
||||
{
|
||||
return streqcase(scheme, S("ftp"))
|
||||
|| streqcase(scheme, S("file"))
|
||||
|| streqcase(scheme, S("http"))
|
||||
|| streqcase(scheme, S("https"))
|
||||
|| streqcase(scheme, S("ws"))
|
||||
|| streqcase(scheme, S("wss"));
|
||||
}
|
||||
|
||||
int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
{
|
||||
int cur = pcur ? *pcur : 0;
|
||||
|
||||
out->flags = flags;
|
||||
|
||||
// Parse scheme characters until the end of the string,
|
||||
// a character not allowed in the scheme, or ':'. If the
|
||||
// found character wasn't ':', set the scheme to empty
|
||||
@@ -385,13 +414,30 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
// info or the domain until we hit a @ character or
|
||||
// a character not allowed in the user info, se we
|
||||
// need to backtrack in a similar way to the scheme
|
||||
int authority_off = cur;
|
||||
out->no_authority = 1;
|
||||
out->no_userinfo = 1;
|
||||
int authority_off = cur;
|
||||
if (len - cur > 1 && src[cur] == '/' && src[cur+1] == '/') {
|
||||
cur += 2; // Consume the //
|
||||
out->no_authority = 0;
|
||||
} else {
|
||||
// The WHATWG standard has a special case for special
|
||||
// schemes (ftp, http, https, ...) where if a scheme
|
||||
// if followed by a single "/", it behaves like "//".
|
||||
//
|
||||
// TODO: This rule does not apply if there is a base
|
||||
// URL with a proper authority. Unfortunately,
|
||||
// we don't have access to base URL information
|
||||
// at this point.
|
||||
if ((flags & URL_FLAG_RFC3986) == 0) {
|
||||
if (is_special_scheme(out->scheme) && !streqcase(out->scheme, S("file")) && cur < len && src[cur] == '/') {
|
||||
cur++;
|
||||
out->no_authority = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!out->no_authority) {
|
||||
out->username = EMPTY;
|
||||
out->password = EMPTY;
|
||||
if (cur < len && (is_userinfo(src[cur]) || src[cur] == ':')) {
|
||||
@@ -512,7 +558,6 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
// TODO: What if the dot is percent-encoded?
|
||||
if (src[cur-1] == '.')
|
||||
cur--;
|
||||
|
||||
} else {
|
||||
out->host_type = URL_HOST_EMPTY;
|
||||
}
|
||||
@@ -528,7 +573,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
// The WHATWG standard forbids port numbers with the
|
||||
// "file" protocol.
|
||||
if ((flags & URL_FLAG_RFC3986) == 0) {
|
||||
if (streq(out->scheme, S("file")))
|
||||
if (streqcase(out->scheme, S("file")))
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -550,7 +595,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
// part of the path and not the authority if userinfo,
|
||||
// host, and port are missing
|
||||
if ((flags & URL_FLAG_RFC3986) == 0) {
|
||||
if ((streq(out->scheme, S("http")) || streq(out->scheme, S("https")))
|
||||
if ((streqcase(out->scheme, S("http")) || streqcase(out->scheme, S("https")))
|
||||
&& out->username.len == 0
|
||||
&& out->password.len == 0
|
||||
&& out->host_type == URL_HOST_EMPTY
|
||||
@@ -600,13 +645,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
// For a subset of protocols, WHATWG sets the default
|
||||
// path to "/"
|
||||
if ((flags & URL_FLAG_RFC3986) == 0) {
|
||||
if (out->path.len == 0 && (
|
||||
streq(out->scheme, S("ftp")) ||
|
||||
streq(out->scheme, S("file")) ||
|
||||
streq(out->scheme, S("http")) ||
|
||||
streq(out->scheme, S("https")) ||
|
||||
streq(out->scheme, S("ws")) ||
|
||||
streq(out->scheme, S("wss"))))
|
||||
if (out->path.len == 0 && is_special_scheme(out->scheme))
|
||||
out->path = S("/");
|
||||
}
|
||||
|
||||
@@ -675,19 +714,22 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
|
||||
|
||||
typedef struct {
|
||||
int count;
|
||||
URL_b32 first_slash;
|
||||
URL_b32 trailing_slash;
|
||||
URL_String stack[PATH_COMPONENT_LIMIT];
|
||||
} PathComps;
|
||||
|
||||
static int
|
||||
resolve_dots_and_append_comps(PathComps *comps, URL_String src)
|
||||
{
|
||||
URL_b32 is_absolute = 0;
|
||||
if (src.len == 0)
|
||||
return 0;
|
||||
|
||||
if (src.len > 0 && src.ptr[0] == '/') {
|
||||
is_absolute = 1;
|
||||
if (comps->count == 0)
|
||||
comps->first_slash = 1;
|
||||
src.ptr++;
|
||||
src.len--;
|
||||
if (src.len == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@@ -698,24 +740,16 @@ resolve_dots_and_append_comps(PathComps *comps, URL_String src)
|
||||
i++;
|
||||
int len = i - off;
|
||||
|
||||
if (len == 0)
|
||||
return -1; // Empty component
|
||||
|
||||
URL_String comp = { src.ptr + off, len };
|
||||
if (comp.len == 2 && comp.ptr[0] == '.' && comp.ptr[1] == '.') {
|
||||
if (comps->count == 0) {
|
||||
// For absolute paths, ".." at root is ignored (stays at root)
|
||||
// For relative paths, ".." with no components references parent, which is invalid
|
||||
if (!is_absolute)
|
||||
return -1;
|
||||
// Otherwise, ignore the ".." (absolute path, already at root)
|
||||
} else {
|
||||
if (streq(comp, S(".."))) {
|
||||
if (comps->count > 0)
|
||||
comps->count--;
|
||||
} else {
|
||||
if (!streq(comp, S("."))) {
|
||||
if (comps->count == PATH_COMPONENT_LIMIT)
|
||||
return -1; // To many components
|
||||
comps->stack[comps->count++] = comp;
|
||||
}
|
||||
} else if (comp.len != 1 || comp.ptr[0] != '.') {
|
||||
if (comps->count == PATH_COMPONENT_LIMIT)
|
||||
return -1; // To many components
|
||||
comps->stack[comps->count++] = comp;
|
||||
}
|
||||
|
||||
if (i == src.len)
|
||||
@@ -728,6 +762,7 @@ resolve_dots_and_append_comps(PathComps *comps, URL_String src)
|
||||
break;
|
||||
}
|
||||
|
||||
comps->trailing_slash = (src.len > 0 && src.ptr[src.len-1] == '/');
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -808,17 +843,35 @@ static void append_fragment(Builder *b, URL_String fragment)
|
||||
}
|
||||
}
|
||||
|
||||
int url_serialize(URL *url, URL *base, char *dst, int cap)
|
||||
int url_serialize(URL url, URL *base, char *dst, int cap)
|
||||
{
|
||||
if (base != NULL && base->scheme.len == 0)
|
||||
return -1; // Base is not an absolute URL
|
||||
|
||||
if ((url.flags & URL_FLAG_RFC3986) == 0) {
|
||||
if (base
|
||||
&& streqcase(url.scheme, base->scheme)
|
||||
&& is_special_scheme(url.scheme)
|
||||
&& url.no_authority) {
|
||||
url.scheme = EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
Builder b = { dst, cap, 0 };
|
||||
if (url->scheme.len > 0) {
|
||||
append_scheme(&b, url->scheme);
|
||||
append_authority(&b, *url);
|
||||
append(&b, url->path);
|
||||
append_query(&b, url->query);
|
||||
if (url.scheme.len > 0) {
|
||||
append_scheme(&b, url.scheme);
|
||||
append_authority(&b, url);
|
||||
PathComps comps = {0};
|
||||
if (resolve_dots_and_append_comps(&comps, url.path) < 0)
|
||||
return -1;
|
||||
for (int i = 0; i < comps.count; i++) {
|
||||
if (i > 0 || comps.first_slash)
|
||||
append(&b, S("/"));
|
||||
append(&b, comps.stack[i]);
|
||||
}
|
||||
if (comps.trailing_slash)
|
||||
append(&b, S("/"));
|
||||
append_query(&b, url.query);
|
||||
} else {
|
||||
if (base == NULL) {
|
||||
// No base URL provided, which means the
|
||||
@@ -827,34 +880,37 @@ int url_serialize(URL *url, URL *base, char *dst, int cap)
|
||||
}
|
||||
ASSERT(base->scheme.len > 0);
|
||||
append_scheme(&b, base->scheme);
|
||||
if (!url->no_authority) {
|
||||
append_authority(&b, *url);
|
||||
if (!url.no_authority) {
|
||||
append_authority(&b, url);
|
||||
PathComps comps = {0};
|
||||
if (resolve_dots_and_append_comps(&comps, url->path) < 0)
|
||||
if (resolve_dots_and_append_comps(&comps, url.path) < 0)
|
||||
return -1;
|
||||
for (int i = 0; i < comps.count; i++) {
|
||||
append(&b, S("/"));
|
||||
if (i > 0 || comps.first_slash)
|
||||
append(&b, S("/"));
|
||||
append(&b, comps.stack[i]);
|
||||
}
|
||||
append_query(&b, url->query);
|
||||
if (comps.trailing_slash)
|
||||
append(&b, S("/"));
|
||||
append_query(&b, url.query);
|
||||
} else {
|
||||
append_authority(&b, *base);
|
||||
if (url->path.len == 0) {
|
||||
if (url.path.len == 0) {
|
||||
append(&b, base->path);
|
||||
if (url->query.len > 0) {
|
||||
append_query(&b, url->query);
|
||||
if (url.query.len > 0) {
|
||||
append_query(&b, url.query);
|
||||
} else {
|
||||
append_query(&b, base->query);
|
||||
}
|
||||
} else {
|
||||
ASSERT(url->path.len > 0);
|
||||
ASSERT(url.path.len > 0);
|
||||
PathComps comps = {0};
|
||||
if (url->path.ptr[0] == '/') {
|
||||
if (resolve_dots_and_append_comps(&comps, url->path) < 0)
|
||||
if (url.path.ptr[0] == '/') {
|
||||
if (resolve_dots_and_append_comps(&comps, url.path) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
if (!base->no_authority && base->path.len == 0) {
|
||||
if (resolve_dots_and_append_comps(&comps, url->path) < 0)
|
||||
if (resolve_dots_and_append_comps(&comps, url.path) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
if (resolve_dots_and_append_comps(&comps, base->path) < 0)
|
||||
@@ -862,19 +918,22 @@ int url_serialize(URL *url, URL *base, char *dst, int cap)
|
||||
if (comps.count > 0) {
|
||||
comps.count--;
|
||||
}
|
||||
if (resolve_dots_and_append_comps(&comps, url->path) < 0)
|
||||
if (resolve_dots_and_append_comps(&comps, url.path) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < comps.count; i++) {
|
||||
append(&b, S("/"));
|
||||
if (i > 0 || comps.first_slash)
|
||||
append(&b, S("/"));
|
||||
append(&b, comps.stack[i]);
|
||||
}
|
||||
append_query(&b, url->query);
|
||||
if (comps.trailing_slash)
|
||||
append(&b, S("/"));
|
||||
append_query(&b, url.query);
|
||||
}
|
||||
}
|
||||
}
|
||||
append_fragment(&b, url->fragment);
|
||||
append_fragment(&b, url.fragment);
|
||||
|
||||
return b.len;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
|
||||
// Flags used to parse this URL
|
||||
int flags;
|
||||
|
||||
// These flags are used to differentiate between
|
||||
// an empty userinfo/authority and an undefined one.
|
||||
// For instance:
|
||||
@@ -169,6 +172,6 @@ int url_percent_decode(URL_String str, char *dst, int cap);
|
||||
// set to NULL.
|
||||
// Note that this can be used to normalize an URL, even
|
||||
// though it's just a best-effor normalization for now.
|
||||
int url_serialize(URL *url, URL *base, char *dst, int cap);
|
||||
int url_serialize(URL url, URL *base, char *dst, int cap);
|
||||
|
||||
#endif // URL_INCLUDED
|
||||
|
||||
Reference in New Issue
Block a user