/*
 * Copyright (c) 2014,2015 DeNA Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
#include "../../test.h"
#include "../../../../lib/common/url.c"

static void test_normalize_path(void)
{
    h2o_mem_pool_t pool;

    h2o_mem_init_pool(&pool);

    size_t q;
    size_t *norm_indexes = NULL;
    h2o_iovec_t input;
    h2o_iovec_t b;

    input = h2o_iovec_init(NULL, 0);
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("a"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 2);
    ok(memcmp(b.base, H2O_STRLIT("/a")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 0);
    ok(norm_indexes[1] == 1);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("aa"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 3);
    ok(memcmp(b.base, H2O_STRLIT("/aa")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 0);
    ok(norm_indexes[1] == 1);
    ok(norm_indexes[2] == 2);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("/."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/./"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/.."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/../"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/abc")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("/abc"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/abc")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("/abc/../def"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/def")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 9);
    ok(norm_indexes[2] == 10);
    ok(norm_indexes[3] == 11);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/../../def"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/def")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 12);
    ok(norm_indexes[2] == 13);
    ok(norm_indexes[3] == 14);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/./def"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 8);
    ok(memcmp(b.base, H2O_STRLIT("/abc/def")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    ok(norm_indexes[5] == 8);
    ok(norm_indexes[6] == 9);
    ok(norm_indexes[7] == 10);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/././def"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 8);
    ok(memcmp(b.base, H2O_STRLIT("/abc/def")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    ok(norm_indexes[5] == 10);
    ok(norm_indexes[6] == 11);
    ok(norm_indexes[7] == 12);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/ghi/../.."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 5);
    ok(memcmp(b.base, H2O_STRLIT("/abc/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/./."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 9);
    ok(memcmp(b.base, H2O_STRLIT("/abc/def/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    ok(norm_indexes[5] == 6);
    ok(norm_indexes[6] == 7);
    ok(norm_indexes[7] == 8);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/ghi/../."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 9);
    ok(memcmp(b.base, H2O_STRLIT("/abc/def/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    ok(norm_indexes[5] == 6);
    ok(norm_indexes[6] == 7);
    ok(norm_indexes[7] == 8);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/./.."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 5);
    ok(memcmp(b.base, H2O_STRLIT("/abc/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/.."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 5);
    ok(memcmp(b.base, H2O_STRLIT("/abc/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc/def/."));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 9);
    ok(memcmp(b.base, H2O_STRLIT("/abc/def/")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    ok(norm_indexes[4] == 5);
    ok(norm_indexes[5] == 6);
    ok(norm_indexes[6] == 7);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc?xx"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/abc")) == 0);
    ok(q == 4);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("/abc/../def?xx"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/def")) == 0);
    ok(q == 11);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 9);
    ok(norm_indexes[2] == 10);
    ok(norm_indexes[3] == 11);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/a%62c"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/abc")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 5);
    ok(norm_indexes[3] == 6);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/a%6"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/a%6")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/a%6?"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 4);
    ok(memcmp(b.base, H2O_STRLIT("/a%6")) == 0);
    ok(q == 4);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 2);
    ok(norm_indexes[2] == 3);
    ok(norm_indexes[3] == 4);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/%25"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 2);
    ok(memcmp(b.base, H2O_STRLIT("/%")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes != NULL);
    ok(norm_indexes[0] == 1);
    ok(norm_indexes[1] == 4);
    norm_indexes = NULL;

    input = h2o_iovec_init(H2O_STRLIT("/abc//"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 6);
    ok(memcmp(b.base, H2O_STRLIT("/abc//")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("/abc//d"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 7);
    ok(memcmp(b.base, H2O_STRLIT("/abc//d")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("//"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 2);
    ok(memcmp(b.base, H2O_STRLIT("//")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("//abc"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 5);
    ok(memcmp(b.base, H2O_STRLIT("//abc")) == 0);
    ok(q == SIZE_MAX);
    ok(norm_indexes == NULL);

    input = h2o_iovec_init(H2O_STRLIT("?abc"));
    b = h2o_url_normalize_path(&pool, input.base, input.len, &q, &norm_indexes);
    ok(b.len == 1);
    ok(memcmp(b.base, H2O_STRLIT("/")) == 0);

    h2o_mem_clear_pool(&pool);
}

static void test_hostport(void)
{
    h2o_iovec_t input, host;
    uint16_t port;
    const char *ret;

    input = h2o_iovec_init(H2O_STRLIT("127.0.0.1"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(ret == input.base + input.len);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("127.0.0.1")));
    ok(port == 65535);

    input = h2o_iovec_init(H2O_STRLIT("127.0.0.1/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "/") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("127.0.0.1")));
    ok(port == 65535);

    input = h2o_iovec_init(H2O_STRLIT("127.0.0.1?"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "?") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("127.0.0.1")));
    ok(port == 65535);

    input = h2o_iovec_init(H2O_STRLIT("127.0.0.1:8081/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "/") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("127.0.0.1")));
    ok(port == 8081);

    input = h2o_iovec_init(H2O_STRLIT("127.0.0.1:8081?"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "?") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("127.0.0.1")));
    ok(port == 8081);

    input = h2o_iovec_init(H2O_STRLIT("[::ffff:192.0.2.1]:8081/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "/") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("::ffff:192.0.2.1")));
    ok(port == 8081);

    input = h2o_iovec_init(H2O_STRLIT("[::ffff:192.0.2.1]:8081?"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(strcmp(ret, "?") == 0);
    ok(h2o_memis(host.base, host.len, H2O_STRLIT("::ffff:192.0.2.1")));
    ok(port == 8081);

    input = h2o_iovec_init(H2O_STRLIT("[::ffff:192.0.2.1:8081/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(ret == NULL);

    input = h2o_iovec_init(H2O_STRLIT(":8081/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(ret == NULL);

    input = h2o_iovec_init(H2O_STRLIT("[]:8081/"));
    ret = h2o_url_parse_hostport(input.base, input.len, &host, &port);
    ok(ret == NULL);
}

static void test_parse(void)
{
    h2o_mem_pool_t pool;
    h2o_url_t parsed;
    int ret;

    h2o_mem_init_pool(&pool);

    ret = h2o_url_parse(&pool, "http://example.com/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com")));
    ok(parsed._port == 65535);
    ok(h2o_url_get_port(&parsed) == 80);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    ret = h2o_url_parse(&pool, "http://example.com", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com")));
    ok(parsed._port == 65535);
    ok(h2o_url_get_port(&parsed) == 80);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/")));

    ret = h2o_url_parse(&pool, "http://example.com:81/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com:81")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com")));
    ok(parsed._port == 81);
    ok(h2o_url_get_port(&parsed) == 81);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    ret = h2o_url_parse(&pool, "http://example.com:81", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com:81")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com")));
    ok(parsed._port == 81);
    ok(h2o_url_get_port(&parsed) == 81);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/")));

    ret = h2o_url_parse(&pool, "https://example.com/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("example.com")));
    ok(parsed._port == 65535);
    ok(h2o_url_get_port(&parsed) == 443);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    ret = h2o_url_parse(&pool, "http:/abc", SIZE_MAX, &parsed);
    ok(ret != 0);

    ret = h2o_url_parse(&pool, "ftp://example.com/abc", SIZE_MAX, &parsed);
    ok(ret != 0);

    ret = h2o_url_parse(&pool, "http://abc:111111/def", SIZE_MAX, &parsed);
    ok(ret != 0);

    ret = h2o_url_parse(&pool, "http://[::ffff:192.0.2.128]", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128")));
    ok(parsed._port == 65535);
    ok(h2o_url_get_port(&parsed) == 80);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/")));

    ret = h2o_url_parse(&pool, "https://[::ffff:192.0.2.128]/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128")));
    ok(parsed._port == 65535);
    ok(h2o_url_get_port(&parsed) == 443);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    ret = h2o_url_parse(&pool, "https://[::ffff:192.0.2.128]:111/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("[::ffff:192.0.2.128]:111")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("::ffff:192.0.2.128")));
    ok(parsed._port == 111);
    ok(h2o_url_get_port(&parsed) == 111);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    ret = h2o_url_parse(&pool, "http://example.com:8080?abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/?abc")));

    h2o_mem_clear_pool(&pool);
}

static void test_parse_relative(void)
{
    h2o_mem_pool_t pool;
    h2o_url_t parsed;
    int ret;

    h2o_mem_init_pool(&pool);

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == NULL);
    ok(parsed.authority.base == NULL);
    ok(parsed.host.base == NULL);
    ok(parsed._port == 65535);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("abc")));

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "/abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == NULL);
    ok(parsed.authority.base == NULL);
    ok(parsed.host.base == NULL);
    ok(parsed._port == 65535);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/abc")));

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "http:abc", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTP);
    ok(parsed.authority.base == NULL);
    ok(parsed.host.base == NULL);
    ok(parsed._port == 65535);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("abc")));

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "//host", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == NULL);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host")));
    ok(parsed._port == 65535);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/")));

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "//host:12345/path", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == NULL);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host:12345")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host")));
    ok(parsed._port == 12345);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/path")));

    memset(&parsed, 0x55, sizeof(parsed));
    ret = h2o_url_parse_relative(&pool, "https://host:12345/path", SIZE_MAX, &parsed);
    ok(ret == 0);
    ok(parsed.scheme == &H2O_URL_SCHEME_HTTPS);
    ok(h2o_memis(parsed.authority.base, parsed.authority.len, H2O_STRLIT("host:12345")));
    ok(h2o_memis(parsed.host.base, parsed.host.len, H2O_STRLIT("host")));
    ok(parsed._port == 12345);
    ok(h2o_memis(parsed.path.base, parsed.path.len, H2O_STRLIT("/path")));

    h2o_mem_clear_pool(&pool);
}

static void test_resolve(void)
{
    h2o_mem_pool_t pool;
    h2o_url_t base, relative, resolved;
    h2o_iovec_t final;
    int ret;

    h2o_mem_init_pool(&pool);

    ret = h2o_url_parse(&pool, "http://example.com/dir/index.html", SIZE_MAX, &base);
    ok(ret == 0);

    ret = h2o_url_parse_relative(&pool, "../assets/jquery.js", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/assets/jquery.js")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/assets/jquery.js")));

    ret = h2o_url_parse_relative(&pool, "foo.html", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/dir/foo.html")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/dir/foo.html")));

    ret = h2o_url_parse_relative(&pool, "./bar.txt", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/dir/bar.txt")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/dir/bar.txt")));

    ret = h2o_url_parse_relative(&pool, "../../../traverse", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/traverse")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/traverse")));

    ret = h2o_url_parse_relative(&pool, "http:foo.html", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/dir/foo.html")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/dir/foo.html")));

    ret = h2o_url_parse_relative(&pool, "http:/icon.ico", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/icon.ico")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 80);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/icon.ico")));

    ret = h2o_url_parse_relative(&pool, "https:/icon.ico", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("https://example.com/icon.ico")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTPS);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.com")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.com")));
    ok(resolved._port == 65535);
    ok(h2o_url_get_port(&resolved) == 443);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/icon.ico")));

    ret = h2o_url_parse_relative(&pool, "//example.jp:81/icon.ico", SIZE_MAX, &relative);
    ok(ret == 0);
    final = h2o_url_resolve(&pool, &base, &relative, &resolved);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.jp:81/icon.ico")));
    ok(resolved.scheme == &H2O_URL_SCHEME_HTTP);
    ok(h2o_memis(resolved.authority.base, resolved.authority.len, H2O_STRLIT("example.jp:81")));
    ok(h2o_memis(resolved.host.base, resolved.host.len, H2O_STRLIT("example.jp")));
    ok(resolved._port == 81);
    ok(h2o_url_get_port(&resolved) == 81);
    ok(h2o_memis(resolved.path.base, resolved.path.len, H2O_STRLIT("/icon.ico")));

    final = h2o_url_stringify(&pool, &base);
    ok(h2o_memis(final.base, final.len, H2O_STRLIT("http://example.com/dir/index.html")));

    h2o_mem_clear_pool(&pool);
}

void test_lib__common__url_c(void)
{
    subtest("normalize_path", test_normalize_path);
    subtest("hostport", test_hostport);
    subtest("parse", test_parse);
    subtest("parse_relative", test_parse_relative);
    subtest("resolve", test_resolve);
}
