diff options
Diffstat (limited to 'debian/vendor-h2o/examples/libh2o/latency-optimization.c')
-rw-r--r-- | debian/vendor-h2o/examples/libh2o/latency-optimization.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/debian/vendor-h2o/examples/libh2o/latency-optimization.c b/debian/vendor-h2o/examples/libh2o/latency-optimization.c new file mode 100644 index 0000000..de34bc9 --- /dev/null +++ b/debian/vendor-h2o/examples/libh2o/latency-optimization.c @@ -0,0 +1,381 @@ +/* + * 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 <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <netdb.h> +#include <netinet/in.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include "h2o/socket.h" +#include "h2o/string_.h" + +/* configuration */ +static char *host, *port; +static SSL_CTX *ssl_ctx; +static int mode_server, server_flag_received; +static h2o_socket_latency_optimization_conditions_t latopt_cond = {.min_rtt = 50, .max_additional_delay = 10, .max_cwnd = 65535}; +size_t write_block_size = 65536; + +/* globals */ +static h2o_loop_t *loop; +static h2o_socket_t *sock; +static struct { + uint64_t resp_start_at; + uint64_t sig_received_at; + uint64_t bytes_received; + uint64_t bytes_before_sig; +} client_stats; + +static void server_write(h2o_socket_t *sock); + +static h2o_iovec_t prepare_write_buf(void) +{ + static h2o_iovec_t buf; + if (buf.base == NULL) { + buf.base = h2o_mem_alloc(write_block_size); + buf.len = write_block_size; + memset(buf.base, '0', buf.len); + } + return buf; +} + +static void server_on_write_ready(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + fprintf(stderr, "socket unexpected closed by peer:%s\n", err); + exit(1); + return; + } + server_write(sock); +} + +static void server_on_write_complete(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + fprintf(stderr, "write failed:%s\n", err); + exit(1); + return; + } + h2o_socket_notify_write(sock, server_on_write_ready); +} + +void server_write(h2o_socket_t *sock) +{ + size_t sz = h2o_socket_prepare_for_latency_optimized_write(sock, &latopt_cond); + h2o_iovec_t buf = prepare_write_buf(); + + if (server_flag_received) + buf.base[0] = '1'; + if (sz < buf.len) + buf.len = sz; + + fprintf(stderr, "writing %zu bytes\n", buf.len); + h2o_socket_write(sock, &buf, 1, server_on_write_complete); +} + +static void server_on_read_second(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + fprintf(stderr, "connection closed unexpectedly:%s\n", err); + exit(1); + return; + } + + fprintf(stderr, "received the flag\n"); + server_flag_received = 1; +} + +static void server_on_read_first(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + fprintf(stderr, "connection closed unexpectedly:%s\n", err); + exit(1); + return; + } + + server_write(sock); + h2o_socket_read_start(sock, server_on_read_second); +} + +static void client_on_write_complete(h2o_socket_t *sock, const char *err) +{ + if (err == NULL) + return; + /* handle error */ + fprintf(stderr, "write failed:%s\n", err); + h2o_socket_close(sock); + exit(1); +} + +static void client_on_read_second(h2o_socket_t *sock, const char *err) +{ + size_t i; + + if (err != NULL) { + fprintf(stderr, "connection closed unexpectedly:%s\n", err); + exit(1); + return; + } + + if (client_stats.sig_received_at == 0) { + for (i = 0; i != sock->input->size; ++i) { + if (sock->input->bytes[i] != '0') { + client_stats.sig_received_at = h2o_now(h2o_socket_get_loop(sock)); + break; + } + ++client_stats.bytes_before_sig; + } + } + client_stats.bytes_received += sock->input->size; + h2o_buffer_consume(&sock->input, sock->input->size); + + if (client_stats.bytes_received >= 1024 * 1024) { + uint64_t now = h2o_now(h2o_socket_get_loop(sock)); + printf("Delay: %" PRIu64 " octets, %" PRIu64 " ms\n", client_stats.bytes_before_sig, + client_stats.sig_received_at - client_stats.resp_start_at); + printf("Total: %" PRIu64 " octets, %" PRIu64 " ms\n", client_stats.bytes_received, now - client_stats.resp_start_at); + exit(0); + } +} + +static void client_on_read_first(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + fprintf(stderr, "connection closed unexpectedly:%s\n", err); + exit(1); + return; + } + + client_stats.resp_start_at = h2o_now(h2o_socket_get_loop(sock)); + client_stats.bytes_before_sig = sock->input->size; + client_stats.bytes_received = sock->input->size; + h2o_buffer_consume(&sock->input, sock->input->size); + + h2o_iovec_t data = {H2O_STRLIT("!")}; + h2o_socket_write(sock, &data, 1, client_on_write_complete); + h2o_socket_read_start(sock, client_on_read_second); +} + +static void on_handshake_complete(h2o_socket_t *sock, const char *err) +{ + if (err != NULL && err != h2o_socket_error_ssl_cert_name_mismatch) { + /* TLS handshake failed */ + fprintf(stderr, "TLS handshake failure:%s\n", err); + ERR_print_errors_fp(stderr); + h2o_socket_close(sock); + exit(1); + return; + } + + if (mode_server) { + h2o_socket_read_start(sock, server_on_read_first); + } else { + h2o_iovec_t buf = {H2O_STRLIT("0")}; + h2o_socket_write(sock, &buf, 1, client_on_write_complete); + h2o_socket_read_start(sock, client_on_read_first); + } +} + +static void on_connect(h2o_socket_t *sock, const char *err) +{ + if (err != NULL) { + /* connection failed */ + fprintf(stderr, "failed to connect to host:%s\n", err); + h2o_socket_close(sock); + exit(1); + return; + } + + if (ssl_ctx != NULL) { + h2o_socket_ssl_handshake(sock, ssl_ctx, mode_server ? NULL : "blahblah", on_handshake_complete); + } else { + on_handshake_complete(sock, NULL); + } +} + +static void on_accept(h2o_socket_t *listener, const char *err) +{ + if (err != NULL) + return; + + if ((sock = h2o_evloop_socket_accept(listener)) != NULL) { + h2o_socket_close(listener); + if (ssl_ctx != NULL) { + h2o_socket_ssl_handshake(sock, ssl_ctx, mode_server ? NULL : "blahblah", on_handshake_complete); + } else { + on_handshake_complete(sock, NULL); + } + } +} + +static void usage(const char *cmd) +{ + fprintf(stderr, "Usage: %s [opts] [<host>:]<port>\n" + "Options: --listen if set, waits for incoming connection. Otherwise,\n" + " connects to the server running at given address\n" + " --reverse-role if set, reverses the role bet. server and the\n" + " client once the connection is established\n" + " --tls use TLS\n" + " --block-size=octets default write block size\n" + " --min-rtt=ms minimum RTT to enable latency optimization\n" + " --max-cwnd=octets maximum size of CWND to enable latency\n" + " optimization\n", + cmd); + exit(1); +} + +int main(int argc, char **argv) +{ + static const struct option longopts[] = {{"listen", no_argument, NULL, 'l'}, + {"reverse-role", no_argument, NULL, 'r'}, + {"tls", no_argument, NULL, 't'}, + {"block-size", no_argument, NULL, 'b'}, + {"min-rtt", required_argument, NULL, 'R'}, + {"max-cwnd", required_argument, NULL, 'c'}, + {}}; + int opt_ch, mode_listen = 0, mode_reverse_role = 0, mode_tls = 0; + struct addrinfo hints, *res = NULL; + int err; + + while ((opt_ch = getopt_long(argc, argv, "lrtb:R:c:", longopts, NULL)) != -1) { + switch (opt_ch) { + case 'l': + mode_listen = 1; + break; + case 'r': + mode_reverse_role = 1; + break; + case 't': + mode_tls = 1; + break; + case 'b': + if (sscanf(optarg, "%zu", &write_block_size) != 1) { + fprintf(stderr, "write block size (-b) must be a non-negative number of octets\n"); + exit(1); + } + break; + case 'R': + if (sscanf(optarg, "%u", &latopt_cond.min_rtt) != 1) { + fprintf(stderr, "min RTT (-m) must be a non-negative number in milliseconds\n"); + exit(1); + } + break; + case 'c': + if (sscanf(optarg, "%u", &latopt_cond.max_cwnd) != 1) { + fprintf(stderr, "max CWND size must be a non-negative number of octets\n"); + exit(1); + } + break; + default: + usage(argv[0]); + break; + } + } + mode_server = mode_listen; + if (mode_reverse_role) + mode_server = !mode_server; + + if (argc == optind) { + usage(argv[0]); + } else { + char *hostport = argv[optind], *colon; + if ((colon = strchr(hostport, ':')) != NULL) { + hostport = argv[optind]; + host = strdup(hostport); + host[colon - hostport] = '\0'; + port = colon + 1; + } else { + host = "0.0.0.0"; + port = argv[optind]; + } + } + + if (mode_tls) { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + if (mode_server) { + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + SSL_CTX_use_certificate_file(ssl_ctx, "examples/h2o/server.crt", SSL_FILETYPE_PEM); + SSL_CTX_use_PrivateKey_file(ssl_ctx, "examples/h2o/server.key", SSL_FILETYPE_PEM); + } else { + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + } + int nid = NID_X9_62_prime256v1; + EC_KEY *key = EC_KEY_new_by_curve_name(nid); + if (key == NULL) { + fprintf(stderr, "Failed to create curve \"%s\"\n", OBJ_nid2sn(nid)); + exit(1); + } + SSL_CTX_set_tmp_ecdh(ssl_ctx, key); + EC_KEY_free(key); + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_cipher_list(ssl_ctx, "ECDHE-RSA-AES128-GCM-SHA256"); + } + +#if H2O_USE_LIBUV + loop = uv_loop_new(); +#else + loop = h2o_evloop_create(); +#endif + + /* resolve host:port (FIXME use the function supplied by the loop) */ + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; + if ((err = getaddrinfo(host, port, &hints, &res)) != 0) { + fprintf(stderr, "failed to resolve %s:%s:%s\n", host, port, gai_strerror(err)); + exit(1); + } + + if (mode_listen) { + int fd, reuseaddr_flag = 1; + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 || + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) != 0 || + bind(fd, res->ai_addr, res->ai_addrlen) != 0 || listen(fd, SOMAXCONN) != 0) { + fprintf(stderr, "failed to listen to %s:%s:%s\n", host, port, strerror(errno)); + exit(1); + } + h2o_socket_t *listen_sock = h2o_evloop_socket_create(loop, fd, H2O_SOCKET_FLAG_DONT_READ); + h2o_socket_read_start(listen_sock, on_accept); + } else { + if ((sock = h2o_socket_connect(loop, res->ai_addr, res->ai_addrlen, on_connect)) == NULL) { + fprintf(stderr, "failed to create socket:%s\n", strerror(errno)); + exit(1); + } + } + + while (1) { +#if H2O_USE_LIBUV + uv_run(loop, UV_RUN_DEFAULT); +#else + h2o_evloop_run(loop, INT32_MAX); +#endif + } + + return 0; +} |