summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/t/00unit/lib/common
diff options
context:
space:
mode:
Diffstat (limited to 'web/server/h2o/libh2o/t/00unit/lib/common')
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/cache.c84
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/hostinfo.c53
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/multithread.c157
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/serverutil.c95
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/socket.c171
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/string.c335
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/time.c48
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/common/url.c662
8 files changed, 1605 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/cache.c b/web/server/h2o/libh2o/t/00unit/lib/common/cache.c
new file mode 100644
index 00000000..ea18cbc4
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/cache.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku
+ *
+ * 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/cache.c"
+
+static size_t bytes_destroyed;
+
+static void on_destroy(h2o_iovec_t vec)
+{
+ bytes_destroyed += vec.len;
+}
+
+void test_lib__common__cache_c(void)
+{
+ h2o_cache_t *cache = h2o_cache_create(H2O_CACHE_FLAG_EARLY_UPDATE, 1024, 1000, on_destroy);
+ uint64_t now = 0;
+ h2o_iovec_t key = {H2O_STRLIT("key")};
+ h2o_cache_ref_t *ref;
+
+ /* fetch "key" */
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(ref == NULL);
+
+ /* set "key" => "value" */
+ h2o_cache_set(cache, now, key, 0, h2o_iovec_init(H2O_STRLIT("value")));
+
+ /* delete "key" */
+ h2o_cache_delete(cache, now, key, 0);
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(ref == NULL);
+
+ /* set "key" => "value" */
+ h2o_cache_set(cache, now, key, 0, h2o_iovec_init(H2O_STRLIT("value")));
+
+ /* fetch "key" */
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(h2o_memis(ref->value.base, ref->value.len, H2O_STRLIT("value")));
+ h2o_cache_release(cache, ref);
+
+ /* proceed 999ms */
+ now += 999;
+
+ /* should fail to fetch "key" */
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(ref == NULL);
+
+ /* refetch should succeed */
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(h2o_memis(ref->value.base, ref->value.len, H2O_STRLIT("value")));
+ h2o_cache_release(cache, ref);
+
+ /* set "key" to "value2" */
+ h2o_cache_set(cache, now, key, 0, h2o_iovec_init(H2O_STRLIT("value2")));
+
+ /* fetch */
+ ref = h2o_cache_fetch(cache, now, key, 0);
+ ok(h2o_memis(ref->value.base, ref->value.len, H2O_STRLIT("value2")));
+ h2o_cache_release(cache, ref);
+
+ ok(bytes_destroyed == 10);
+
+ h2o_cache_destroy(cache);
+
+ ok(bytes_destroyed == 16);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/hostinfo.c b/web/server/h2o/libh2o/t/00unit/lib/common/hostinfo.c
new file mode 100644
index 00000000..9d8e5fe7
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/hostinfo.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 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 <arpa/inet.h>
+#include "../../test.h"
+#include "../../../../lib/common/hostinfo.c"
+
+static void test_aton(void)
+{
+ struct in_addr addr;
+
+ memset(&addr, 0x55, sizeof(addr));
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("127.0.0.1")}, &addr) == 0);
+ ok(ntohl(addr.s_addr) == 0x7f000001);
+
+ memset(&addr, 0x55, sizeof(addr));
+ ok(h2o_hostinfo_aton((h2o_iovec_t){"127.0.0.12", sizeof("127.0.0.1") - 1}, &addr) == 0);
+ ok(ntohl(addr.s_addr) == 0x7f000001);
+
+ memset(&addr, 0x55, sizeof(addr));
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("255.001.002.128")}, &addr) == 0);
+ ok(ntohl(addr.s_addr) == 0xff010280);
+
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("127.0.0.z")}, &addr) != 0);
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("256.0.0.0")}, &addr) != 0);
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("0001.0.0.0")}, &addr) != 0);
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("0.0..1")}, &addr) != 0);
+ ok(h2o_hostinfo_aton((h2o_iovec_t){H2O_STRLIT("1.0.0.0.")}, &addr) != 0);
+}
+
+void test_lib__common__hostinfo_c(void)
+{
+ /* TODO add tests for h2o_hostinfo_getaddr and related */
+ subtest("aton", test_aton);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/multithread.c b/web/server/h2o/libh2o/t/00unit/lib/common/multithread.c
new file mode 100644
index 00000000..683e489a
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/multithread.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014 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 <stdlib.h>
+#include "../../test.h"
+#include "../../../../lib/common/multithread.c"
+
+struct st_thread_t {
+ h2o_loop_t *loop;
+ h2o_multithread_queue_t *queue;
+};
+
+struct {
+ h2o_loop_t *loop;
+ h2o_multithread_queue_t *queue;
+ h2o_multithread_receiver_t pong_receiver;
+ h2o_multithread_receiver_t shutdown_receiver;
+ int received_shutdown;
+} main_thread;
+
+struct {
+ h2o_loop_t *loop;
+ h2o_multithread_queue_t *queue;
+ h2o_multithread_receiver_t ping_receiver;
+ size_t num_ping_received;
+ int should_exit;
+} worker_thread;
+
+static void send_empty_message(h2o_multithread_receiver_t *receiver)
+{
+ h2o_multithread_message_t *message = h2o_mem_alloc(sizeof(*message));
+ *message = (h2o_multithread_message_t){{NULL}};
+ h2o_multithread_send_message(receiver, message);
+}
+
+static void pop_empty_message(h2o_linklist_t *list)
+{
+ h2o_multithread_message_t *message = H2O_STRUCT_FROM_MEMBER(h2o_multithread_message_t, link, list->next);
+ h2o_linklist_unlink(&message->link);
+ free(message);
+}
+
+static void on_ping(h2o_multithread_receiver_t *receiver, h2o_linklist_t *list)
+{
+ while (!h2o_linklist_is_empty(list)) {
+ pop_empty_message(list);
+ if (++worker_thread.num_ping_received < 100) {
+ send_empty_message(&main_thread.pong_receiver);
+ } else {
+ send_empty_message(&main_thread.shutdown_receiver);
+ worker_thread.should_exit = 1;
+ }
+ }
+}
+
+static void on_pong(h2o_multithread_receiver_t *receiver, h2o_linklist_t *list)
+{
+ while (!h2o_linklist_is_empty(list)) {
+ pop_empty_message(list);
+ send_empty_message(&worker_thread.ping_receiver);
+ }
+}
+
+static void on_shutdown(h2o_multithread_receiver_t *receiver, h2o_linklist_t *list)
+{
+ while (!h2o_linklist_is_empty(list))
+ pop_empty_message(list);
+ main_thread.received_shutdown = 1;
+}
+
+#if H2O_USE_LIBUV
+static h2o_loop_t *create_loop(void)
+{
+ h2o_loop_t *loop = h2o_mem_alloc(sizeof(*loop));
+ uv_loop_init(loop);
+ return loop;
+}
+
+static void destroy_loop(h2o_loop_t *loop)
+{
+ uv_run(loop, UV_RUN_NOWAIT);
+ uv_loop_close(loop);
+ free(loop);
+}
+#else
+#define create_loop h2o_evloop_create
+#define destroy_loop(loop) (0) /* FIXME */
+#endif
+
+static void *worker_main(void *_unused)
+{
+ while (!worker_thread.should_exit) {
+#if H2O_USE_LIBUV
+ uv_run(worker_thread.loop, UV_RUN_ONCE);
+#else
+ h2o_evloop_run(worker_thread.loop, INT32_MAX);
+#endif
+ }
+
+ return NULL;
+}
+
+void test_lib__common__multithread_c(void)
+{
+ pthread_t tid;
+
+ main_thread.loop = create_loop();
+ main_thread.queue = h2o_multithread_create_queue(main_thread.loop);
+ h2o_multithread_register_receiver(main_thread.queue, &main_thread.pong_receiver, on_pong);
+ h2o_multithread_register_receiver(main_thread.queue, &main_thread.shutdown_receiver, on_shutdown);
+ worker_thread.loop = create_loop();
+ worker_thread.queue = h2o_multithread_create_queue(worker_thread.loop);
+ h2o_multithread_register_receiver(worker_thread.queue, &worker_thread.ping_receiver, on_ping);
+
+ pthread_create(&tid, NULL, worker_main, NULL);
+
+ /* send first message */
+ send_empty_message(&worker_thread.ping_receiver);
+
+ while (!main_thread.received_shutdown) {
+#if H2O_USE_LIBUV
+ uv_run(main_thread.loop, UV_RUN_ONCE);
+#else
+ h2o_evloop_run(main_thread.loop, INT32_MAX);
+#endif
+ }
+
+ pthread_join(tid, NULL);
+
+ h2o_multithread_unregister_receiver(worker_thread.queue, &worker_thread.ping_receiver);
+ h2o_multithread_destroy_queue(worker_thread.queue);
+ destroy_loop(worker_thread.loop);
+ h2o_multithread_unregister_receiver(main_thread.queue, &main_thread.pong_receiver);
+ h2o_multithread_unregister_receiver(main_thread.queue, &main_thread.shutdown_receiver);
+ h2o_multithread_destroy_queue(main_thread.queue);
+ destroy_loop(main_thread.loop);
+
+ ok(1);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/serverutil.c b/web/server/h2o/libh2o/t/00unit/lib/common/serverutil.c
new file mode 100644
index 00000000..dfbb2715
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/serverutil.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014 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 <stdlib.h>
+#include "../../test.h"
+#include "../../../../lib/common/serverutil.c"
+
+static void test_server_starter(void)
+{
+ int *fds;
+ size_t num_fds;
+
+ unsetenv("SERVER_STARTER_PORT");
+ num_fds = h2o_server_starter_get_fds(&fds);
+ ok(num_fds == 0);
+
+ setenv("SERVER_STARTER_PORT", "0.0.0.0:80=3", 1);
+ num_fds = h2o_server_starter_get_fds(&fds);
+ ok(num_fds == 1);
+ ok(fds[0] == 3);
+
+ setenv("SERVER_STARTER_PORT", "0.0.0.0:80=3;/tmp/foo.sock=4", 1);
+ num_fds = h2o_server_starter_get_fds(&fds);
+ ok(num_fds == 2);
+ ok(fds[0] == 3);
+ ok(fds[1] == 4);
+
+ setenv("SERVER_STARTER_PORT", "0.0.0.0:80=foo", 1);
+ num_fds = h2o_server_starter_get_fds(&fds);
+ ok(num_fds == SIZE_MAX);
+
+ /* without bind address */
+ setenv("SERVER_STARTER_PORT", "50908=4", 1);
+ num_fds = h2o_server_starter_get_fds(&fds);
+ ok(num_fds == 1);
+ ok(fds[0] == 4);
+}
+
+static void test_read_command(void)
+{
+ char *argv[] = {"t/00unit/assets/read_command.pl", "hello", NULL};
+ h2o_buffer_t *resp;
+ int ret, status;
+
+ /* success */
+ ret = h2o_read_command(argv[0], argv, &resp, &status);
+ ok(ret == 0);
+ if (ret == 0) {
+ ok(WIFEXITED(status));
+ ok(WEXITSTATUS(status) == 0);
+ ok(h2o_memis(resp->bytes, resp->size, H2O_STRLIT("hello")));
+ h2o_buffer_dispose(&resp);
+ }
+
+ /* exit status */
+ setenv("READ_COMMAND_EXIT_STATUS", "75", 1);
+ ret = h2o_read_command(argv[0], argv, &resp, &status);
+ ok(ret == 0);
+ if (ret == 0) {
+ ok(WIFEXITED(status));
+ ok(WEXITSTATUS(status) == 75);
+ ok(h2o_memis(resp->bytes, resp->size, H2O_STRLIT("hello")));
+ h2o_buffer_dispose(&resp);
+ }
+ unsetenv("READ_COMMAND_EXIT_STATUS");
+
+ /* command not an executable */
+ argv[0] = "t/00unit/assets";
+ ret = h2o_read_command(argv[0], argv, &resp, &status);
+ ok(ret != 0 || (ret == 0 && WIFEXITED(status) && WEXITSTATUS(status) == 127));
+}
+
+void test_lib__common__serverutil_c(void)
+{
+ subtest("server-starter", test_server_starter);
+ subtest("read-command", test_read_command);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/socket.c b/web/server/h2o/libh2o/t/00unit/lib/common/socket.c
new file mode 100644
index 00000000..89aae515
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/socket.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 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 <stdlib.h>
+#include "../../test.h"
+#include "../../../../lib/common/socket.c"
+
+static void test_on_alpn_select(void)
+{
+ static const h2o_iovec_t protocols[] = {{H2O_STRLIT("h2")}, {H2O_STRLIT("h2-16")}, {H2O_STRLIT("h2-14")}, {NULL}};
+ const unsigned char *out;
+ unsigned char outlen;
+ int ret;
+
+ ret = on_alpn_select(NULL, &out, &outlen, (const unsigned char *)H2O_STRLIT("\3foo"), (void *)protocols);
+ ok(ret == SSL_TLSEXT_ERR_NOACK);
+
+ ret = on_alpn_select(NULL, &out, &outlen, (const unsigned char *)H2O_STRLIT("\2h2"), (void *)protocols);
+ ok(ret == SSL_TLSEXT_ERR_OK);
+ ok(h2o_memis(out, outlen, H2O_STRLIT("h2")));
+
+ ret = on_alpn_select(NULL, &out, &outlen, (const unsigned char *)H2O_STRLIT("\5h2-14\5h2-16\2h2"), (void *)protocols);
+ ok(ret == SSL_TLSEXT_ERR_OK);
+ ok(h2o_memis(out, outlen, H2O_STRLIT("h2")));
+
+ ret = on_alpn_select(NULL, &out, &outlen, (const unsigned char *)H2O_STRLIT("\5h2-14\5h2-16"), (void *)protocols);
+ ok(ret == SSL_TLSEXT_ERR_OK);
+ ok(h2o_memis(out, outlen, H2O_STRLIT("h2-16")));
+}
+
+static void test_sliding_counter(void)
+{
+ h2o_sliding_counter_t counter = {0};
+ size_t i;
+
+ h2o_sliding_counter_start(&counter, 100);
+ h2o_sliding_counter_stop(&counter, 80 + 100);
+ ok(counter.average == 10);
+
+ for (i = 0; i != 7; ++i) {
+ h2o_sliding_counter_start(&counter, 1);
+ h2o_sliding_counter_stop(&counter, 81);
+ }
+ ok(counter.average == 80);
+
+ h2o_sliding_counter_start(&counter, 1000);
+ h2o_sliding_counter_stop(&counter, 1000 + 1000 * 8 - 80 * 7);
+ ok(counter.average == 1000);
+
+ for (i = 0; i != 8; ++i) {
+ h2o_sliding_counter_start(&counter, 1);
+ h2o_sliding_counter_stop(&counter, 11);
+ }
+ ok(counter.average == 10);
+}
+
+static struct {
+ struct {
+ int ret;
+ } fetch_tcp_info;
+ struct {
+ int ret;
+ unsigned cur;
+ size_t call_cnt;
+ } minimize_notsent_lowat;
+ struct {
+ unsigned long ret;
+ } get_cipher;
+} cb_ret_vars;
+
+static int test_adjust_notsent_lowat(h2o_socket_t *sock, unsigned notsent_lowat)
+{
+ cb_ret_vars.minimize_notsent_lowat.cur = notsent_lowat;
+ ++cb_ret_vars.minimize_notsent_lowat.call_cnt;
+ return cb_ret_vars.minimize_notsent_lowat.ret;
+}
+
+static void test_prepare_for_latency_optimization(void)
+{
+ struct st_h2o_socket_ssl_t sock_ssl = {NULL, NULL, NULL, 5 + 8 + 16 /* GCM overhead */};
+ h2o_socket_t sock = {NULL, &sock_ssl};
+ h2o_socket_latency_optimization_conditions_t cond = {UINT_MAX, 10, 65535};
+
+ /* option disabled, or if rtt is too small */
+ memset(&sock._latency_optimization, 0, sizeof(sock._latency_optimization));
+ memset(&cb_ret_vars, 0, sizeof(cb_ret_vars));
+ cb_ret_vars.fetch_tcp_info.ret = 0;
+ cb_ret_vars.get_cipher.ret = TLS1_CK_RSA_WITH_AES_128_GCM_SHA256;
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, 10 /* cwnd_size */, 6 /* cwnd_avail */, 4,
+ test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DISABLED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 16384);
+ ok(sock._latency_optimization.suggested_write_size == SIZE_MAX);
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 0);
+
+ /* trigger optimiziation */
+ memset(&sock._latency_optimization, 0, sizeof(sock._latency_optimization));
+ cond.min_rtt = 25; /* 25 ms */
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, 10 /* cwnd_size */, 6 /* cwnd_avail */, 4,
+ test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 1400 - (5 + 8 + 16));
+ ok(sock._latency_optimization.suggested_write_size == (1400 - (5 + 8 + 16)) * (10 - 5 + 1));
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 1);
+ ok(cb_ret_vars.minimize_notsent_lowat.cur == 1);
+
+ /* recalculate with an updated cwnd,unacked */
+ sock._latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_NEEDS_UPDATE;
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, 14 /* cwnd_size */, 12 /* cwnd_avail */, 4,
+ test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 1400 - (5 + 8 + 16));
+ ok(sock._latency_optimization.suggested_write_size == (1400 - (5 + 8 + 16)) * (14 - 3 + 1));
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 1);
+ ok(cb_ret_vars.minimize_notsent_lowat.cur == 1);
+
+ /* switches to B/W optimization when CWND becomes greater */
+ sock._latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_NEEDS_UPDATE;
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, (65535 / 1400) + 1 /* cwnd_size */,
+ (65535 / 1400) + 1 /* cwnd_avail */, 4, test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 16384);
+ ok(sock._latency_optimization.suggested_write_size == SIZE_MAX);
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 2);
+ ok(cb_ret_vars.minimize_notsent_lowat.cur == 0);
+
+ /* switches back to latency optimization when CWND becomes small */
+ sock._latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_NEEDS_UPDATE;
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, 8 /* cwnd_size */, 6 /* cwnd_avail */, 4,
+ test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DETERMINED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 1400 - (5 + 8 + 16));
+ ok(sock._latency_optimization.suggested_write_size == (1400 - (5 + 8 + 16)) * (8 - 3 + 1));
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 3);
+ ok(cb_ret_vars.minimize_notsent_lowat.cur == 1);
+
+ /* switches back to B/W optimization when loop time becomes greater than threshold */
+ sock._latency_optimization.state = H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_NEEDS_UPDATE;
+ prepare_for_latency_optimized_write(&sock, &cond, 50000 /* rtt */, 1400 /* mss */, 8 /* cwnd_size */, 6 /* cwnd_avail */, 6,
+ test_adjust_notsent_lowat);
+ ok(sock._latency_optimization.state == H2O_SOCKET_LATENCY_OPTIMIZATION_STATE_DISABLED);
+ ok(sock._latency_optimization.suggested_tls_payload_size == 16384);
+ ok(sock._latency_optimization.suggested_write_size == SIZE_MAX);
+ ok(cb_ret_vars.minimize_notsent_lowat.call_cnt == 4);
+ ok(cb_ret_vars.minimize_notsent_lowat.cur == 0);
+}
+
+void test_lib__common__socket_c(void)
+{
+ subtest("on_alpn_select", test_on_alpn_select);
+ subtest("sliding_counter", test_sliding_counter);
+ subtest("prepare_for_latency_optimization", test_prepare_for_latency_optimization);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/string.c b/web/server/h2o/libh2o/t/00unit/lib/common/string.c
new file mode 100644
index 00000000..464506e3
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/string.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2014 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/string.c"
+
+static void test_strstr(void)
+{
+ ok(h2o_strstr("abcd", 4, "bc", 2) == 1);
+ ok(h2o_strstr("abcd", 3, "bc", 2) == 1);
+ ok(h2o_strstr("abcd", 2, "bc", 2) == -1);
+}
+
+static void test_stripws(void)
+{
+ h2o_iovec_t t;
+
+ t = h2o_str_stripws(H2O_STRLIT(""));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("")));
+ t = h2o_str_stripws(H2O_STRLIT("hello world"));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world")));
+ t = h2o_str_stripws(H2O_STRLIT(" hello world"));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world")));
+ t = h2o_str_stripws(H2O_STRLIT("hello world "));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world")));
+ t = h2o_str_stripws(H2O_STRLIT(" hello world "));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("hello world")));
+ t = h2o_str_stripws(H2O_STRLIT(" "));
+ ok(h2o_memis(t.base, t.len, H2O_STRLIT("")));
+}
+
+static void test_get_filext(void)
+{
+ h2o_iovec_t ext;
+
+ ext = h2o_get_filext(H2O_STRLIT("/abc.txt"));
+ ok(h2o_memis(ext.base, ext.len, H2O_STRLIT("txt")));
+ ext = h2o_get_filext(H2O_STRLIT("/abc.txt.gz"));
+ ok(h2o_memis(ext.base, ext.len, H2O_STRLIT("gz")));
+ ext = h2o_get_filext(H2O_STRLIT("/abc."));
+ ok(h2o_memis(ext.base, ext.len, H2O_STRLIT("")));
+ ext = h2o_get_filext(H2O_STRLIT("/abc"));
+ ok(ext.base == NULL);
+ ext = h2o_get_filext(H2O_STRLIT("/abc.def/abc"));
+ ok(ext.base == NULL);
+ ext = h2o_get_filext(H2O_STRLIT("/abc.def/"));
+ ok(ext.base == NULL);
+}
+
+static void test_next_token(void)
+{
+ h2o_iovec_t iter;
+ const char *token;
+ size_t token_len;
+
+#define NEXT() \
+ if ((token = h2o_next_token(&iter, ',', &token_len, NULL)) == NULL) { \
+ ok(0); \
+ return; \
+ }
+
+ iter = h2o_iovec_init(H2O_STRLIT("public, max-age=86400, must-revalidate"));
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("public")));
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("max-age=86400")));
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("must-revalidate")));
+ token = h2o_next_token(&iter, ',', &token_len, NULL);
+ ok(token == NULL);
+
+ iter = h2o_iovec_init(H2O_STRLIT(" public ,max-age=86400 ,"));
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("public")));
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("max-age=86400")));
+ token = h2o_next_token(&iter, ',', &token_len, NULL);
+ ok(token == NULL);
+
+ iter = h2o_iovec_init(H2O_STRLIT(""));
+ token = h2o_next_token(&iter, ',', &token_len, NULL);
+ ok(token == NULL);
+
+ iter = h2o_iovec_init(H2O_STRLIT(", ,a, "));
+ NEXT();
+ ok(token_len == 0);
+ NEXT();
+ ok(token_len == 0);
+ NEXT();
+ ok(h2o_memis(token, token_len, H2O_STRLIT("a")));
+ token = h2o_next_token(&iter, ',', &token_len, NULL);
+ ok(token == NULL);
+
+#undef NEXT
+}
+
+static void test_next_token2(void)
+{
+ h2o_iovec_t iter, value;
+ const char *name;
+ size_t name_len;
+
+#define NEXT() \
+ if ((name = h2o_next_token(&iter, ',', &name_len, &value)) == NULL) { \
+ ok(0); \
+ return; \
+ }
+
+ iter = h2o_iovec_init(H2O_STRLIT("public, max-age=86400, must-revalidate"));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("public")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("max-age")));
+ ok(h2o_memis(value.base, value.len, H2O_STRLIT("86400")));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("must-revalidate")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ name = h2o_next_token(&iter, ',', &name_len, &value);
+ ok(name == NULL);
+
+ iter = h2o_iovec_init(H2O_STRLIT("public, max-age = 86400 = c , must-revalidate="));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("public")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("max-age")));
+ ok(h2o_memis(value.base, value.len, H2O_STRLIT("86400 = c")));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("must-revalidate")));
+ name = h2o_next_token(&iter, ',', &name_len, &value);
+ ok(h2o_memis(value.base, value.len, H2O_STRLIT("")));
+
+#undef NEXT
+}
+
+static void test_next_token3(void)
+{
+ h2o_iovec_t iter, value;
+ const char *name;
+ size_t name_len;
+
+#define NEXT() \
+ if ((name = h2o_next_token(&iter, ';', &name_len, &value)) == NULL) { \
+ ok(0); \
+ return; \
+ }
+
+ iter = h2o_iovec_init(H2O_STRLIT("</foo.css>; rel=preload; xxx=,</bar.js>, </zzz.js>"));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("</foo.css>")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("rel")));
+ ok(h2o_memis(value.base, value.len, H2O_STRLIT("preload")));
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("xxx")));
+ ok(value.base != NULL); /* xxx _has_ a value! */
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT(",")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("</bar.js>")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT(",")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ NEXT();
+ ok(h2o_memis(name, name_len, H2O_STRLIT("</zzz.js>")));
+ ok(value.base == NULL);
+ ok(value.len == 0);
+ name = h2o_next_token(&iter, ',', &name_len, &value);
+ ok(name == NULL);
+
+#undef NEXT
+}
+
+static void test_decode_base64(void)
+{
+ h2o_mem_pool_t pool;
+ char buf[256];
+
+ h2o_mem_init_pool(&pool);
+
+ h2o_iovec_t src = {H2O_STRLIT("The quick brown fox jumps over the lazy dog.")}, decoded;
+ h2o_base64_encode(buf, (const uint8_t *)src.base, src.len, 1);
+ ok(strcmp(buf, "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4") == 0);
+ decoded = h2o_decode_base64url(&pool, buf, strlen(buf));
+ ok(src.len == decoded.len);
+ ok(strcmp(decoded.base, src.base) == 0);
+
+ h2o_mem_clear_pool(&pool);
+}
+
+static void test_htmlescape(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_mem_init_pool(&pool);
+
+#define TEST(src, expected) \
+ do { \
+ h2o_iovec_t escaped = h2o_htmlescape(&pool, H2O_STRLIT(src)); \
+ ok(h2o_memis(escaped.base, escaped.len, H2O_STRLIT(expected))); \
+ } while (0)
+
+ TEST("hello world", "hello world");
+ TEST("x < y", "x &lt; y");
+ TEST("\0\"&'<>", "\0&quot;&amp;&#39;&lt;&gt;");
+
+#undef TEST
+
+ h2o_mem_clear_pool(&pool);
+}
+
+static void test_uri_escape(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_mem_init_pool(&pool);
+
+#define TEST(src, preserve, expected) \
+ do { \
+ h2o_iovec_t escaped = h2o_uri_escape(&pool, H2O_STRLIT(src), preserve); \
+ ok(h2o_memis(escaped.base, escaped.len, H2O_STRLIT(expected))); \
+ } while (0)
+
+ TEST("abc", NULL, "abc");
+ TEST("a/c", NULL, "a%2Fc");
+ TEST("a/c", "/", "a/c");
+ TEST("\xe3\x81\x82", NULL, "%E3%81%82");
+ TEST("a\0!", NULL, "a%00!");
+ TEST("a/\0!", "/", "a/%00!");
+
+#undef TEST
+
+ h2o_mem_clear_pool(&pool);
+}
+
+static void test_at_position(void)
+{
+ char buf[160];
+ int ret;
+
+ /* normal cases */
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 1);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 5);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n ^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 6);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n ^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 7);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n ^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 2, 1);
+ ok(ret == 0);
+ ok(strcmp(buf, "world\n^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 2, 5);
+ ok(ret == 0);
+ ok(strcmp(buf, "world\n ^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 7);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n ^\n") == 0);
+
+ ret = h2o_str_at_position(
+ buf, H2O_STRLIT("_________1_________2_________3_________4_________5_________6_________7_________\nworld\n"), 1, 5);
+ ok(ret == 0);
+ ok(strcmp(buf, "_________1_________2_________3_________4_________5_________6_________7______\n ^\n") == 0);
+
+ ret = h2o_str_at_position(
+ buf, H2O_STRLIT("_________1_________2_________3_________4_________5_________6_________7_________\nworld\n"), 1, 60);
+ ok(ret == 0);
+ ok(strcmp(buf, "_________3_________4_________5_________6_________7_________\n ^\n") == 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello"), 1, 20);
+ ok(ret == 0);
+ ok(strcmp(buf, "hello\n ^\n") == 0);
+
+ /* error cases */
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 0, 1);
+ ok(ret != 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 1, 0);
+ ok(ret != 0);
+
+ ret = h2o_str_at_position(buf, H2O_STRLIT("hello\nworld\n"), 4, 1);
+ ok(ret != 0);
+}
+
+void test_lib__common__string_c(void)
+{
+ subtest("strstr", test_strstr);
+ subtest("stripws", test_stripws);
+ subtest("get_filext", test_get_filext);
+ subtest("next_token", test_next_token);
+ subtest("next_token2", test_next_token2);
+ subtest("next_token3", test_next_token3);
+ subtest("decode_base64", test_decode_base64);
+ subtest("htmlescape", test_htmlescape);
+ subtest("uri_escape", test_uri_escape);
+ subtest("at_position", test_at_position);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/time.c b/web/server/h2o/libh2o/t/00unit/lib/common/time.c
new file mode 100644
index 00000000..c6fc12ff
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/time.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 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/time.c"
+
+void test_lib__common__time_c(void)
+{
+ struct tm tm = {
+ 56, /* sec */
+ 34, /* min */
+ 12, /* hour */
+ 4, /* 4th */
+ 1, /* feb */
+ 115, /* 2015 */
+ 3 /* Wed */
+ };
+ char buf[H2O_TIMESTR_RFC1123_LEN + 1];
+
+ h2o_time2str_rfc1123(buf, &tm);
+ ok(strcmp(buf, "Wed, 04 Feb 2015 12:34:56 GMT") == 0);
+ tm = (struct tm){0};
+ h2o_time_parse_rfc1123(buf, H2O_TIMESTR_RFC1123_LEN, &tm);
+ ok(tm.tm_year == 115);
+ ok(tm.tm_mon == 1);
+ ok(tm.tm_mday == 4);
+ ok(tm.tm_hour == 12);
+ ok(tm.tm_min == 34);
+ ok(tm.tm_sec == 56);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/common/url.c b/web/server/h2o/libh2o/t/00unit/lib/common/url.c
new file mode 100644
index 00000000..ce167600
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/common/url.c
@@ -0,0 +1,662 @@
+/*
+ * 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);
+
+ 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: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(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_url_t parsed;
+ int ret;
+
+ ret = h2o_url_parse("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("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("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("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("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("http:/abc", SIZE_MAX, &parsed);
+ ok(ret != 0);
+
+ ret = h2o_url_parse("ftp://example.com/abc", SIZE_MAX, &parsed);
+ ok(ret != 0);
+
+ ret = h2o_url_parse("http://abc:111111/def", SIZE_MAX, &parsed);
+ ok(ret != 0);
+
+ ret = h2o_url_parse("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("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("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")));
+}
+
+static void test_parse_relative(void)
+{
+ h2o_url_t parsed;
+ int ret;
+
+ memset(&parsed, 0x55, sizeof(parsed));
+ ret = h2o_url_parse_relative("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("/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("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("//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("//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("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")));
+}
+
+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("http://example.com/dir/index.html", SIZE_MAX, &base);
+ ok(ret == 0);
+
+ ret = h2o_url_parse_relative("../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("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("./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("../../../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("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("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("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("//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);
+}