3 changed files with 58 additions and 27 deletions
+2 -4
View File
@@ -1,5 +1,5 @@
# url.c # url.c
This is a small library to parse and manipulate URLs in conformance to RFC 3986 and WHATWG. This is a small library to parse and manipulate URLs in conformance to RFC 3986 and (most of) the WHATWG specification.
It features It features
* No allocations * No allocations
@@ -15,6 +15,4 @@ You just copy paste `url.h` and `url.c` in your source tree and compile them as
Some example programs are provided in the `examples/` folder. To compile them, run `build.bat` on Windows or `build.sh` on Linux. Some example programs are provided in the `examples/` folder. To compile them, run `build.bat` on Windows or `build.sh` on Linux.
# Testing # Testing
The parser and reference resolver is tested against the URL section of the [web platform tests](https://github.com/web-platform-tests/wpt). Note that not all tests pass. This is due to the parser not doing any normalization and not supporting more niche features such as converting `\` characters to `/` or supporting drive letters like `C:` in the URL's path. The parser and reference resolver is tested against the URL section of the [web platform tests](https://github.com/web-platform-tests/wpt), which validates implementations that adhere to the WHATWG specification (what the major browsers actually do). To run the test suite, `cd` into `tests/` and compile the test runner using `build.sh` (Linux) or `build.bat` (Windows). Then, run `./test.out` (Linux) or `./test.exe` (Windows). Note that not all tests pass, which is to be expected as adherence to the WHATWG specification is a work-in-progress.
To run the test suite, `cd` into `tests/` and compile the test runner using `build.sh` (Linux) or `build.bat` (Windows). Then, run `./test.out` (Linux) or `./test.exe` (Windows).
+26
View File
@@ -31,6 +31,10 @@ void test_printf(char *fmt, ...)
sizeof(test_output_buffer) - test_output_buffer_used, sizeof(test_output_buffer) - test_output_buffer_used,
fmt, args); fmt, args);
va_end(args); va_end(args);
assert(len > -1);
if (len > (int) sizeof(test_output_buffer) - test_output_buffer_used)
len = sizeof(test_output_buffer) - test_output_buffer_used;
test_output_buffer_used += len; test_output_buffer_used += len;
} }
@@ -198,6 +202,10 @@ static int json_to_url(cJSON *json, URL *url)
url->fragment.len--; url->fragment.len--;
} }
if (port.len == 0) {
url->no_port = 1;
url->port = 0;
} else {
char tmp[8]; char tmp[8];
if (port.len >= (int) sizeof(tmp)) { if (port.len >= (int) sizeof(tmp)) {
test_printf(" JSON object's port field is longer than expected\n"); test_printf(" JSON object's port field is longer than expected\n");
@@ -206,7 +214,9 @@ static int json_to_url(cJSON *json, URL *url)
memcpy(tmp, port.ptr, port.len); memcpy(tmp, port.ptr, port.len);
tmp[port.len] = 0; tmp[port.len] = 0;
url->no_port = 0;
url->port = atoi(tmp); url->port = atoi(tmp);
}
return 0; return 0;
} }
@@ -265,6 +275,20 @@ static int compare_urls(URL parsed, URL expected)
ok = false; ok = false;
} }
if (parsed.no_port != expected.no_port) {
if (ok) test_printf("\n");
if (parsed.no_port) {
test_printf(" port mismatch\n");
test_printf(" parsed (empty)\n");
test_printf(" expected [%d]\n", expected.port);
} else {
test_printf(" port mismatch\n");
test_printf(" parsed [%d]\n", parsed.port);
test_printf(" expected (empty)\n");
}
ok = false;
}
if (parsed.port != expected.port) { if (parsed.port != expected.port) {
if (ok) test_printf("\n"); if (ok) test_printf("\n");
test_printf(" port mismatch\n"); test_printf(" port mismatch\n");
@@ -488,6 +512,7 @@ int main(void)
if (!cJSON_IsArray(root)) { if (!cJSON_IsArray(root)) {
fprintf(stderr, "Error: %s doesn't contain a JSON array as expected", path); fprintf(stderr, "Error: %s doesn't contain a JSON array as expected", path);
cJSON_Delete(root);
free(buf); free(buf);
return -1; return -1;
} }
@@ -540,6 +565,7 @@ int main(void)
printf(" failed : %d\n", num_failed); printf(" failed : %d\n", num_failed);
printf(" aborted: %d\n", num_aborted); printf(" aborted: %d\n", num_aborted);
cJSON_Delete(root);
free(buf); free(buf);
return 0; return 0;
} }
+11 -4
View File
@@ -580,6 +580,9 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
out->no_port = 1; out->no_port = 1;
out->port = 0; out->port = 0;
if (cur < len && src[cur] == ':') { if (cur < len && src[cur] == ':') {
cur++; // Consume the ':'
if (cur < len && is_digit(src[cur])) {
// The WHATWG standard forbids port numbers with the // The WHATWG standard forbids port numbers with the
// "file" protocol. // "file" protocol.
@@ -588,17 +591,15 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags)
return -1; return -1;
} }
cur++; // Consume the ':'
out->no_port = 0; out->no_port = 0;
do {
while (cur < len && is_digit(src[cur])) {
int n = src[cur] - '0'; int n = src[cur] - '0';
cur++; cur++;
if (out->port > (UINT16_MAX - n) / 10) if (out->port > (UINT16_MAX - n) / 10)
return -1; // Overflow return -1; // Overflow
out->port = out->port * 10 + n; out->port = out->port * 10 + n;
} while (cur < len && is_digit(src[cur]));
} }
} }
@@ -856,6 +857,8 @@ 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)
{ {
ASSERT(cap == 0 || dst != NULL);
if (base != NULL && base->scheme.len == 0) if (base != NULL && base->scheme.len == 0)
return -1; // Base is not an absolute URL return -1; // Base is not an absolute URL
@@ -959,6 +962,8 @@ static URL_b32 is_white_space(char c)
int url_remove_white_space(char *src, int len, char *dst, int cap) int url_remove_white_space(char *src, int len, char *dst, int cap)
{ {
ASSERT(cap == 0 || dst != NULL);
while (len > 0 && is_white_space(src[0])) { while (len > 0 && is_white_space(src[0])) {
src++; src++;
len--; len--;
@@ -981,6 +986,8 @@ int url_remove_white_space(char *src, int len, char *dst, int cap)
int url_percent_decode(URL_String str, char *dst, int cap) int url_percent_decode(URL_String str, char *dst, int cap)
{ {
ASSERT(cap == 0 || dst != NULL);
char *src = str.ptr; char *src = str.ptr;
int len = str.len; int len = str.len;
int rd = 0; int rd = 0;