summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/t/00unit/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/core/headers.c53
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/core/proxy.c49
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/core/util.c435
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/compress.c125
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/fastcgi.c183
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/file.c752
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/headers.c127
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/mimemap.c127
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/handler/redirect.c82
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/http2/cache_digests.c78
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/http2/casper.c133
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/http2/hpack.c511
-rw-r--r--web/server/h2o/libh2o/t/00unit/lib/http2/scheduler.c563
21 files changed, 4823 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);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/core/headers.c b/web/server/h2o/libh2o/t/00unit/lib/core/headers.c
new file mode 100644
index 00000000..97cea41e
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/core/headers.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 "../../test.h"
+#include "../../../../lib/core/headers.c"
+
+static void test_set_header_token(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_headers_t headers = {NULL};
+
+ h2o_mem_init_pool(&pool);
+
+ h2o_set_header_token(&pool, &headers, H2O_TOKEN_VARY, H2O_STRLIT("cookie"));
+ ok(headers.size == 1);
+ ok(headers.entries[0].name == &H2O_TOKEN_VARY->buf);
+ ok(h2o_memis(headers.entries[0].value.base, headers.entries[0].value.len, H2O_STRLIT("cookie")));
+ h2o_set_header_token(&pool, &headers, H2O_TOKEN_VARY, H2O_STRLIT("accept-encoding"));
+ ok(headers.size == 1);
+ ok(headers.entries[0].name == &H2O_TOKEN_VARY->buf);
+ ok(h2o_memis(headers.entries[0].value.base, headers.entries[0].value.len, H2O_STRLIT("cookie, accept-encoding")));
+
+ headers.entries[0].value.base[0] = 'C';
+ h2o_set_header_token(&pool, &headers, H2O_TOKEN_VARY, H2O_STRLIT("cookie"));
+ ok(headers.size == 1);
+ ok(headers.entries[0].name == &H2O_TOKEN_VARY->buf);
+ ok(h2o_memis(headers.entries[0].value.base, headers.entries[0].value.len, H2O_STRLIT("Cookie, accept-encoding")));
+
+ h2o_mem_clear_pool(&pool);
+}
+
+void test_lib__core__headers_c(void)
+{
+ subtest("set_header_token", test_set_header_token);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/core/proxy.c b/web/server/h2o/libh2o/t/00unit/lib/core/proxy.c
new file mode 100644
index 00000000..6f87db1f
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/core/proxy.c
@@ -0,0 +1,49 @@
+/*
+ * 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/core/proxy.c"
+
+static void test_rewrite_location(void)
+{
+ h2o_url_t upstream;
+ h2o_mem_pool_t pool;
+ h2o_iovec_t ret;
+
+ h2o_url_parse(H2O_STRLIT("http://realhost:81/real/"), &upstream);
+
+ h2o_mem_init_pool(&pool);
+
+ ret = rewrite_location(&pool, H2O_STRLIT("http://realhost:81/real/abc"), &upstream, &H2O_URL_SCHEME_HTTPS,
+ h2o_iovec_init(H2O_STRLIT("vhost:8443")), h2o_iovec_init(H2O_STRLIT("/virtual/")));
+ ok(h2o_memis(ret.base, ret.len, H2O_STRLIT("https://vhost:8443/virtual/abc")));
+ ret = rewrite_location(&pool, H2O_STRLIT("http://realhost:81/other/abc"), &upstream, &H2O_URL_SCHEME_HTTPS,
+ h2o_iovec_init(H2O_STRLIT("vhost:8443")), h2o_iovec_init(H2O_STRLIT("/virtual/")));
+ ok(ret.base == NULL);
+ ok(ret.len == 0);
+
+ h2o_mem_clear_pool(&pool);
+}
+
+void test_lib__core__proxy_c()
+{
+ subtest("rewrite_location", test_rewrite_location);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/core/util.c b/web/server/h2o/libh2o/t/00unit/lib/core/util.c
new file mode 100644
index 00000000..a3141577
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/core/util.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2015 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 <string.h>
+#include "../../test.h"
+#include "../../../../lib/core/util.c"
+
+static void test_parse_proxy_line(void)
+{
+ char in[256];
+ struct sockaddr_storage sa;
+ socklen_t salen;
+ ssize_t ret;
+
+ strcpy(in, "");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == -2);
+
+ strcpy(in, "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\nabc");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == strlen(in) - 3);
+ ok(salen == sizeof(struct sockaddr_in));
+ ok(sa.ss_family == AF_INET);
+ ok(((struct sockaddr_in *)&sa)->sin_addr.s_addr == htonl(0xc0a80001));
+ ok(((struct sockaddr_in *)&sa)->sin_port == htons(56324));
+
+ strcpy(in, "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == -2);
+
+ strcpy(in, "PROXY TCP5");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == -1);
+
+ strcpy(in, "PROXY UNKNOWN");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == -2);
+
+ strcpy(in, "PROXY UNKNOWN\r\nabc");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == strlen(in) - 3);
+ ok(salen == 0);
+
+ strcpy(in, "PROXY TCP6 ::1 ::1 56324 443\r\n");
+ ret = parse_proxy_line(in, strlen(in), (void *)&sa, &salen);
+ ok(ret == strlen(in));
+ ok(salen == sizeof(struct sockaddr_in6));
+ ok(sa.ss_family == AF_INET6);
+ ok(memcmp(&((struct sockaddr_in6 *)&sa)->sin6_addr, H2O_STRLIT("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1")) == 0);
+ ok(((struct sockaddr_in6 *)&sa)->sin6_port == htons(56324));
+}
+
+static void test_extract_push_path_from_link_header(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_iovec_vector_t paths;
+ h2o_iovec_t path;
+ h2o_iovec_t filtered_value;
+ h2o_iovec_t base_path = {H2O_STRLIT("/basepath/")}, input_authority = {H2O_STRLIT("basehost")},
+ other_authority = {H2O_STRLIT("otherhost")};
+#define INPUT base_path, &H2O_URL_SCHEME_HTTP, input_authority
+ h2o_mem_init_pool(&pool);
+ h2o_iovec_t value;
+
+ value = h2o_iovec_init(H2O_STRLIT("<http://basehost/otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/basepath/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<../otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<http:otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/basepath/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<../otherpath>; rel=author"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<http://basehost:81/otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<https://basehost/otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<https:otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTPS, &input_authority,
+ &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &other_authority,
+ &filtered_value);
+ ok(paths.entries == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<http://basehost/otherpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &other_authority,
+ &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/otherpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("<http:otherpath>; rel=preload; nopush"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, NULL, NULL, &filtered_value);
+ ok(paths.entries == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </secondpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 2);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/secondpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; nopush, </secondpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/secondpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; nopush, </secondpath>; nopush; rel=preload; </thirdpath>"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; nopush, <secondpath>; rel=notpreload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </secondpath>; rel=preload; nopush, </thirdpath>; rel=preload"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 2);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/thirdpath")));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("hogefoo"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ h2o_mem_clear_pool(&pool);
+#undef INPUT
+}
+
+static void test_extract_push_path_from_link_header_push_only(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_iovec_vector_t paths;
+ h2o_iovec_t path;
+ h2o_iovec_t filtered_value;
+ h2o_iovec_t base_path = {H2O_STRLIT("/basepath/")}, input_authority = {H2O_STRLIT("basehost")};
+#define INPUT base_path, &H2O_URL_SCHEME_HTTP, input_authority
+ h2o_mem_init_pool(&pool);
+ h2o_iovec_t value;
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </secondpath>; rel=preload; nopush, </thirdpath>; rel=preload, "
+ "</fourthpath>; rel=preload; x-http2-push-only"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 3);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/thirdpath")));
+ path = paths.entries[2];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/fourthpath")));
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </secondpath>; rel=preload; nopush, </thirdpath>; rel=preload"));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; x-http2-push-only, </secondpath>; rel=preload; nopush, "
+ "</thirdpath>; rel=preload, </fourthpath>; rel=preload; x-http2-push-only"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 3);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/thirdpath")));
+ path = paths.entries[2];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/fourthpath")));
+ value = h2o_iovec_init(H2O_STRLIT("</secondpath>; rel=preload; nopush, </thirdpath>; rel=preload"));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </secondpath>; rel=preload; x-http2-push-only, </thirdpath>; "
+ "rel=preload; nopush, </fourthpath>; rel=preload; x-http2-push-only"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 3);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/secondpath")));
+ path = paths.entries[2];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/fourthpath")));
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload, </thirdpath>; rel=preload; nopush"));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; x-http2-push-only, </secondpath>; rel=preload, </thirdpath>; "
+ "rel=preload; x-http2-push-only, </fourthpath>; rel=preload; nopush"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 3);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/secondpath")));
+ path = paths.entries[2];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/thirdpath")));
+ value = h2o_iovec_init(H2O_STRLIT("</secondpath>; rel=preload, </fourthpath>; rel=preload; nopush"));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(
+ H2O_STRLIT("</firstpath>; rel=preload; x-http2-push-only, </secondpath>; rel=preload; x-http2-push-only, </thirdpath>; "
+ "rel=preload, </fourthpath>; rel=preload; nopush"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 3);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ path = paths.entries[1];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/secondpath")));
+ path = paths.entries[2];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/thirdpath")));
+ value = h2o_iovec_init(H2O_STRLIT("</thirdpath>; rel=preload, </fourthpath>; rel=preload; nopush"));
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("firstpath; rel=preload, </secondpath>; rel=preload; x-http2-push-only, </thirdpath>; "
+ "rel=preload; nopush, </fourthpath>; rel=preload; x-http2-push-only"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 0);
+ ok(h2o_memis(value.base, value.len, filtered_value.base, filtered_value.len));
+
+ value = h2o_iovec_init(H2O_STRLIT("</firstpath>; rel=preload; x-http2-push-only, bar"));
+ paths = h2o_extract_push_path_from_link_header(&pool, value.base, value.len, INPUT, &H2O_URL_SCHEME_HTTP, &input_authority,
+ &filtered_value);
+ ok(paths.size == 1);
+ path = paths.entries[0];
+ ok(h2o_memis(path.base, path.len, H2O_STRLIT("/firstpath")));
+ ok(h2o_memis(H2O_STRLIT("bar"), filtered_value.base, filtered_value.len));
+
+ h2o_mem_clear_pool(&pool);
+}
+void test_build_destination(void)
+{
+ h2o_pathconf_t conf_not_slashed = {NULL, {H2O_STRLIT("/abc")}}, conf_slashed = {NULL, {H2O_STRLIT("/abc/")}};
+ h2o_req_t req;
+ h2o_iovec_t dest;
+ int escape;
+
+ for (escape = 0; escape <= 1; escape++) {
+ h2o_init_request(&req, NULL, NULL);
+
+ note("escaping: %s", escape ? "on" : "off");
+ req.path_normalized = h2o_iovec_init(H2O_STRLIT("/abc/xyz"));
+ req.query_at = req.path_normalized.len;
+ req.input.path = req.path = h2o_concat(&req.pool, req.path_normalized, h2o_iovec_init(H2O_STRLIT("?q")));
+
+ /* basic pattern */
+ req.pathconf = &conf_not_slashed;
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q")));
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q")));
+ req.pathconf = &conf_slashed;
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q")));
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz?q")));
+
+ /* test wo. query */
+ if (escape) {
+ req.pathconf = &conf_not_slashed;
+ req.query_at = SIZE_MAX;
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/xyz")));
+ }
+
+ /* no trailing */
+ req.path_normalized = h2o_iovec_init(H2O_STRLIT("/abc"));
+ req.query_at = req.path_normalized.len;
+ req.input.path = req.path = h2o_concat(&req.pool, req.path_normalized, h2o_iovec_init(H2O_STRLIT("?q")));
+
+ req.pathconf = &conf_not_slashed;
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def?q")));
+ dest = h2o_build_destination(&req, H2O_STRLIT("/def/"), escape);
+ ok(h2o_memis(dest.base, dest.len, H2O_STRLIT("/def/?q")));
+ }
+
+ h2o_mem_clear_pool(&req.pool);
+}
+
+void test_build_destination_escaping(void)
+{
+ h2o_req_t req;
+ h2o_iovec_t dest;
+ int escape = 0;
+ int i, j;
+ struct {
+ char *pathconf;
+ char *dest;
+ char *input;
+ char *output;
+ } tests[] = {
+ {"/abc", "/def", "/abc/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/%61bc/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/%61%62c/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/./%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/../%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/././%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/./.././%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/./../blah/../%61%62%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/./../blah/.././%61%62c/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/./../blah/.././../../%61b%63/xyz?query&m=n/o", "/def/xyz?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/?query&m=n/o", "/def/xyz/?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/.?query&m=n/o", "/def/xyz/.?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/./?query&m=n/o", "/def/xyz/./?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/..?query&m=n/o", "/def/xyz/..?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/../?query&m=n/o", "/def/xyz/../?query&m=n/o"},
+ {"/abc", "/def", "/abc/xyz/../a?query&m=n/o", "/def/xyz/../a?query&m=n/o"},
+ {"/abc", "/def", "/abc/%yz/?query&m=n/o", "/def/%yz/?query&m=n/o"},
+ {"/abc", "/def", "/abc/%78yz/?query&m=n/o", "/def/%78yz/?query&m=n/o"},
+ {"/", "/", "/xyz/../mno", "/xyz/../mno"},
+ {"/", "/", "/xyz/../mno/..", "/xyz/../mno/.."},
+ {"/", "/def", "/xyz/../mno", "/def/xyz/../mno"},
+ {"/", "/def/", "/xyz/../mno", "/def/xyz/../mno"},
+ {"/", "/def", "/xyz/../", "/def/xyz/../"},
+ {"/", "/def/", "/xyz/..", "/def/xyz/.."},
+ };
+ h2o_init_request(&req, NULL, NULL);
+
+ /* 'j' runs the test with a missing leading '/' in the input path */
+ for (j = 0; j <= 1; j++) {
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ h2o_pathconf_t conf = {NULL, {tests[i].pathconf, strlen(tests[i].pathconf)}};
+ req.pathconf = &conf;
+ req.path = req.input.path = h2o_iovec_init(tests[i].input + j, strlen(tests[i].input) - j);
+ req.norm_indexes = NULL;
+ req.path_normalized = h2o_url_normalize_path(&req.pool, req.path.base, req.path.len, &req.query_at, &req.norm_indexes);
+ dest = h2o_build_destination(&req, tests[i].dest, strlen(tests[i].dest), escape);
+ note("%s: %d, %sskipping the leading '/'", tests[i].input, i, !j ? "not " : "");
+ ok(dest.len == strlen(tests[i].output));
+ ok(h2o_memis(dest.base, dest.len, tests[i].output, strlen(tests[i].output)));
+ }
+ }
+
+ h2o_mem_clear_pool(&req.pool);
+}
+
+void test_lib__core__util_c()
+{
+ subtest("parse_proxy_line", test_parse_proxy_line);
+ subtest("extract_push_path_from_link_header", test_extract_push_path_from_link_header);
+ subtest("extract_push_path_from_link_header_push_only", test_extract_push_path_from_link_header_push_only);
+ subtest("test_build_destination", test_build_destination);
+ subtest("test_build_destination_escaping", test_build_destination_escaping);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/compress.c b/web/server/h2o/libh2o/t/00unit/lib/handler/compress.c
new file mode 100644
index 00000000..5b430b4c
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/compress.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015 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 <stdlib.h>
+#include <zlib.h>
+#include "../../test.h"
+#define BUF_SIZE 256
+#include "../../../../lib/handler/compress/gzip.c"
+#include "../../../../lib/handler/compress.c"
+
+static void check_result(h2o_iovec_t *vecs, size_t num_vecs, const char *expected, size_t expectedlen)
+{
+ z_stream zs;
+ char decbuf[expectedlen + 1];
+
+ memset(&zs, 0, sizeof(zs));
+ zs.zalloc = alloc_cb;
+ zs.zfree = free_cb;
+ zs.next_out = (void *)decbuf;
+ zs.avail_out = (unsigned)sizeof(decbuf);
+
+ inflateInit2(&zs, WINDOW_BITS);
+
+ int inflate_ret = -1;
+ size_t i;
+ for (i = 0; i != num_vecs; ++i) {
+ zs.next_in = (void *)vecs[i].base;
+ zs.avail_in = (unsigned)vecs[i].len;
+ inflate_ret = inflate(&zs, Z_NO_FLUSH);
+ if (zs.avail_out == 0) {
+ ok(0);
+ return;
+ }
+ if (zs.avail_in != 0) {
+ ok(0);
+ return;
+ }
+ }
+
+ ok(inflate_ret == Z_STREAM_END);
+ inflateEnd(&zs);
+
+ ok(zs.avail_out == sizeof(decbuf) - expectedlen);
+ ok(memcmp(decbuf, expected, expectedlen) == 0);
+}
+
+void test_gzip_simple(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_iovec_t inbuf, *outbufs;
+ size_t outbufcnt;
+
+ h2o_mem_init_pool(&pool);
+
+ h2o_compress_context_t *compressor = h2o_compress_gzip_open(&pool, Z_BEST_SPEED);
+ inbuf = h2o_iovec_init(H2O_STRLIT("hello world"));
+ compressor->transform(compressor, &inbuf, 1, 1, &outbufs, &outbufcnt);
+
+ check_result(outbufs, outbufcnt, H2O_STRLIT("hello world"));
+
+ h2o_mem_clear_pool(&pool);
+}
+
+void test_gzip_multi(void)
+{
+#define P1 \
+ "Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she " \
+ "had peeped into the book her sister was reading, but it had no pictures or conversations in it, `and what is the use of a " \
+ "book,' thought Alice `without pictures or conversation?'\n\n"
+#define P2 \
+ "So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and stupid), " \
+ "whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the daisies, when " \
+ "suddenly a White Rabbit with pink eyes ran close by her.\n\n"
+#define P3 \
+ "There was nothing so very remarkable in that; nor did Alice think it so very much out of the way to hear the Rabbit say to " \
+ "itself, `Oh dear! Oh dear! I shall be late!' (when she thought it over afterwards, it occurred to her that she ought to " \
+ "have wondered at this, but at the time it all seemed quite natural); but when the Rabbit actually took a watch out of its " \
+ "waistcoat-pocket, and looked at it, and then hurried on, Alice started to her feet, for it flashed across her mind that she " \
+ "had never before seen a rabbit with either a waistcoat-pocket, or a watch to take out of it, and burning with curiosity, " \
+ "she ran across the field after it, and fortunately was just in time to see it pop down a large rabbit-hole under the " \
+ "hedge.\n\n"
+
+ h2o_mem_pool_t pool;
+ h2o_iovec_t inbufs[] = {{H2O_STRLIT(P1)}, {H2O_STRLIT(P2)}, {H2O_STRLIT(P3)}}, *outbufs;
+ size_t outbufcnt;
+
+ h2o_mem_init_pool(&pool);
+
+ h2o_compress_context_t *compressor = h2o_compress_gzip_open(&pool, Z_BEST_SPEED);
+ compressor->transform(compressor, inbufs, sizeof(inbufs) / sizeof(inbufs[0]), 1, &outbufs, &outbufcnt);
+
+ assert(outbufcnt > 1); /* we want to test multi-vec output */
+
+ check_result(outbufs, outbufcnt, H2O_STRLIT(P1 P2 P3));
+
+ h2o_mem_clear_pool(&pool);
+
+#undef P1
+#undef P2
+#undef P3
+}
+
+void test_lib__handler__gzip_c()
+{
+ subtest("gzip_simple", test_gzip_simple);
+ subtest("gzip_multi", test_gzip_multi);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/fastcgi.c b/web/server/h2o/libh2o/t/00unit/lib/handler/fastcgi.c
new file mode 100644
index 00000000..f79fbf70
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/fastcgi.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2015 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 <stdlib.h>
+#include "../../test.h"
+#include "../../../../lib/handler/fastcgi.c"
+
+static h2o_context_t ctx;
+
+static int check_params(h2o_iovec_t *vecs, size_t *index, uint16_t request_id, const char *expected, size_t expected_len)
+{
+#define DECODE_UINT16(p) (((unsigned char *)&p)[0] << 8 | ((unsigned char *)&p)[1])
+
+ char buf[4096];
+ size_t offset = 0;
+
+ while (1) {
+ if (vecs[*index].len != FCGI_RECORD_HEADER_SIZE) {
+ fprintf(stderr, "record too short (index: %zu)\n", *index);
+ return 0;
+ }
+ struct st_fcgi_record_header_t *header = (void *)vecs[*index].base;
+ if (header->version != FCGI_VERSION_1 || header->type != FCGI_PARAMS || header->paddingLength != 0 ||
+ header->reserved != 0) {
+ fprintf(stderr, "header is corrupt (index: %zu)\n", *index);
+ return 0;
+ }
+ if (DECODE_UINT16(header->requestId) != request_id) {
+ fprintf(stderr, "unexpected request id (index: %zu)\n", *index);
+ return 0;
+ }
+ ++*index;
+ if (DECODE_UINT16(header->contentLength) == 0)
+ break;
+ if (vecs[*index].len != DECODE_UINT16(header->contentLength)) {
+ fprintf(stderr, "unexpected body size (index: %zu)\n", *index);
+ return 0;
+ }
+ memcpy(buf + offset, vecs[*index].base, vecs[*index].len);
+ offset += vecs[*index].len;
+ ++*index;
+ }
+
+ if (!h2o_memis(buf, offset, expected, expected_len)) {
+ fprintf(stderr, "PARAMS content mistach\n");
+ return 0;
+ }
+
+ return 1;
+
+#undef DECODE_UINT16
+}
+
+static void test_build_request(void)
+{
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ h2o_fastcgi_config_vars_t config = {5000, 0};
+ iovec_vector_t vecs;
+ size_t vec_index;
+
+ conn->req.input.method = conn->req.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.scheme = conn->req.scheme = &H2O_URL_SCHEME_HTTP;
+ conn->req.input.authority = conn->req.authority = h2o_iovec_init(H2O_STRLIT("localhost"));
+ conn->req.input.path = conn->req.path = h2o_iovec_init(H2O_STRLIT("/"));
+ conn->req.path_normalized = conn->req.path;
+ conn->req.query_at = SIZE_MAX;
+ conn->req.version = 0x101;
+ conn->req.hostconf = *ctx.globalconf->hosts;
+ conn->req.pathconf = conn->req.hostconf->paths.entries;
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, NULL, H2O_STRLIT("foo=bar"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_USER_AGENT, NULL,
+ H2O_STRLIT("Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9"));
+
+ /* build with max_record_size=65535 */
+ build_request(&conn->req, &vecs, 0x1234, 65535, &config);
+ ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00"
+ "\x00\x01\0\0\0\0\0\0")));
+ vec_index = 1;
+ ok(check_params(vecs.entries, &vec_index, 0x1234,
+ H2O_STRLIT("\x0b\x00SCRIPT_NAME" /* */
+ "\x09\x01PATH_INFO/" /* */
+ "\x0c\x00QUERY_STRING" /* */
+ "\x0b\x09REMOTE_ADDR127.0.0.1" /* */
+ "\x0b\x05REMOTE_PORT55555" /* */
+ "\x0e\x03REQUEST_METHODGET" /* */
+ "\x09\x09HTTP_HOSTlocalhost" /* */
+ "\x0b\x01REQUEST_URI/" /* */
+ "\x0b\x09SERVER_ADDR127.0.0.1" /* */
+ "\x0b\x02SERVER_PORT80" /* */
+ "\x0b\x07SERVER_NAMEdefault" /* */
+ "\x0f\x08SERVER_PROTOCOLHTTP/1.1" /* */
+ "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1" /* */
+ "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */
+ "\x0b\x07HTTP_COOKIEfoo=bar" /* */
+ )));
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00")));
+ ++vec_index;
+ ok(vec_index == vecs.size);
+
+ /* build with max_record_size=64, DOCUMENT_ROOT, additional cookie, and content */
+ config.document_root = h2o_iovec_init(H2O_STRLIT("/var/www/htdocs"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_COOKIE, NULL, H2O_STRLIT("hoge=fuga"));
+ conn->req.entity = h2o_iovec_init(H2O_STRLIT("The above copyright notice and this permission notice shall be included in all "
+ "copies or substantial portions of the Software."));
+ build_request(&conn->req, &vecs, 0x1234, 64, &config);
+ ok(h2o_memis(vecs.entries[0].base, vecs.entries[0].len, H2O_STRLIT("\x01\x01\x12\x34\x00\x08\x00\x00"
+ "\x00\x01\0\0\0\0\0\0")));
+ vec_index = 1;
+ ok(check_params(vecs.entries, &vec_index, 0x1234,
+ H2O_STRLIT("\x0e\x03"
+ "CONTENT_LENGTH126" /* */
+ "\x0b\x00SCRIPT_NAME" /* */
+ "\x09\x01PATH_INFO/" /* */
+ "\x0d\x0f"
+ "DOCUMENT_ROOT/var/www/htdocs" /* */
+ "\x0f\x10PATH_TRANSLATED/var/www/htdocs/" /* */
+ "\x0c\x00QUERY_STRING" /* */
+ "\x0b\x09REMOTE_ADDR127.0.0.1" /* */
+ "\x0b\x05REMOTE_PORT55555" /* */
+ "\x0e\x03REQUEST_METHODGET" /* */
+ "\x09\x09HTTP_HOSTlocalhost" /* */
+ "\x0b\x01REQUEST_URI/" /* */
+ "\x0b\x09SERVER_ADDR127.0.0.1" /* */
+ "\x0b\x02SERVER_PORT80" /* */
+ "\x0b\x07SERVER_NAMEdefault" /* */
+ "\x0f\x08SERVER_PROTOCOLHTTP/1.1" /* */
+ "\x0f\x10SERVER_SOFTWAREh2o/1.2.1-alpha1" /* */
+ "\x0f\x3fHTTP_USER_AGENTMozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9" /* */
+ "\x0b\x11HTTP_COOKIEfoo=bar;hoge=fuga" /* */
+ )));
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x40\x00\x00")));
+ ++vec_index;
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len,
+ H2O_STRLIT("The above copyright notice and this permission notice shall be i")));
+ ++vec_index;
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x3e\x00\x00")));
+ ++vec_index;
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len,
+ H2O_STRLIT("ncluded in all copies or substantial portions of the Software.")));
+ ++vec_index;
+ ok(h2o_memis(vecs.entries[vec_index].base, vecs.entries[vec_index].len, H2O_STRLIT("\x01\x05\x12\x34\x00\x00\x00\x00")));
+ ++vec_index;
+ ok(vec_index == vecs.size);
+
+ h2o_loopback_destroy(conn);
+}
+
+void test_lib__handler__fastcgi_c()
+{
+ h2o_globalconf_t globalconf;
+ h2o_hostconf_t *hostconf;
+ h2o_pathconf_t *pathconf;
+
+ h2o_config_init(&globalconf);
+ globalconf.server_name = h2o_iovec_init(H2O_STRLIT("h2o/1.2.1-alpha1"));
+ hostconf = h2o_config_register_host(&globalconf, h2o_iovec_init(H2O_STRLIT("default")), 65535);
+ pathconf = h2o_config_register_path(hostconf, "/", 0);
+
+ h2o_context_init(&ctx, test_loop, &globalconf);
+
+ subtest("build-request", test_build_request);
+
+ h2o_context_dispose(&ctx);
+ h2o_config_dispose(&globalconf);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/file.c b/web/server/h2o/libh2o/t/00unit/lib/handler/file.c
new file mode 100644
index 00000000..6c0e5948
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/file.c
@@ -0,0 +1,752 @@
+/*
+ * 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/handler/file.c"
+
+static h2o_context_t ctx;
+
+static int check_header(h2o_res_t *res, const h2o_token_t *header_name, const char *expected)
+{
+ size_t index = h2o_find_header(&res->headers, header_name, SIZE_MAX);
+ if (index == SIZE_MAX)
+ return 0;
+ return h2o_lcstris(res->headers.entries[index].value.base, res->headers.entries[index].value.len, expected, strlen(expected));
+}
+
+static int check_multirange_body(char *resbody, const char *boundary, const h2o_iovec_t *expected, size_t partlen)
+{
+ char *bptr = resbody;
+ const h2o_iovec_t *eptr = expected;
+ int not_first_line = 0;
+ while (partlen--) {
+ if (not_first_line) {
+ if (!h2o_memis(bptr, 2, H2O_STRLIT("\r\n")))
+ return 0;
+ bptr += 2;
+ } else
+ not_first_line = 1;
+ if (!h2o_memis(bptr, 2, H2O_STRLIT("--")))
+ return 0;
+ bptr += 2;
+ if (!h2o_memis(bptr, BOUNDARY_SIZE, boundary, BOUNDARY_SIZE))
+ return 0;
+ bptr += 20;
+ if (!h2o_memis(bptr, 2, H2O_STRLIT("\r\n")))
+ return 0;
+ bptr += 2;
+ if (!h2o_memis(bptr, eptr->len, eptr->base, eptr->len))
+ return 0;
+ bptr += eptr->len;
+ eptr++;
+ }
+ if (!h2o_memis(bptr, 4, H2O_STRLIT("\r\n--")))
+ return 0;
+ bptr += 4;
+ if (!h2o_memis(bptr, BOUNDARY_SIZE, boundary, BOUNDARY_SIZE))
+ return 0;
+ bptr += 20;
+ if (!h2o_memis(bptr, 4, H2O_STRLIT("--\r\n")))
+ return 0;
+ return 1;
+}
+
+static void test_process_range(void)
+{
+ h2o_mem_pool_t testpool;
+ size_t ret, *ranges;
+ h2o_iovec_t testrange;
+ h2o_mem_init_pool(&testpool);
+
+ { /* check single range within filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=, 0-10"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 0);
+ ok(*ranges == 11);
+ }
+
+ { /* check single range with only start */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=60-"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 60);
+ ok(*ranges == 40);
+ }
+
+ { /* check single suffix range */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=-10"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 90);
+ ok(*ranges == 10);
+ }
+
+ { /* this and next two check multiple ranges within filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=0-10, -10"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 2);
+ ok(*ranges++ == 0);
+ ok(*ranges++ == 11);
+ ok(*ranges++ == 90);
+ ok(*ranges == 10);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=0-0, 20-89"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 2);
+ ok(*ranges++ == 0);
+ ok(*ranges++ == 1);
+ ok(*ranges++ == 20);
+ ok(*ranges == 70);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=-10,-20"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 2);
+ ok(*ranges++ == 90);
+ ok(*ranges++ == 10);
+ ok(*ranges++ == 80);
+ ok(*ranges++ == 20);
+ }
+
+ { /* check ranges entirely out of filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=100-102"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ { /* check ranges with "negative" length */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=70-21"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ { /* check ranges with one side inside filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=90-102"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 90);
+ ok(*ranges == 10);
+ }
+
+ { /* check suffix range larger than filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=-200"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 0);
+ ok(*ranges == 100);
+ }
+
+ { /* check multiple ranges with unsatisfiable ranges, but also contain satisfiable ranges */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=100-102, 90-102, 72-30,-22, 95-"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 3);
+ ok(*ranges++ == 90);
+ ok(*ranges++ == 10);
+ ok(*ranges++ == 78);
+ ok(*ranges++ == 22);
+ ok(*ranges++ == 95);
+ ok(*ranges++ == 5);
+ }
+
+ { /* this and next 6 check malformed ranges */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes 20-1002"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes="));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bsdfeadsfjwleakjf"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=100-102, 90-102, -72-30,-22,95-"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=10-12-13, 90-102, -72, -22, 95-"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=100-102, 90-102, 70-39, -22$"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=-0"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ { /* check same ranges with different filesize */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=20-200"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 20);
+ ok(*ranges == 80);
+ }
+
+ {
+ ranges = process_range(&testpool, &testrange, 1000, &ret);
+ ok(ret == 1);
+ ok(*ranges++ == 20);
+ ok(*ranges == 181);
+ }
+
+ { /* check a range with plenty of WS and COMMA */
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=,\t,1-3 ,, ,5-9,"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 2);
+ ok(*ranges++ == 1);
+ ok(*ranges++ == 3);
+ ok(*ranges++ == 5);
+ ok(*ranges == 5);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes= 1-3"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=1-3 5-10"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ranges == NULL);
+ }
+
+ {
+ testrange = h2o_iovec_init(H2O_STRLIT("bytes=1-\t,5-10"));
+ ranges = process_range(&testpool, &testrange, 100, &ret);
+ ok(ret == 2);
+ ok(*ranges++ == 1);
+ ok(*ranges++ == 99);
+ ok(*ranges++ == 5);
+ ok(*ranges == 6);
+ }
+
+ h2o_mem_clear_pool(&testpool);
+}
+
+static void test_if_modified_since(void)
+{
+ char lm_date[H2O_TIMESTR_RFC1123_LEN + 1];
+
+ { /* obtain last-modified */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ ssize_t lm_index;
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ if ((lm_index = h2o_find_header(&conn->req.res.headers, H2O_TOKEN_LAST_MODIFIED, -1)) == -1) {
+ ok(0);
+ return;
+ }
+ ok(conn->req.res.headers.entries[lm_index].value.len == H2O_TIMESTR_RFC1123_LEN);
+ memcpy(lm_date, conn->req.res.headers.entries[lm_index].value.base, H2O_TIMESTR_RFC1123_LEN);
+ lm_date[H2O_TIMESTR_RFC1123_LEN] = '\0';
+ h2o_loopback_destroy(conn);
+ }
+
+ { /* send if-modified-since using the obtained last-modified */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_IF_MODIFIED_SINCE, NULL, lm_date, H2O_TIMESTR_RFC1123_LEN);
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 304);
+ ok(conn->body->size == 0);
+ ok(h2o_find_header(&conn->req.res.headers, H2O_TOKEN_ETAG, -1) != -1);
+ h2o_loopback_destroy(conn);
+ }
+
+ { /* send if-modified-since using an old date */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_IF_MODIFIED_SINCE, NULL,
+ H2O_STRLIT("Sun, 06 Nov 1994 08:49:37 GMT"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ h2o_loopback_destroy(conn);
+ }
+
+ { /* send if-modified-since using a date in the future */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_IF_MODIFIED_SINCE, NULL,
+ H2O_STRLIT("Wed, 18 May 2033 12:33:20 GMT"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 304);
+ ok(conn->body->size == 0);
+ ok(h2o_find_header(&conn->req.res.headers, H2O_TOKEN_ETAG, -1) != -1);
+ h2o_loopback_destroy(conn);
+ }
+}
+
+static void test_if_match(void)
+{
+ h2o_iovec_t etag = {NULL};
+
+ { /* obtain etag */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ ssize_t etag_index;
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ if ((etag_index = h2o_find_header(&conn->req.res.headers, H2O_TOKEN_ETAG, -1)) == -1) {
+ ok(0);
+ return;
+ }
+ etag = h2o_strdup(NULL, conn->req.res.headers.entries[etag_index].value.base,
+ conn->req.res.headers.entries[etag_index].value.len);
+ h2o_loopback_destroy(conn);
+ }
+
+ { /* send if-non-match using the obtained etag */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_IF_NONE_MATCH, NULL, etag.base, etag.len);
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 304);
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+
+ free(etag.base);
+}
+
+static void test_range_req(void)
+{
+ { /* check if accept-ranges is "bytes" */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ if (check_header(&conn->req.res, H2O_TOKEN_ACCEPT_RANGES, "none")) {
+ ok(1);
+ return;
+ }
+ ok(check_header(&conn->req.res, H2O_TOKEN_ACCEPT_RANGES, "bytes"));
+ ok(conn->body->size == 1000);
+ ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "dfd3ae1f5c475555fad62efe42e07309fa45f2ed") == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* check a normal single range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=0-10"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 0-10/1000"));
+ ok(conn->body->size == 11);
+ ok(memcmp(conn->body->bytes, "123456789\n1", 11) == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* check an over range single range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=990-1100"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 990-999/1000"));
+ ok(conn->body->size == 10);
+ ok(memcmp(conn->body->bytes, "123456789\n", 10) == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* check a single range without end */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=989-"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 989-999/1000"));
+ ok(conn->body->size == 11);
+ ok(memcmp(conn->body->bytes, "\n123456789\n", 11) == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* check a single suffix range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-21"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 979-999/1000"));
+ ok(conn->body->size == 21);
+ ok(memcmp(conn->body->bytes, "\n123456789\n123456789\n", 21) == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* check a single suffix range over filesize */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-2100"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 0-999/1000"));
+ ok(conn->body->size == 1000);
+ ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "dfd3ae1f5c475555fad62efe42e07309fa45f2ed") == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* malformed range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-0-10, 9-, -10"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* malformed range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=0-10-12, 9-, -10"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* malformed range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytfasdf"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* half-malformed range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-0"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* single range over filesize */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=1000-1001"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* single range with "negative" length */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=900-100"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 416);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain; charset=utf-8"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes */1000"));
+ ok(conn->body->size == strlen("requested range not satisfiable"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("requested range not satisfiable")));
+ h2o_loopback_destroy(conn);
+ }
+ { /* check a half-malformed range with a normal range */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-0, 0-0"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_RANGE, "bytes 0-0/1000"));
+ ok(conn->body->size == 1);
+ ok(memcmp(conn->body->bytes, "1", 1) == 0);
+ h2o_loopback_destroy(conn);
+ }
+ { /* multiple ranges */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ ssize_t content_type_index;
+ h2o_iovec_t content_type, expected[2] = {{NULL}};
+ char boundary[BOUNDARY_SIZE + 1];
+ size_t mimebaselen = strlen("multipart/byteranges; boundary=");
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=-0, 0-9,-11"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ if ((content_type_index = h2o_find_header(&conn->req.res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) == -1) {
+ ok(0);
+ return;
+ }
+ content_type = conn->req.res.headers.entries[content_type_index].value;
+ ok(h2o_memis(content_type.base, mimebaselen, "multipart/byteranges; boundary=", mimebaselen));
+ memcpy(boundary, content_type.base + mimebaselen, BOUNDARY_SIZE);
+ boundary[BOUNDARY_SIZE] = 0;
+ expected[0].base = h2o_mem_alloc_pool(&conn->req.pool, 256);
+ expected[0].len =
+ sprintf(expected[0].base, "Content-Type: %s\r\nContent-Range: bytes 0-9/1000\r\n\r\n%s", "text/plain", "123456789\n");
+ expected[1].base = h2o_mem_alloc_pool(&conn->req.pool, 256);
+ expected[1].len = sprintf(expected[1].base, "Content-Type: %s\r\nContent-Range: bytes 989-999/1000\r\n\r\n%s", "text/plain",
+ "\n123456789\n");
+ ok(h2o_find_header(&conn->req.res.headers, H2O_TOKEN_CONTENT_RANGE, -1) == -1);
+ ok(conn->body->size == conn->req.res.content_length);
+ ok(check_multirange_body(conn->body->bytes, boundary, expected, 2));
+ h2o_loopback_destroy(conn);
+ }
+ { /* multiple ranges with plenty of WS and COMMA */
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ ssize_t content_type_index;
+ h2o_iovec_t content_type, expected[2] = {{NULL}};
+ char boundary[BOUNDARY_SIZE + 1];
+ size_t mimebaselen = strlen("multipart/byteranges; boundary=");
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_add_header(&conn->req.pool, &conn->req.headers, H2O_TOKEN_RANGE, NULL, H2O_STRLIT("bytes=,\t,1-3 ,, ,5-9,"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 206);
+ if ((content_type_index = h2o_find_header(&conn->req.res.headers, H2O_TOKEN_CONTENT_TYPE, -1)) == -1) {
+ ok(0);
+ return;
+ }
+ content_type = conn->req.res.headers.entries[content_type_index].value;
+ ok(h2o_memis(content_type.base, mimebaselen, "multipart/byteranges; boundary=", mimebaselen));
+ memcpy(boundary, content_type.base + mimebaselen, BOUNDARY_SIZE);
+ boundary[BOUNDARY_SIZE] = 0;
+ expected[0].base = h2o_mem_alloc_pool(&conn->req.pool, 256);
+ expected[0].len =
+ sprintf(expected[0].base, "Content-Type: %s\r\nContent-Range: bytes 1-3/1000\r\n\r\n%s", "text/plain", "234");
+ expected[1].base = h2o_mem_alloc_pool(&conn->req.pool, 256);
+ expected[1].len =
+ sprintf(expected[1].base, "Content-Type: %s\r\nContent-Range: bytes 5-9/1000\r\n\r\n%s", "text/plain", "6789\n");
+ ok(h2o_find_header(&conn->req.res.headers, H2O_TOKEN_CONTENT_RANGE, -1) == -1);
+ ok(conn->body->size == conn->req.res.content_length);
+ ok(check_multirange_body(conn->body->bytes, boundary, expected, 2));
+ h2o_loopback_destroy(conn);
+ }
+}
+
+void test_lib__handler__file_c()
+{
+ h2o_globalconf_t globalconf;
+ h2o_hostconf_t *hostconf;
+ h2o_pathconf_t *pathconf;
+
+ h2o_config_init(&globalconf);
+ hostconf = h2o_config_register_host(&globalconf, h2o_iovec_init(H2O_STRLIT("default")), 65535);
+ pathconf = h2o_config_register_path(hostconf, "/", 0);
+ h2o_file_register(pathconf, "t/00unit/assets", NULL, NULL, 0);
+
+ h2o_context_init(&ctx, test_loop, &globalconf);
+
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello html\n")));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index.html"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index.html"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/html"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello html\n")));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000.txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(conn->body->size == 1000);
+ ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "dfd3ae1f5c475555fad62efe42e07309fa45f2ed") == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000000.txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/1000000.txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(conn->body->size == 1000000);
+ ok(strcmp(sha1sum(conn->body->bytes, conn->body->size), "00c8ab71d0914dce6a1ec2eaa0fda0df7044b2a2") == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 200);
+ ok(check_header(&conn->req.res, H2O_TOKEN_CONTENT_TYPE, "text/plain"));
+ ok(h2o_memis(conn->body->bytes, conn->body->size, H2O_STRLIT("hello text\n")));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "/index_txt/"));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "/index_txt/"));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt_as_dir/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "/index_txt_as_dir/index.txt/"));
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/index_txt_as_dir/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "/index_txt_as_dir/index.txt/"));
+ h2o_loopback_destroy(conn);
+ }
+ subtest("if-modified-since", test_if_modified_since);
+ subtest("if-match", test_if_match);
+ subtest("process_range()", test_process_range);
+ subtest("range request", test_range_req);
+
+ h2o_context_dispose(&ctx);
+ h2o_config_dispose(&globalconf);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/headers.c b/web/server/h2o/libh2o/t/00unit/lib/handler/headers.c
new file mode 100644
index 00000000..9ef8386b
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/headers.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015 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/handler/headers.c"
+
+static int headers_are(h2o_mem_pool_t *pool, h2o_headers_t *headers, const char *s, size_t len)
+{
+ size_t i;
+ h2o_iovec_t flattened = {NULL};
+
+ for (i = 0; i != headers->size; ++i) {
+ flattened = h2o_concat(pool, flattened, *headers->entries[i].name, h2o_iovec_init(H2O_STRLIT(": ")),
+ headers->entries[i].value, h2o_iovec_init(H2O_STRLIT("\n")));
+ }
+
+ return h2o_memis(flattened.base, flattened.len, s, len);
+}
+
+static void setup_headers(h2o_mem_pool_t *pool, h2o_headers_t *headers)
+{
+ *headers = (h2o_headers_t){NULL};
+ h2o_add_header(pool, headers, H2O_TOKEN_CONTENT_TYPE, NULL, H2O_STRLIT("text/plain"));
+ h2o_add_header(pool, headers, H2O_TOKEN_CACHE_CONTROL, NULL, H2O_STRLIT("public, max-age=86400"));
+ h2o_add_header(pool, headers, H2O_TOKEN_SET_COOKIE, NULL, H2O_STRLIT("a=b"));
+ h2o_add_header_by_str(pool, headers, H2O_STRLIT("x-foo"), 0, NULL, H2O_STRLIT("bar"));
+}
+
+void test_lib__handler__headers_c(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_headers_t headers;
+ h2o_headers_command_t cmd;
+ h2o_iovec_t header_str;
+
+ h2o_mem_init_pool(&pool);
+
+ /* tests using token headers */
+ setup_headers(&pool, &headers);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\n")));
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_ADD, &H2O_TOKEN_SET_COOKIE->buf, {H2O_STRLIT("c=d")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(
+ &pool, &headers,
+ H2O_STRLIT(
+ "content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\nset-cookie: c=d\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_APPEND, &H2O_TOKEN_CACHE_CONTROL->buf, {H2O_STRLIT("public")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(
+ &pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400, public\nset-cookie: a=b\nx-foo: bar\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_MERGE, &H2O_TOKEN_CACHE_CONTROL->buf, {H2O_STRLIT("public")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_SET, &H2O_TOKEN_CACHE_CONTROL->buf, {H2O_STRLIT("no-cache")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\nset-cookie: a=b\nx-foo: bar\ncache-control: no-cache\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_SETIFEMPTY, &H2O_TOKEN_CACHE_CONTROL->buf, {H2O_STRLIT("no-cache")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\n")));
+
+ /* tests using non-token headers */
+ header_str = h2o_iovec_init(H2O_STRLIT("x-foo"));
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_ADD, &header_str, {H2O_STRLIT("baz")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(
+ &pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\nx-foo: baz\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_APPEND, &header_str, {H2O_STRLIT("bar")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(
+ &pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar, bar\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_MERGE, &header_str, {H2O_STRLIT("bar")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_SET, &header_str, {H2O_STRLIT("baz")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: baz\n")));
+
+ setup_headers(&pool, &headers);
+ cmd = (h2o_headers_command_t){H2O_HEADERS_CMD_SETIFEMPTY, &header_str, {H2O_STRLIT("baz")}};
+ h2o_rewrite_headers(&pool, &headers, &cmd);
+ ok(headers_are(&pool, &headers,
+ H2O_STRLIT("content-type: text/plain\ncache-control: public, max-age=86400\nset-cookie: a=b\nx-foo: bar\n")));
+
+ h2o_mem_clear_pool(&pool);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/mimemap.c b/web/server/h2o/libh2o/t/00unit/lib/handler/mimemap.c
new file mode 100644
index 00000000..51ebdff6
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/mimemap.c
@@ -0,0 +1,127 @@
+/*
+ * 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/handler/mimemap.c"
+
+static void test_default_attributes(void)
+{
+ h2o_mime_attributes_t attr;
+
+ h2o_mimemap_get_default_attributes("text/plain", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL);
+
+ h2o_mimemap_get_default_attributes("text/plain; charset=utf-8", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL);
+
+ h2o_mimemap_get_default_attributes("application/xhtml+xml", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL);
+
+ h2o_mimemap_get_default_attributes("application/xhtml+xml; charset=utf-8", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL);
+
+ h2o_mimemap_get_default_attributes("text/css", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST);
+
+ h2o_mimemap_get_default_attributes("text/css; charset=utf-8", &attr);
+ ok(attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_HIGHEST);
+
+ h2o_mimemap_get_default_attributes("application/octet-stream", &attr);
+ ok(!attr.is_compressible);
+ ok(attr.priority == H2O_MIME_ATTRIBUTE_PRIORITY_NORMAL);
+}
+
+static int is_mimetype(h2o_mimemap_type_t *type, const char *expected)
+{
+ return type->type == H2O_MIMEMAP_TYPE_MIMETYPE && type->data.mimetype.len == strlen(expected) &&
+ memcmp(type->data.mimetype.base, expected, type->data.mimetype.len) == 0;
+}
+
+static void test_basic()
+{
+ h2o_mimemap_t *mimemap = h2o_mimemap_create(), *mimemap2;
+
+ subtest("default-attributes", test_default_attributes);
+
+ /* default and set default */
+ ok(is_mimetype(h2o_mimemap_get_default_type(mimemap), "application/octet-stream"));
+ {
+ char buf[sizeof("text/plain")];
+ strcpy(buf, "text/plain");
+ h2o_mimemap_set_default_type(mimemap, buf, NULL);
+ memset(buf, 0, sizeof(buf));
+ }
+ ok(is_mimetype(h2o_mimemap_get_default_type(mimemap), "text/plain"));
+
+ /* set and overwrite */
+ h2o_mimemap_define_mimetype(mimemap, "foo", "example/foo", NULL);
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))), "example/foo"));
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("FOO"))), "example/foo"));
+ ok(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))) ==
+ h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(H2O_STRLIT("example/foo")), 0));
+ h2o_mimemap_define_mimetype(mimemap, "foo", "example/overwritten", NULL);
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))), "example/overwritten"));
+ ok(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))) ==
+ h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(H2O_STRLIT("example/overwritten")), 0));
+ ok(h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(H2O_STRLIT("example/foo")), 0) == NULL);
+
+ /* clone and release */
+ mimemap2 = h2o_mimemap_clone(mimemap);
+ ok(is_mimetype(h2o_mimemap_get_default_type(mimemap2), "text/plain"));
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap2, h2o_iovec_init(H2O_STRLIT("foo"))), "example/overwritten"));
+ ok(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))) ==
+ h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(H2O_STRLIT("example/overwritten")), 0));
+ h2o_mem_release_shared(mimemap2);
+
+ /* check original */
+ ok(is_mimetype(h2o_mimemap_get_default_type(mimemap), "text/plain"));
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))), "example/overwritten"));
+
+ /* remove */
+ h2o_mimemap_remove_type(mimemap, "foo");
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))), "text/plain"));
+ ok(h2o_mimemap_get_type_by_mimetype(mimemap, h2o_iovec_init(H2O_STRLIT("example/overwritten")), 0) == NULL);
+ h2o_mimemap_remove_type(mimemap, "foo");
+ ok(is_mimetype(h2o_mimemap_get_type_by_extension(mimemap, h2o_iovec_init(H2O_STRLIT("foo"))), "text/plain"));
+
+ h2o_mem_release_shared(mimemap);
+}
+
+static void test_dynamic()
+{
+ h2o_mimemap_t *mimemap = h2o_mimemap_create();
+ const char *exts[] = {".php", NULL};
+ h2o_globalconf_t global = {NULL};
+ h2o_mimemap_define_dynamic(mimemap, exts, &global);
+ h2o_mem_release_shared(mimemap);
+}
+
+void test_lib__handler__mimemap_c()
+{
+ subtest("basic", test_basic);
+ subtest("dynamic", test_dynamic);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/handler/redirect.c b/web/server/h2o/libh2o/t/00unit/lib/handler/redirect.c
new file mode 100644
index 00000000..90cb987d
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/handler/redirect.c
@@ -0,0 +1,82 @@
+/*
+ * 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/handler/redirect.c"
+
+static h2o_context_t ctx;
+
+static int check_header(h2o_res_t *res, const h2o_token_t *header_name, const char *expected)
+{
+ size_t index = h2o_find_header(&res->headers, header_name, SIZE_MAX);
+ if (index == SIZE_MAX)
+ return 0;
+ return h2o_lcstris(res->headers.entries[index].value.base, res->headers.entries[index].value.len, expected, strlen(expected));
+}
+
+void test_lib__handler__redirect_c()
+{
+ h2o_globalconf_t globalconf;
+ h2o_hostconf_t *hostconf;
+ h2o_pathconf_t *pathconf;
+
+ h2o_config_init(&globalconf);
+ hostconf = h2o_config_register_host(&globalconf, h2o_iovec_init(H2O_STRLIT("default")), 65535);
+ pathconf = h2o_config_register_path(hostconf, "/", 0);
+ h2o_redirect_register(pathconf, 0, 301, "https://example.com/bar/");
+
+ h2o_context_init(&ctx, test_loop, &globalconf);
+
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/"));
+ ok(conn->body->size != 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("GET"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/abc"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/abc"));
+ ok(conn->body->size != 0);
+ h2o_loopback_destroy(conn);
+ }
+ {
+ h2o_loopback_conn_t *conn = h2o_loopback_create(&ctx, ctx.globalconf->hosts);
+ conn->req.input.method = h2o_iovec_init(H2O_STRLIT("HEAD"));
+ conn->req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_loopback_run_loop(conn);
+ ok(conn->req.res.status == 301);
+ ok(check_header(&conn->req.res, H2O_TOKEN_LOCATION, "https://example.com/bar/"));
+ ok(conn->body->size == 0);
+ h2o_loopback_destroy(conn);
+ }
+
+ h2o_context_dispose(&ctx);
+ h2o_config_dispose(&globalconf);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/http2/cache_digests.c b/web/server/h2o/libh2o/t/00unit/lib/http2/cache_digests.c
new file mode 100644
index 00000000..3e55d173
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/http2/cache_digests.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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 <string.h>
+#include "../../test.h"
+#include "../../../../lib/http2/cache_digests.c"
+
+static void test_calc_hash(void)
+{
+ ok(calc_hash(H2O_STRLIT("https://example.com/style.css"), H2O_STRLIT("")) == 0xbaf9e86f03330860);
+ ok(calc_hash(H2O_STRLIT("https://example.com/style.css"), H2O_STRLIT("\"deadbeef\"")) == 0xa53eb398509042d7);
+}
+
+static void test_decode(void)
+{
+ h2o_cache_digests_t *digests = NULL;
+
+ h2o_cache_digests_load_header(&digests, H2O_STRLIT("AeLA"));
+ ok(digests != NULL);
+ if (digests == NULL)
+ return;
+ ok(digests->fresh.url_only.size == 1);
+ ok(digests->fresh.url_and_etag.size == 0);
+ ok(digests->fresh.url_only.entries[0].capacity_bits == 7);
+ ok(digests->fresh.url_only.entries[0].keys.size == 1);
+ ok(digests->fresh.url_only.entries[0].keys.entries[0] == 0x0b);
+ ok(!digests->fresh.complete);
+
+ ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/cache-digests.cgi/hello.js")) ==
+ H2O_CACHE_DIGESTS_STATE_FRESH);
+ ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/notfound.js")) ==
+ H2O_CACHE_DIGESTS_STATE_UNKNOWN);
+
+ h2o_cache_digests_load_header(&digests, H2O_STRLIT("FOO; stale, AcA; validators; complete"));
+ ok(digests->fresh.url_only.size == 1);
+ ok(digests->fresh.url_and_etag.size == 1);
+ ok(digests->fresh.url_and_etag.entries[0].capacity_bits == 7);
+ ok(digests->fresh.url_and_etag.entries[0].keys.size == 0);
+ ok(digests->fresh.complete);
+
+ ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/notfound.js")) ==
+ H2O_CACHE_DIGESTS_STATE_NOT_CACHED);
+ ok(h2o_cache_digests_lookup_by_url(digests, H2O_STRLIT("https://127.0.0.1.xip.io:8081/cache-digests.cgi/hello.js")) ==
+ H2O_CACHE_DIGESTS_STATE_FRESH);
+
+ h2o_cache_digests_load_header(&digests, H2O_STRLIT("AcA; reset"));
+ ok(digests->fresh.url_only.size == 1);
+ ok(digests->fresh.url_and_etag.size == 0);
+ ok(digests->fresh.url_only.entries[0].capacity_bits == 7);
+ ok(digests->fresh.url_only.entries[0].keys.size == 0);
+ ok(!digests->fresh.complete);
+
+ h2o_cache_digests_destroy(digests);
+}
+
+void test_lib__http2__cache_digests(void)
+{
+ subtest("calc_hash", test_calc_hash);
+ subtest("test_decode", test_decode);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/http2/casper.c b/web/server/h2o/libh2o/t/00unit/lib/http2/casper.c
new file mode 100644
index 00000000..5b2a43a5
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/http2/casper.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 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 <string.h>
+#include "../../test.h"
+#include "../../../../lib/http2/casper.c"
+
+static size_t get_end_of_cookie_value(char *cookie, size_t cookie_len)
+{
+ size_t i;
+ for (i = 0; i != cookie_len; ++i)
+ if (cookie[i] == ';')
+ break;
+ return i;
+}
+
+static void test_calc_key(void)
+{
+ h2o_http2_casper_t *casper = h2o_http2_casper_create(13, 6);
+
+ unsigned key = calc_key(casper, H2O_STRLIT("/index.html")), expected;
+ memcpy(&expected, "\x14\xfe\x45\x59", 4);
+ expected &= (1 << 13) - 1;
+ ok(key == expected);
+
+ h2o_http2_casper_destroy(casper);
+}
+
+static void test_lookup(void)
+{
+ h2o_http2_casper_t *casper;
+ casper = h2o_http2_casper_create(13, 6);
+
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 0);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 1) == 0);
+ ok(casper->keys.size == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 1) == 1);
+ ok(casper->keys.size == 1);
+
+ h2o_http2_casper_destroy(casper);
+}
+
+static void test_cookie(void)
+{
+ h2o_http2_casper_t *casper;
+
+ casper = h2o_http2_casper_create(13, 6);
+
+ h2o_iovec_t cookie = h2o_http2_casper_get_cookie(casper);
+ ok(cookie.base == NULL);
+ ok(cookie.len == 0);
+
+ h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 1);
+ cookie = h2o_http2_casper_get_cookie(casper);
+ ok(cookie.len != 0);
+ cookie = h2o_strdup(NULL, cookie.base, cookie.len);
+ h2o_http2_casper_destroy(casper);
+ casper = h2o_http2_casper_create(13, 6);
+
+ h2o_http2_casper_consume_cookie(casper, cookie.base, get_end_of_cookie_value(cookie.base, cookie.len));
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.php"), 0) == 0);
+ h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.php"), 1);
+
+ h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 1);
+ cookie = h2o_http2_casper_get_cookie(casper);
+ ok(cookie.len != 0);
+ cookie = h2o_strdup(NULL, cookie.base, cookie.len);
+
+ h2o_http2_casper_destroy(casper);
+ casper = h2o_http2_casper_create(13, 6);
+
+ h2o_http2_casper_consume_cookie(casper, cookie.base, get_end_of_cookie_value(cookie.base, cookie.len));
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.php"), 0) == 1);
+
+ h2o_http2_casper_destroy(casper);
+}
+
+static void test_cookie_merge(void)
+{
+ h2o_http2_casper_t *casper;
+
+ casper = h2o_http2_casper_create(13, 6);
+ h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 1);
+ h2o_iovec_t cookie1 = h2o_http2_casper_get_cookie(casper);
+ cookie1 = h2o_strdup(NULL, cookie1.base, cookie1.len);
+ h2o_http2_casper_destroy(casper);
+
+ casper = h2o_http2_casper_create(13, 6);
+ h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.php"), 1);
+ h2o_iovec_t cookie2 = h2o_http2_casper_get_cookie(casper);
+ cookie2 = h2o_strdup(NULL, cookie2.base, cookie2.len);
+ h2o_http2_casper_destroy(casper);
+
+ casper = h2o_http2_casper_create(13, 6);
+ h2o_http2_casper_consume_cookie(casper, cookie1.base, get_end_of_cookie_value(cookie1.base, cookie1.len));
+ h2o_http2_casper_consume_cookie(casper, cookie1.base, get_end_of_cookie_value(cookie1.base, cookie1.len));
+ ok(casper->keys.size == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 1);
+ h2o_http2_casper_consume_cookie(casper, cookie2.base, get_end_of_cookie_value(cookie2.base, cookie2.len));
+ ok(casper->keys.size == 2);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.html"), 0) == 1);
+ ok(h2o_http2_casper_lookup(casper, H2O_STRLIT("/index.php"), 0) == 1);
+ h2o_http2_casper_destroy(casper);
+}
+
+void test_lib__http2__casper(void)
+{
+ subtest("calc_key", test_calc_key);
+ subtest("test_lookup", test_lookup);
+ subtest("cookie", test_cookie);
+ subtest("cookie-merge", test_cookie_merge);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/http2/hpack.c b/web/server/h2o/libh2o/t/00unit/lib/http2/hpack.c
new file mode 100644
index 00000000..059df82c
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/http2/hpack.c
@@ -0,0 +1,511 @@
+/*
+ * 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 <stdarg.h>
+#include "../../test.h"
+#include "../../../../lib/http2/hpack.c"
+
+static void test_request(h2o_iovec_t first_req, h2o_iovec_t second_req, h2o_iovec_t third_req)
+{
+ h2o_hpack_header_table_t header_table;
+ h2o_req_t req;
+ h2o_iovec_t in;
+ int r, pseudo_headers_map;
+ size_t content_length;
+ const char *err_desc = NULL;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+
+ memset(&req, 0, sizeof(req));
+ h2o_mem_init_pool(&req.pool);
+ in = first_req;
+ r = h2o_hpack_parse_headers(&req, &header_table, (const uint8_t *)in.base, in.len, &pseudo_headers_map, &content_length, NULL,
+ &err_desc);
+ ok(r == 0);
+ ok(req.input.authority.len == 15);
+ ok(memcmp(req.input.authority.base, H2O_STRLIT("www.example.com")) == 0);
+ ok(req.input.method.len == 3);
+ ok(memcmp(req.input.method.base, H2O_STRLIT("GET")) == 0);
+ ok(req.input.path.len == 1);
+ ok(memcmp(req.input.path.base, H2O_STRLIT("/")) == 0);
+ ok(req.input.scheme == &H2O_URL_SCHEME_HTTP);
+ ok(req.headers.size == 0);
+
+ h2o_mem_clear_pool(&req.pool);
+
+ memset(&req, 0, sizeof(req));
+ h2o_mem_init_pool(&req.pool);
+ in = second_req;
+ r = h2o_hpack_parse_headers(&req, &header_table, (const uint8_t *)in.base, in.len, &pseudo_headers_map, &content_length, NULL,
+ &err_desc);
+ ok(r == 0);
+ ok(req.input.authority.len == 15);
+ ok(memcmp(req.input.authority.base, H2O_STRLIT("www.example.com")) == 0);
+ ok(req.input.method.len == 3);
+ ok(memcmp(req.input.method.base, H2O_STRLIT("GET")) == 0);
+ ok(req.input.path.len == 1);
+ ok(memcmp(req.input.path.base, H2O_STRLIT("/")) == 0);
+ ok(req.input.scheme == &H2O_URL_SCHEME_HTTP);
+ ok(req.headers.size == 1);
+ ok(h2o_memis(req.headers.entries[0].name->base, req.headers.entries[0].name->len, H2O_STRLIT("cache-control")));
+ ok(h2o_lcstris(req.headers.entries[0].value.base, req.headers.entries[0].value.len, H2O_STRLIT("no-cache")));
+
+ h2o_mem_clear_pool(&req.pool);
+
+ memset(&req, 0, sizeof(req));
+ h2o_mem_init_pool(&req.pool);
+ in = third_req;
+ r = h2o_hpack_parse_headers(&req, &header_table, (const uint8_t *)in.base, in.len, &pseudo_headers_map, &content_length, NULL,
+ &err_desc);
+ ok(r == 0);
+ ok(req.input.authority.len == 15);
+ ok(memcmp(req.input.authority.base, H2O_STRLIT("www.example.com")) == 0);
+ ok(req.input.method.len == 3);
+ ok(memcmp(req.input.method.base, H2O_STRLIT("GET")) == 0);
+ ok(req.input.path.len == 11);
+ ok(memcmp(req.input.path.base, H2O_STRLIT("/index.html")) == 0);
+ ok(req.input.scheme == &H2O_URL_SCHEME_HTTPS);
+ ok(req.headers.size == 1);
+ ok(h2o_memis(req.headers.entries[0].name->base, req.headers.entries[0].name->len, H2O_STRLIT("custom-key")));
+ ok(h2o_lcstris(req.headers.entries[0].value.base, req.headers.entries[0].value.len, H2O_STRLIT("custom-value")));
+
+ h2o_hpack_dispose_header_table(&header_table);
+ h2o_mem_clear_pool(&req.pool);
+}
+
+static void check_flatten(h2o_hpack_header_table_t *header_table, h2o_res_t *res, const char *expected, size_t expected_len)
+{
+ h2o_buffer_t *buf;
+ h2o_http2_frame_t frame;
+ const char *err_desc;
+
+ h2o_buffer_init(&buf, &h2o_socket_buffer_prototype);
+ h2o_hpack_flatten_response(&buf, header_table, 1, H2O_HTTP2_SETTINGS_DEFAULT.max_frame_size, res, NULL, NULL, SIZE_MAX);
+
+ ok(h2o_http2_decode_frame(&frame, (uint8_t *)buf->bytes, buf->size, &H2O_HTTP2_SETTINGS_DEFAULT, &err_desc) > 0);
+ ok(h2o_memis(frame.payload, frame.length, expected, expected_len));
+
+ h2o_buffer_dispose(&buf);
+}
+
+static void test_hpack(void)
+{
+ h2o_mem_pool_t pool;
+ const char *err_desc;
+
+ h2o_mem_init_pool(&pool);
+
+ note("decode_int");
+ {
+ h2o_iovec_t in;
+ const uint8_t *p;
+ int32_t out;
+#define TEST(input, output) \
+ in = h2o_iovec_init(H2O_STRLIT(input)); \
+ p = (const uint8_t *)in.base; \
+ out = decode_int(&p, p + in.len, 7); \
+ ok(out == output); \
+ ok(p == (const uint8_t *)in.base + in.len);
+ TEST("\x00", 0);
+ TEST("\x03", 3);
+ TEST("\x81", 1);
+ TEST("\x7f\x00", 127);
+ TEST("\x7f\x01", 128);
+ TEST("\x7f\x7f", 254);
+ TEST("\x7f\x81\x00", 128);
+ TEST("\x7f\x80\x01", 255);
+ TEST("\x7f\xff\xff\xff\x7f", 0xfffffff + 127);
+ /* failures */
+ TEST("", -1);
+ TEST("\x7f", -1);
+ TEST("\x7f\xff", -1);
+ TEST("\x7f\xff\xff\xff\xff", -1);
+#undef TEST
+ }
+
+ note("encode_int");
+ {
+ uint8_t buf[16];
+ size_t len;
+#define TEST(encoded, value) \
+ memset(buf, 0, sizeof(buf)); \
+ len = encode_int(buf, value, 7) - buf; \
+ ok(len == sizeof(encoded) - 1); \
+ ok(memcmp(buf, encoded, sizeof(encoded) - 1) == 0);
+ TEST("\x00", 0);
+ TEST("\x03", 3);
+ TEST("\x7e", 126);
+ TEST("\x7f\x00", 127);
+ TEST("\x7f\x01", 128);
+ TEST("\x7f\x7f", 254);
+ TEST("\x7f\x80\x01", 255);
+ TEST("\x7f\xff\xff\xff\x7f", 0xfffffff + 127);
+#undef TEST
+ }
+
+ note("decode_huffman");
+ {
+ h2o_iovec_t huffcode = {H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff")};
+ uint8_t flags = 0;
+ h2o_iovec_t *decoded = decode_huffman(&pool, (const uint8_t *)huffcode.base, huffcode.len, &flags);
+ ok(decoded->len == sizeof("www.example.com") - 1);
+ ok(strcmp(decoded->base, "www.example.com") == 0);
+ ok(flags == 0);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("decode_string_bogus");
+ {
+ char *str = "\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff";
+ const uint8_t *buf;
+ const char *errstr = NULL;
+ size_t len;
+ len = strlen(str);
+ buf = (const uint8_t *)str;
+ /* since we're only passing one byte, decode_string should fail */
+ h2o_iovec_t *decoded = decode_string(&pool, &buf, &buf[1], 0, &errstr);
+ ok(decoded == NULL);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("decode_header (literal header field with indexing)");
+ {
+ struct st_h2o_decode_header_result_t result;
+ h2o_hpack_header_table_t header_table;
+ h2o_iovec_t in;
+ int r;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+ in = h2o_iovec_init(
+ H2O_STRLIT("\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0d\x63\x75\x73\x74\x6f\x6d\x2d\x68\x65\x61\x64\x65\x72"));
+ const uint8_t *p = (const uint8_t *)in.base;
+ err_desc = NULL;
+ r = decode_header(&pool, &result, &header_table, &p, p + in.len, &err_desc);
+ ok(r == 0);
+ ok(result.name->len == 10);
+ ok(strcmp(result.name->base, "custom-key") == 0);
+ ok(result.value->len == 13);
+ ok(strcmp(result.value->base, "custom-header") == 0);
+ ok(header_table.hpack_size == 55);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("decode_header (literal header field without indexing)");
+ {
+ struct st_h2o_decode_header_result_t result;
+ h2o_hpack_header_table_t header_table;
+ h2o_iovec_t in;
+ int r;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+ in = h2o_iovec_init(H2O_STRLIT("\x04\x0c\x2f\x73\x61\x6d\x70\x6c\x65\x2f\x70\x61\x74\x68"));
+ const uint8_t *p = (const uint8_t *)in.base;
+ err_desc = NULL;
+ r = decode_header(&pool, &result, &header_table, &p, p + in.len, &err_desc);
+ ok(r == 0);
+ ok(result.name == &H2O_TOKEN_PATH->buf);
+ ok(result.value->len == 12);
+ ok(strcmp(result.value->base, "/sample/path") == 0);
+ ok(header_table.hpack_size == 0);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("decode_header (literal header field never indexed)");
+ {
+ struct st_h2o_decode_header_result_t result;
+ h2o_hpack_header_table_t header_table;
+ h2o_iovec_t in;
+ int r;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+ in = h2o_iovec_init(H2O_STRLIT("\x10\x08\x70\x61\x73\x73\x77\x6f\x72\x64\x06\x73\x65\x63\x72\x65\x74"));
+ const uint8_t *p = (const uint8_t *)in.base;
+ err_desc = NULL;
+ r = decode_header(&pool, &result, &header_table, &p, p + in.len, &err_desc);
+ ok(r == 0);
+ ok(result.name->len == 8);
+ ok(strcmp(result.name->base, "password") == 0);
+ ok(result.value->len == 6);
+ ok(strcmp(result.value->base, "secret") == 0);
+ ok(header_table.hpack_size == 0);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("decode_header (indexed header field)");
+ {
+ struct st_h2o_decode_header_result_t result;
+ h2o_hpack_header_table_t header_table;
+ h2o_iovec_t in;
+ int r;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+ in = h2o_iovec_init(H2O_STRLIT("\x82"));
+ const uint8_t *p = (const uint8_t *)in.base;
+ err_desc = NULL;
+ r = decode_header(&pool, &result, &header_table, &p, p + in.len, &err_desc);
+ ok(r == 0);
+ ok(result.name == &H2O_TOKEN_METHOD->buf);
+ ok(result.value->len == 3);
+ ok(strcmp(result.value->base, "GET") == 0);
+ ok(header_table.hpack_size == 0);
+ }
+ h2o_mem_clear_pool(&pool);
+
+ note("request examples without huffman coding");
+ test_request(h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x0f\x77\x77\x77\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d")),
+ h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x08\x6e\x6f\x2d\x63\x61\x63\x68\x65")),
+ h2o_iovec_init(H2O_STRLIT("\x82\x87\x85\xbf\x40\x0a\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0c\x63\x75\x73\x74"
+ "\x6f\x6d\x2d\x76\x61\x6c\x75\x65")));
+
+ note("request examples with huffman coding");
+ test_request(h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\x41\x8c\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff")),
+ h2o_iovec_init(H2O_STRLIT("\x82\x86\x84\xbe\x58\x86\xa8\xeb\x10\x64\x9c\xbf")),
+ h2o_iovec_init(H2O_STRLIT(
+ "\x82\x87\x85\xbf\x40\x88\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f\x89\x25\xa8\x49\xe9\x5b\xb8\xe8\xb4\xbf")));
+
+ note("encode_huffman");
+ {
+ h2o_iovec_t huffcode = {H2O_STRLIT("\xf1\xe3\xc2\xe5\xf2\x3a\x6b\xa0\xab\x90\xf4\xff")};
+ char buf[sizeof("www.example.com")];
+ size_t l = encode_huffman((uint8_t *)buf, (uint8_t *)H2O_STRLIT("www.example.com"));
+ ok(l == huffcode.len);
+ ok(memcmp(buf, huffcode.base, huffcode.len) == 0);
+ }
+
+ note("response examples with huffmann coding");
+ {
+ h2o_hpack_header_table_t header_table;
+ h2o_res_t res;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 256;
+
+ memset(&res, 0, sizeof(res));
+ res.status = 302;
+ res.reason = "Found";
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, NULL, H2O_STRLIT("private"));
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, NULL, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT"));
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, NULL, H2O_STRLIT("https://www.example.com"));
+ check_flatten(&header_table, &res, H2O_STRLIT("\x08\x03\x33\x30\x32\x58\x85\xae\xc3\x77\x1a\x4b\x61\x96\xd0\x7a\xbe\x94\x10"
+ "\x54\xd4\x44\xa8\x20\x05\x95\x04\x0b\x81\x66\xe0\x82\xa6\x2d\x1b\xff\x6e\x91"
+ "\x9d\x29\xad\x17\x18\x63\xc7\x8f\x0b\x97\xc8\xe9\xae\x82\xae\x43\xd3"));
+
+ memset(&res, 0, sizeof(res));
+ res.status = 307;
+ res.reason = "Temporary Redirect";
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_CACHE_CONTROL, NULL, H2O_STRLIT("private"));
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_DATE, NULL, H2O_STRLIT("Mon, 21 Oct 2013 20:13:21 GMT"));
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_LOCATION, NULL, H2O_STRLIT("https://www.example.com"));
+ check_flatten(&header_table, &res, H2O_STRLIT("\x08\x03\x33\x30\x37\xc0\xbf\xbe"));
+#if 0
+ h2o_iovec_init(H2O_STRLIT("\x48\x03\x33\x30\x37\xc1\xc0\xbf")),
+ h2o_iovec_init(H2O_STRLIT("\x88\xc1\x61\x1d\x4d\x6f\x6e\x2c\x20\x32\x31\x20\x4f\x63\x74\x20\x32\x30\x31\x33\x20\x32\x30\x3a\x31\x33\x3a\x32\x32\x20\x47\x4d\x54\xc0\x5a\x04\x67\x7a\x69\x70\x77\x38\x66\x6f\x6f\x3d\x41\x53\x44\x4a\x4b\x48\x51\x4b\x42\x5a\x58\x4f\x51\x57\x45\x4f\x50\x49\x55\x41\x58\x51\x57\x45\x4f\x49\x55\x3b\x20\x6d\x61\x78\x2d\x61\x67\x65\x3d\x33\x36\x30\x30\x3b\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x31")));
+#endif
+ }
+
+ h2o_mem_clear_pool(&pool);
+}
+
+static void parse_and_compare_request(h2o_hpack_header_table_t *ht, const char *promise_base, size_t promise_len,
+ h2o_iovec_t expected_method, const h2o_url_scheme_t *expected_scheme,
+ h2o_iovec_t expected_authority, h2o_iovec_t expected_path, ...)
+{
+ h2o_req_t req = {NULL};
+ h2o_mem_init_pool(&req.pool);
+
+ int pseudo_header_exists_map = 0;
+ size_t content_length = SIZE_MAX;
+ const char *err_desc = NULL;
+ int r = h2o_hpack_parse_headers(&req, ht, (void *)(promise_base + 13), promise_len - 13, &pseudo_header_exists_map,
+ &content_length, NULL, &err_desc);
+ ok(r == 0);
+ ok(h2o_memis(req.input.method.base, req.input.method.len, expected_method.base, expected_method.len));
+ ok(req.input.scheme == expected_scheme);
+ ok(h2o_memis(req.input.authority.base, req.input.authority.len, expected_authority.base, expected_authority.len));
+ ok(h2o_memis(req.input.path.base, req.input.path.len, expected_path.base, expected_path.len));
+
+ va_list args;
+ va_start(args, expected_path);
+ size_t i;
+ for (i = 0; i != req.headers.size; ++i) {
+ h2o_iovec_t expected_name = va_arg(args, h2o_iovec_t);
+ if (expected_name.base == NULL)
+ break;
+ h2o_iovec_t expected_value = va_arg(args, h2o_iovec_t);
+ ok(h2o_memis(req.headers.entries[i].name->base, req.headers.entries[i].name->len, expected_name.base, expected_name.len));
+ ok(h2o_memis(req.headers.entries[i].value.base, req.headers.entries[i].value.len, expected_value.base, expected_value.len));
+ }
+ ok(i == req.headers.size);
+ va_end(args);
+
+ h2o_mem_clear_pool(&req.pool);
+}
+
+static void test_hpack_push(void)
+{
+ const static h2o_iovec_t method = {H2O_STRLIT("GET")}, authority = {H2O_STRLIT("example.com")},
+ user_agent = {H2O_STRLIT(
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:40.0) Gecko/20100101 Firefox/40.0")},
+ accept_root = {H2O_STRLIT("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")},
+ accept_images = {H2O_STRLIT("image/png,image/*;q=0.8,*/*;q=0.5")},
+ accept_language = {H2O_STRLIT("ja,en-US;q=0.7,en;q=0.3")},
+ accept_encoding = {H2O_STRLIT("gzip, deflate")}, referer = {H2O_STRLIT("https://example.com/")};
+
+ h2o_hpack_header_table_t encode_table = {NULL}, decode_table = {NULL};
+ encode_table.hpack_capacity = decode_table.hpack_capacity = 4096;
+ h2o_req_t req = {NULL};
+ h2o_mem_init_pool(&req.pool);
+ h2o_buffer_t *buf;
+ h2o_buffer_init(&buf, &h2o_socket_buffer_prototype);
+
+ /* setup first request */
+ req.input.method = method;
+ req.input.scheme = &H2O_URL_SCHEME_HTTPS;
+ req.input.authority = authority;
+ req.input.path = h2o_iovec_init(H2O_STRLIT("/"));
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_USER_AGENT, NULL, user_agent.base, user_agent.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT, NULL, accept_root.base, accept_root.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT_LANGUAGE, NULL, accept_language.base, accept_language.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT_ENCODING, NULL, accept_encoding.base, accept_encoding.len);
+
+ /* serialize, deserialize, and compare */
+ h2o_hpack_flatten_request(&buf, &encode_table, 0, 16384, &req, 0);
+ parse_and_compare_request(&decode_table, buf->bytes, buf->size, method, &H2O_URL_SCHEME_HTTPS, authority,
+ h2o_iovec_init(H2O_STRLIT("/")), H2O_TOKEN_USER_AGENT->buf, user_agent, H2O_TOKEN_ACCEPT->buf,
+ accept_root, H2O_TOKEN_ACCEPT_LANGUAGE->buf, accept_language, H2O_TOKEN_ACCEPT_ENCODING->buf,
+ accept_encoding, (h2o_iovec_t){NULL});
+ h2o_buffer_consume(&buf, buf->size);
+
+ /* setup second request */
+ req.input.path = h2o_iovec_init(H2O_STRLIT("/banner.jpg"));
+ req.headers = (h2o_headers_t){NULL};
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_USER_AGENT, NULL, user_agent.base, user_agent.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT, NULL, accept_images.base, accept_images.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT_LANGUAGE, NULL, accept_language.base, accept_language.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_ACCEPT_ENCODING, NULL, accept_encoding.base, accept_encoding.len);
+ h2o_add_header(&req.pool, &req.headers, H2O_TOKEN_REFERER, NULL, referer.base, referer.len);
+
+ /* serialize, deserialize, and compare */
+ h2o_hpack_flatten_request(&buf, &encode_table, 0, 16384, &req, 0);
+ parse_and_compare_request(
+ &decode_table, buf->bytes, buf->size, method, &H2O_URL_SCHEME_HTTPS, authority, h2o_iovec_init(H2O_STRLIT("/banner.jpg")),
+ H2O_TOKEN_USER_AGENT->buf, user_agent, H2O_TOKEN_ACCEPT->buf, accept_images, H2O_TOKEN_ACCEPT_LANGUAGE->buf,
+ accept_language, H2O_TOKEN_ACCEPT_ENCODING->buf, accept_encoding, H2O_TOKEN_REFERER->buf, referer, (h2o_iovec_t){NULL});
+ h2o_buffer_consume(&buf, buf->size);
+
+ /* setup third request (headers are the same) */
+ req.input.path = h2o_iovec_init(H2O_STRLIT("/icon.png"));
+
+ /* serialize, deserialize, and compare */
+ h2o_hpack_flatten_request(&buf, &encode_table, 0, 16384, &req, 0);
+ parse_and_compare_request(&decode_table, buf->bytes, buf->size, method, &H2O_URL_SCHEME_HTTPS, authority,
+ h2o_iovec_init(H2O_STRLIT("/icon.png")), H2O_TOKEN_USER_AGENT->buf, user_agent, H2O_TOKEN_ACCEPT->buf,
+ accept_images, H2O_TOKEN_ACCEPT_LANGUAGE->buf, accept_language, H2O_TOKEN_ACCEPT_ENCODING->buf,
+ accept_encoding, H2O_TOKEN_REFERER->buf, referer, (h2o_iovec_t){NULL});
+ h2o_buffer_consume(&buf, buf->size);
+
+ h2o_buffer_dispose(&buf);
+ h2o_mem_clear_pool(&req.pool);
+}
+
+static void test_hpack_dynamic_table(void)
+{
+ h2o_hpack_header_table_t header_table;
+ uint8_t encoded[256], *p;
+ h2o_iovec_t n, v;
+
+ memset(&header_table, 0, sizeof(header_table));
+ header_table.hpack_capacity = 4096;
+
+ p = encoded;
+ /* expected: literal header with incremental indexing (name not indexed) */
+ n = h2o_iovec_init(H2O_STRLIT("x-name"));
+ v = h2o_iovec_init(H2O_STRLIT("v1"));
+ p = encode_header(&header_table, p, &n, &v);
+ /* expected: literal header with incremental indexing (name indexed) */
+ v = h2o_iovec_init(H2O_STRLIT("v2"));
+ p = encode_header(&header_table, p, &n, &v);
+ /* expected: literal header with incremental indexing (name indexed, referring to the name associated with v2) */
+ v = h2o_iovec_init(H2O_STRLIT("v3"));
+ p = encode_header(&header_table, p, &n, &v);
+ /* expected: indexed header field */
+ v = h2o_iovec_init(H2O_STRLIT("v1"));
+ p = encode_header(&header_table, p, &n, &v);
+
+ const h2o_iovec_t expected = h2o_iovec_init(
+ H2O_STRLIT("\x40\x85" /* literal header with incremental indexing (name not indexed, 5 bytes, huffman coded) */
+ "\xf2\xb5\x43\xa4\xbf" /* "x-name" */
+ "\x02" /* value not compressed, 2 bytes */
+ "v1" /* "v1" */
+ "\x7e" /* literal header with incremental indexing (name indexed) */
+ "\x02" /* value not compressed, 2 bytes */
+ "v2" /* "v2" */
+ "\x7e" /* literal header with incremental indexing (name indexed, referring to the last entry) */
+ "\x02" /* value not compressed, 2 bytes */
+ "v3" /* "v3" */
+ "\xc0" /* indexed header field */
+ ));
+ ok(p - encoded == expected.len);
+ ok(memcmp(encoded, expected.base, expected.len) == 0);
+}
+
+void test_token_wo_hpack_id(void)
+{
+ h2o_mem_pool_t pool;
+ h2o_mem_init_pool(&pool);
+ h2o_hpack_header_table_t table = {NULL};
+ table.hpack_capacity = 4096;
+ h2o_res_t res = {0};
+ h2o_buffer_t *buf;
+ h2o_buffer_init(&buf, &h2o_socket_buffer_prototype);
+
+ res.status = 200;
+ res.reason = "OK";
+ h2o_add_header(&pool, &res.headers, H2O_TOKEN_TE, NULL, H2O_STRLIT("test"));
+
+ h2o_hpack_flatten_response(&buf, &table, 1, H2O_HTTP2_SETTINGS_DEFAULT.max_frame_size, &res, NULL, NULL, SIZE_MAX);
+ ok(h2o_memis(buf->bytes + 9, buf->size - 9, H2O_STRLIT("\x88" /* :status:200 */
+ "\x40\x02" /* literal header w. incremental indexing, raw, TE */
+ "te"
+ "\x83" /* header value, huffman */
+ "IP\x9f" /* test */)));
+ h2o_buffer_consume(&buf, buf->size);
+ h2o_hpack_flatten_response(&buf, &table, 1, H2O_HTTP2_SETTINGS_DEFAULT.max_frame_size, &res, NULL, NULL, SIZE_MAX);
+ ok(h2o_memis(buf->bytes + 9, buf->size - 9, H2O_STRLIT("\x88" /* :status:200 */
+ "\xbe" /* te: test, indexed */)));
+
+ h2o_buffer_dispose(&buf);
+ h2o_hpack_dispose_header_table(&table);
+ h2o_mem_clear_pool(&pool);
+}
+
+void test_lib__http2__hpack(void)
+{
+ subtest("hpack", test_hpack);
+ subtest("hpack-push", test_hpack_push);
+ subtest("hpack-dynamic-table", test_hpack_dynamic_table);
+ subtest("token-wo-hpack-id", test_token_wo_hpack_id);
+}
diff --git a/web/server/h2o/libh2o/t/00unit/lib/http2/scheduler.c b/web/server/h2o/libh2o/t/00unit/lib/http2/scheduler.c
new file mode 100644
index 00000000..d643cbf4
--- /dev/null
+++ b/web/server/h2o/libh2o/t/00unit/lib/http2/scheduler.c
@@ -0,0 +1,563 @@
+/*
+ * 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 <string.h>
+#include "../../test.h"
+#include "../../../../lib/http2/scheduler.c"
+
+static void test_queue(void)
+{
+ h2o_http2_scheduler_queue_t drr;
+ struct node_t {
+ h2o_http2_scheduler_queue_node_t super;
+ uint16_t weight;
+ size_t cnt;
+ } w256 = {{{NULL}}, 256}, w128 = {{{NULL}}, 128}, w32 = {{{NULL}}, 32}, w1 = {{{NULL}}, 1};
+ size_t i;
+
+ queue_init(&drr);
+ queue_set(&drr, &w256.super, 256);
+ queue_set(&drr, &w128.super, 128);
+ queue_set(&drr, &w32.super, 32);
+ queue_set(&drr, &w1.super, 1);
+
+ for (i = 0; i != (256 + 128 + 32 + 1) * 100; ++i) {
+ struct node_t *popped = (struct node_t *)queue_pop(&drr);
+ if (popped == NULL) {
+ ok(0);
+ return;
+ }
+ ++popped->cnt;
+ queue_set(&drr, &popped->super, popped->weight);
+ }
+
+ ok(w256.cnt == 25600);
+ ok(w128.cnt == 12800);
+ ok(w32.cnt == 3200);
+ ok(w1.cnt == 100);
+}
+
+typedef struct {
+ h2o_http2_scheduler_openref_t ref;
+ const char *name;
+ int still_is_active;
+ int bail_out;
+} node_t;
+
+static char output[1024];
+static size_t max_cnt;
+
+static int iterate_cb(h2o_http2_scheduler_openref_t *ref, int *still_is_active, void *_unused)
+{
+ node_t *node = (void *)ref;
+
+ if (output[0] != '\0')
+ strcat(output, ",");
+ strcat(output, node->name);
+ *still_is_active = node->still_is_active;
+
+ if (--max_cnt == 0)
+ return 1;
+ return node->bail_out;
+}
+
+static void test_round_robin(void)
+{
+ h2o_http2_scheduler_node_t root;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+
+ h2o_http2_scheduler_init(&root);
+ h2o_http2_scheduler_open(&nodeA.ref, &root, 1, 0);
+ h2o_http2_scheduler_open(&nodeB.ref, &root, 1, 0);
+ h2o_http2_scheduler_open(&nodeC.ref, &root, 1, 0);
+
+ /* none are active */
+ output[0] = '\0';
+ max_cnt = 4;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "") == 0);
+
+ /* set A to active */
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ output[0] = '\0';
+ max_cnt = 4;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,A,A,A") == 0);
+
+ /* A should change to inactive */
+ nodeA.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 4;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A") == 0);
+
+ /* set all to active */
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ nodeA.still_is_active = 1;
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ h2o_http2_scheduler_activate(&nodeC.ref);
+ output[0] = '\0';
+ max_cnt = 7;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,C,A,B,C,A") == 0);
+
+ /* change them to inactive */
+ nodeA.still_is_active = 0;
+ nodeB.still_is_active = 0;
+ nodeC.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 4;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "B,C,A") == 0);
+
+ /* close C */
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ output[0] = '\0';
+ max_cnt = 4;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,B") == 0);
+
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void test_priority(void)
+{
+ h2o_http2_scheduler_node_t root;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+
+ h2o_http2_scheduler_init(&root);
+ h2o_http2_scheduler_open(&nodeA.ref, &root, 32, 0);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_open(&nodeB.ref, &root, 32, 0);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ h2o_http2_scheduler_open(&nodeC.ref, &root, 12, 0);
+ h2o_http2_scheduler_activate(&nodeC.ref);
+
+ /* should only get the higher ones */
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,A,B,C,A,B,A,B,A,B,C,A,B,A,B,C,A,B,A") == 0);
+
+ /* eventually disactivate A */
+ nodeA.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 10;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "B,A,B,C,B,B,B,C,B,B") == 0);
+
+ /* should start serving C as B gets disactivated */
+ nodeB.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 10;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "C,B,C,C,C,C,C,C,C,C") == 0);
+
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void test_dependency(void)
+{
+ h2o_http2_scheduler_node_t root;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+ node_t nodeD = {{{NULL}}, "D", 1, 0};
+
+ /*
+ * root
+ * /|\
+ * A B C
+ * |
+ * D
+ */
+
+ h2o_http2_scheduler_init(&root);
+ h2o_http2_scheduler_open(&nodeA.ref, &root, 32, 0);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_open(&nodeB.ref, &root, 32, 0);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ h2o_http2_scheduler_open(&nodeC.ref, &root, 12, 0);
+ h2o_http2_scheduler_activate(&nodeC.ref);
+ h2o_http2_scheduler_open(&nodeD.ref, &nodeA.ref.node, 6, 0);
+ h2o_http2_scheduler_activate(&nodeD.ref);
+
+ /* should get A and B (and some C) */
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,A,B,C,A,B,A,B,A,B,C,A,B,A,B,C,A,B,A") == 0);
+
+ /* eventually disactivate A, should get D,B (and some C) */
+ nodeA.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "B,A,B,C,D,B,D,B,D,B,C,D,B,D,B,C,D,B,D,B") == 0);
+
+ /* eventually disactivate B, should get D only (and some C) */
+ nodeB.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "D,B,C,D,D,D,C,D,D,C,D,D,D,C,D,D,C,D,D,D") == 0);
+
+ /* closing A raises D, and the priority becomes D = B > C */
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ nodeB.still_is_active = 1;
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "D,C,B,D,B,D,B,C,D,B,D,B,D,C,B,D,B,D,B,C") == 0);
+
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_close(&nodeD.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void test_exclusive(void)
+{
+ h2o_http2_scheduler_node_t scheduler;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+
+ h2o_http2_scheduler_init(&scheduler);
+
+ /*
+ * root root
+ * /\ |
+ * A B => C
+ * |\
+ * A B
+ */
+
+ /* open A & B */
+ h2o_http2_scheduler_open(&nodeA.ref, &scheduler, 32, 0);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_open(&nodeB.ref, &scheduler, 32, 0);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&scheduler, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,A,B,A") == 0);
+
+ /* add C as an exclusive */
+ h2o_http2_scheduler_open(&nodeC.ref, &scheduler, 12, 1);
+
+ /* should get A & B since C is inactive */
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&scheduler, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,A,B,A") == 0); /* under current impl, moving the deps causes them to be ordered using _all_ref */
+
+ /* should see C once it is activated */
+ h2o_http2_scheduler_activate(&nodeC.ref);
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&scheduler, iterate_cb, NULL);
+ ok(strcmp(output, "C,C,C,C,C") == 0);
+
+ /* eventually disabling C should show A and B */
+ nodeC.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&scheduler, iterate_cb, NULL);
+ ok(strcmp(output, "C,B,A,B,A") == 0);
+
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_dispose(&scheduler);
+}
+
+static void test_firefox(void)
+{
+ /*
+ * firefox sends something like below
+ *
+ * PRIORITY: id:3, dependency:0, weight: 201
+ * PRIORITY: id:5, dependency:0, weight: 101
+ * PRIORITY: id:7, dependency:0, weight: 1
+ * PRIORITY: id:9, dependency:7, weight: 1
+ * PRIORITY: id:11, dependency:3, weight: 1
+ * HEADERS: id:13, dependency:11, weight: 22
+ * HEADERS: id:15, dependency:3, weight: 22
+ * HEADERS: id:17, dependency:3, weight: 22
+ */
+ h2o_http2_scheduler_node_t root;
+ node_t g1 = {{{NULL}}, "g1", 0, 0};
+ node_t g2 = {{{NULL}}, "g2", 0, 0};
+ node_t g3 = {{{NULL}}, "g3", 0, 0};
+ node_t g4 = {{{NULL}}, "g4", 0, 0};
+ node_t g5 = {{{NULL}}, "g5", 0, 0};
+ node_t r1 = {{{NULL}}, "r1", 1, 0};
+ node_t r2 = {{{NULL}}, "r2", 1, 0};
+ node_t r3 = {{{NULL}}, "r3", 1, 0};
+
+ h2o_http2_scheduler_init(&root);
+
+ /* setup the proirity groups */
+ h2o_http2_scheduler_open(&g1.ref, &root, 201, 0);
+ h2o_http2_scheduler_open(&g2.ref, &root, 101, 0);
+ h2o_http2_scheduler_open(&g3.ref, &root, 1, 0);
+ h2o_http2_scheduler_open(&g4.ref, &g3.ref.node, 1, 0);
+ h2o_http2_scheduler_open(&g5.ref, &g1.ref.node, 1, 0);
+
+ /* open r1 and set serving */
+ h2o_http2_scheduler_open(&r1.ref, &g5.ref.node, 22, 0);
+ h2o_http2_scheduler_activate(&r1.ref);
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "r1,r1,r1,r1,r1") == 0);
+
+ /* open r2,r3 and serve */
+ h2o_http2_scheduler_open(&r2.ref, &g1.ref.node, 22, 0);
+ h2o_http2_scheduler_activate(&r2.ref);
+ h2o_http2_scheduler_open(&r3.ref, &g1.ref.node, 22, 0);
+ h2o_http2_scheduler_activate(&r3.ref);
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "r2,r3,r2,r3,r2") == 0);
+
+ /* eventually disactive r2,r3 */
+ r2.still_is_active = 0;
+ r3.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "r3,r2,r1,r1,r1") == 0);
+
+ /* close r2,r3 */
+ h2o_http2_scheduler_close(&r2.ref);
+ h2o_http2_scheduler_close(&r3.ref);
+ output[0] = '\0';
+ max_cnt = 5;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "r1,r1,r1,r1,r1") == 0);
+
+ h2o_http2_scheduler_close(&r1.ref);
+
+ h2o_http2_scheduler_close(&g1.ref);
+ h2o_http2_scheduler_close(&g2.ref);
+ h2o_http2_scheduler_close(&g3.ref);
+ h2o_http2_scheduler_close(&g4.ref);
+ h2o_http2_scheduler_close(&g5.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void dump_tree(h2o_http2_scheduler_node_t *node)
+{
+ if (node->_parent != NULL) {
+ node_t *n = (void *)node;
+ strcat(output, n->name);
+ sprintf(output + strlen(output), "%u", (unsigned)h2o_http2_scheduler_get_weight(&n->ref));
+ }
+
+ if (!h2o_linklist_is_empty(&node->_all_refs)) {
+ unsigned weight;
+ int found_any = 0;
+ for (weight = 256; weight >= 1; --weight) {
+ h2o_linklist_t *link;
+ for (link = node->_all_refs.next; link != &node->_all_refs; link = link->next) {
+ h2o_http2_scheduler_openref_t *ref = H2O_STRUCT_FROM_MEMBER(h2o_http2_scheduler_openref_t, _all_link, link);
+ if (ref->weight == weight) {
+ if (!found_any) {
+ found_any = 1;
+ strcat(output, "(");
+ }
+ dump_tree(&ref->node);
+ }
+ }
+ }
+ if (found_any)
+ strcat(output, ")");
+ }
+}
+
+static int test_reprioritize_exclusive;
+
+static void test_reprioritize(void)
+{
+ /* from 5.3.3 of HTTP-2 draft 16
+ * ? ? ? ?
+ * | / \ | |
+ * A D A D D
+ * / \ / / \ / \ |
+ * B C ==> F B C ==> F A OR A
+ * / \ | / \ /|\
+ * D E E B C B C F
+ * | | |
+ * F E E
+ * (intermediate) (non-exclusive) (exclusive)
+ */
+ h2o_http2_scheduler_node_t root;
+ node_t a = {{{NULL}}, "A"};
+ node_t b = {{{NULL}}, "B"};
+ node_t c = {{{NULL}}, "C"};
+ node_t d = {{{NULL}}, "D"};
+ node_t e = {{{NULL}}, "E"};
+ node_t f = {{{NULL}}, "F"};
+
+ h2o_http2_scheduler_init(&root);
+ h2o_http2_scheduler_open(&a.ref, &root, 6, 0);
+ h2o_http2_scheduler_open(&b.ref, &a.ref.node, 5, 0);
+ h2o_http2_scheduler_open(&c.ref, &a.ref.node, 4, 0);
+ h2o_http2_scheduler_open(&d.ref, &c.ref.node, 3, 0);
+ h2o_http2_scheduler_open(&e.ref, &c.ref.node, 2, 0);
+ h2o_http2_scheduler_open(&f.ref, &d.ref.node, 1, 0);
+
+ output[0] = '\0';
+ dump_tree(&root);
+ ok(strcmp(output, "(A6(B5C4(D3(F1)E2)))") == 0);
+
+ h2o_http2_scheduler_rebind(&a.ref, &d.ref.node, 1, test_reprioritize_exclusive);
+ output[0] = '\0';
+ dump_tree(&root);
+ if (!test_reprioritize_exclusive) {
+ ok(strcmp(output, "(D3(F1A1(B5C4(E2))))") == 0);
+ } else {
+ ok(strcmp(output, "(D3(A1(B5C4(E2)F1)))") == 0);
+ }
+
+ h2o_http2_scheduler_close(&a.ref);
+ h2o_http2_scheduler_close(&b.ref);
+ h2o_http2_scheduler_close(&c.ref);
+ h2o_http2_scheduler_close(&d.ref);
+ h2o_http2_scheduler_close(&e.ref);
+ h2o_http2_scheduler_close(&f.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void test_change_weight(void)
+{
+ h2o_http2_scheduler_node_t root;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+
+ h2o_http2_scheduler_init(&root);
+
+ /* open them all with priority=16 */
+ h2o_http2_scheduler_open(&nodeA.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_open(&nodeB.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ h2o_http2_scheduler_open(&nodeC.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeC.ref);
+ output[0] = '\0';
+ dump_tree(&root);
+ ok(strcmp(output, "(A16B16C16)") == 0);
+
+ /* check the output */
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "A,B,C,A,B,C,A,B,C,A,B,C,A,B,C,A,B,C,A,B") == 0);
+
+ /* nodeA.priority = 4 */
+ h2o_http2_scheduler_rebind(&nodeA.ref, &root, 4, 0);
+ output[0] = '\0';
+ dump_tree(&root);
+ ok(strcmp(output, "(B16C16A4)") == 0);
+ output[0] = '\0';
+ max_cnt = 20;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "C,B,C,B,C,B,C,A,B,C,B,C,B,C,B,C,A,B,C,B") == 0);
+
+ /* eventually disactivate B,C */
+ nodeB.still_is_active = 0;
+ nodeC.still_is_active = 0;
+ output[0] = '\0';
+ max_cnt = 10;
+ h2o_http2_scheduler_run(&root, iterate_cb, NULL);
+ ok(strcmp(output, "C,B,A,A,A,A,A,A,A,A") == 0);
+
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+static void test_exclusive_at_current_pos(void)
+{
+ h2o_http2_scheduler_node_t root;
+ node_t nodeA = {{{NULL}}, "A", 1, 0};
+ node_t nodeB = {{{NULL}}, "B", 1, 0};
+ node_t nodeC = {{{NULL}}, "C", 1, 0};
+
+ h2o_http2_scheduler_init(&root);
+
+ /* open them all with priority=16 */
+ h2o_http2_scheduler_open(&nodeA.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeA.ref);
+ h2o_http2_scheduler_open(&nodeB.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeB.ref);
+ h2o_http2_scheduler_open(&nodeC.ref, &root, 16, 0);
+ h2o_http2_scheduler_activate(&nodeC.ref);
+
+ output[0] = '\0';
+ dump_tree(&root);
+ ok(strcmp(output, "(A16B16C16)") == 0);
+
+ h2o_http2_scheduler_rebind(&nodeB.ref, &root, 1, 1);
+
+ output[0] = '\0';
+ dump_tree(&root);
+ ok(strcmp(output, "(B1(A16C16))") == 0);
+
+ h2o_http2_scheduler_close(&nodeA.ref);
+ h2o_http2_scheduler_close(&nodeB.ref);
+ h2o_http2_scheduler_close(&nodeC.ref);
+ h2o_http2_scheduler_dispose(&root);
+}
+
+void test_lib__http2__scheduler(void)
+{
+ subtest("drr", test_queue);
+ subtest("round-robin", test_round_robin);
+ subtest("priority", test_priority);
+ subtest("dependency", test_dependency);
+ subtest("exclusive", test_exclusive);
+ subtest("firefox", test_firefox);
+ test_reprioritize_exclusive = 0;
+ subtest("repriortize-nonexclusive", test_reprioritize);
+ test_reprioritize_exclusive = 1;
+ subtest("repriortize-exclusive", test_reprioritize);
+ subtest("change-weight", test_change_weight);
+ subtest("exclusive-at-current-pos", test_exclusive_at_current_pos);
+}