From 94931784fa1f8eba22d5504174d8f05732fe0ff2 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Sat, 24 Jun 2017 12:20:52 +0900
Subject: [PATCH] Add client/server

---
 Makefile.am          |   5 +-
 configure.ac         |  31 +++
 examples/.gitignore  |   2 +
 examples/Makefile.am |  41 +++
 examples/client.cc   | 490 ++++++++++++++++++++++++++++++++++
 examples/client.h    |  73 ++++++
 examples/network.h   |  61 +++++
 examples/server.cc   | 611 +++++++++++++++++++++++++++++++++++++++++++
 examples/server.h    |  88 +++++++
 examples/template.h  |  50 ++++
 lib/Makefile.am      |   2 +-
 lib/ngtcp2_conn.c    |  45 ++--
 lib/ngtcp2_conn.h    |   3 +-
 lib/ngtcp2_pkt.h     |   5 +
 14 files changed, 1485 insertions(+), 22 deletions(-)
 create mode 100644 examples/.gitignore
 create mode 100644 examples/Makefile.am
 create mode 100644 examples/client.cc
 create mode 100644 examples/client.h
 create mode 100644 examples/network.h
 create mode 100644 examples/server.cc
 create mode 100644 examples/server.h
 create mode 100644 examples/template.h

diff --git a/Makefile.am b/Makefile.am
index 9c9fd6c1..f697f6dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,7 @@
 # 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.
-SUBDIRS = lib tests
+SUBDIRS = lib tests examples
 
 ACLOCAL_AMFLAGS = -I m4
 
@@ -30,4 +30,5 @@ ACLOCAL_AMFLAGS = -I m4
 clang-format:
 	CLANGFORMAT=`git config --get clangformat.binary`; \
 	test -z $${CLANGFORMAT} && CLANGFORMAT="clang-format"; \
-	$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/ngtcp2/*.h
+	$${CLANGFORMAT} -i lib/*.{c,h} lib/includes/ngtcp2/*.h \
+	examples/*.{cc,h}
diff --git a/configure.ac b/configure.ac
index 5cf28e75..17523c15 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,8 @@ AC_ARG_ENABLE([debug],
 
 # Checks for programs
 AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
 AC_PROG_INSTALL
 AC_PROG_LN_S
 AC_PROG_MAKE_SET
@@ -74,6 +76,8 @@ AC_PROG_MKDIR_P
 
 PKG_PROG_PKG_CONFIG([0.20])
 
+AX_CXX_COMPILE_STDCXX([14], [noext], [optional])
+
 # Checks for libraries.
 
 # cunit
@@ -105,6 +109,29 @@ fi
 
 AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
 
+# openssl (for examples)
+PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.1.1],
+                  [have_openssl=yes], [have_openssl=no])
+if test "x${have_openssl}" = "xno"; then
+  AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)
+fi
+
+# libev (for examples)
+# libev does not have pkg-config file.  Check it in an old way.
+save_LIBS=$LIBS
+# android requires -lm for floor
+AC_CHECK_LIB([ev], [ev_time], [have_libev=yes], [have_libev=no], [-lm])
+if test "x${have_libev}" = "xyes"; then
+  AC_CHECK_HEADER([ev.h], [have_libev=yes], [have_libev=no])
+  if test "x${have_libev}" = "xyes"; then
+    LIBEV_LIBS=-lev
+    LIBEV_CFLAGS=
+    AC_SUBST([LIBEV_LIBS])
+    AC_SUBST([LIBEV_CFLAGS])
+  fi
+fi
+LIBS=$save_LIBS
+
 # Checks for header files.
 AC_CHECK_HEADERS([ \
   arpa/inet.h \
@@ -222,6 +249,7 @@ AC_CONFIG_FILES([
   lib/includes/Makefile
   lib/includes/ngtcp2/version.h
   tests/Makefile
+  examples/Makefile
 ])
 AC_OUTPUT
 
@@ -250,4 +278,7 @@ AC_MSG_NOTICE([summary of build options:
       CUnit:          ${have_cunit} (CFLAGS='${CUNIT_CFLAGS}' LIBS='${CUNIT_LIBS}')
     Debug:
       Debug:          ${debug}
+    Libs:
+      OpenSSL:        ${have_openssl} (CFLAGS='${OPENSSL_CFLAGS}' LIBS='${OPENSSL_LIBS}')
+      Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
 ])
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 00000000..f2ad8530
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,2 @@
+client
+server
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 00000000..43123e8f
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,41 @@
+# ngtcp2
+
+# Copyright (c) 2017 ngtcp2 contributors
+
+# 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.
+
+AM_CFLAGS = $(WARNCFLAGS)
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/lib/includes \
+	-I$(top_builddir)/lib/includes \
+	@OPENSSL_CFLAGS@ \
+	@LIBEV_CFLAGS@ \
+	@DEFS@
+LDADD = $(top_builddir)/lib/libngtcp2.la \
+	@OPENSSL_LIBS@ \
+	@LIBEV_LIBS@
+
+noinst_PROGRAMS = client server
+
+client_SOURCES = client.cc \
+	template.h
+
+server_SOURCES = server.cc \
+	template.h
diff --git a/examples/client.cc b/examples/client.cc
new file mode 100644
index 00000000..08a6e32e
--- /dev/null
+++ b/examples/client.cc
@@ -0,0 +1,490 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * 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 <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <algorithm>
+
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <openssl/bio.h>
+
+#include "client.h"
+#include "template.h"
+#include "network.h"
+
+using namespace ngtcp2;
+
+namespace {
+void *BIO_get_data(BIO *bio) { return bio->ptr; }
+void BIO_set_data(BIO *bio, void *ptr) { bio->ptr = ptr; }
+void BIO_set_init(BIO *bio, int init) { bio->init = init; }
+} // namespace
+
+namespace {
+int bio_write(BIO *b, const char *buf, int len) {
+  BIO_clear_retry_flags(b);
+
+  auto c = static_cast<Client *>(BIO_get_data(b));
+
+  c->write_client_handshake(reinterpret_cast<const uint8_t *>(buf), len);
+
+  std::cerr << "ClientHello: " << len << " bytes" << std::endl;
+  ;
+  return len;
+}
+} // namespace
+
+namespace {
+int bio_read(BIO *b, char *buf, int len) {
+  BIO_clear_retry_flags(b);
+
+  auto c = static_cast<Client *>(BIO_get_data(b));
+
+  len = c->read_server_handshake(reinterpret_cast<uint8_t *>(buf), len);
+  if (len == 0) {
+    BIO_set_retry_read(b);
+    return -1;
+  }
+
+  std::cerr << "ServerHello: " << len << " bytes" << std::endl;
+
+  return len;
+}
+} // namespace
+
+namespace {
+int bio_puts(BIO *b, const char *str) { return bio_write(b, str, strlen(str)); }
+} // namespace
+
+namespace {
+int bio_gets(BIO *b, char *buf, int len) { return -1; }
+} // namespace
+
+namespace {
+long bio_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  switch (cmd) {
+  case BIO_CTRL_FLUSH:
+    return 1;
+  }
+
+  return 0;
+}
+} // namespace
+
+namespace {
+int bio_create(BIO *b) {
+  BIO_set_init(b, 1);
+  return 1;
+}
+} // namespace
+
+namespace {
+int bio_destroy(BIO *b) {
+  if (b == nullptr) {
+    return 0;
+  }
+
+  return 1;
+}
+} // namespace
+
+namespace {
+BIO_METHOD *create_bio_method() {
+  static auto meth = new BIO_METHOD{
+      BIO_TYPE_FD, "bio",    bio_write,  bio_read,    bio_puts,
+      bio_gets,    bio_ctrl, bio_create, bio_destroy,
+  };
+
+  return meth;
+}
+} // namespace
+
+namespace {
+void writecb(struct ev_loop *loop, ev_io *w, int revents) {}
+} // namespace
+
+namespace {
+void readcb(struct ev_loop *loop, ev_io *w, int revents) {
+  auto c = static_cast<Client *>(w->data);
+
+  if (c->on_read() != 0) {
+    c->disconnect();
+  }
+}
+} // namespace
+
+Client::Client(struct ev_loop *loop, SSL_CTX *ssl_ctx)
+    : loop_(loop),
+      ssl_ctx_(ssl_ctx),
+      ssl_(nullptr),
+      fd_(-1),
+      ncread_(0),
+      nsread_(0),
+      conn_(nullptr) {
+  ev_io_init(&wev_, writecb, 0, EV_WRITE);
+  ev_io_init(&rev_, readcb, 0, EV_READ);
+  wev_.data = this;
+  rev_.data = this;
+}
+
+Client::~Client() {
+  disconnect();
+}
+
+void Client::disconnect() {
+  std::cerr << "disconnecting" << std::endl;
+
+  ev_io_stop(loop_, &rev_);
+  ev_io_stop(loop_, &wev_);
+
+  if (conn_) {
+    ngtcp2_conn_del(conn_);
+    conn_ = nullptr;
+  }
+
+  if (ssl_) {
+    SSL_free(ssl_);
+    ssl_ = nullptr;
+  }
+
+  if (fd_ != -1) {
+    close(fd_);
+    fd_ = -1;
+  }
+}
+
+namespace {
+ssize_t send_client_initial(ngtcp2_conn *conn, uint32_t flags,
+                            uint64_t *ppkt_num, const uint8_t **pdest,
+                            size_t maxdestlen, void *user_data) {
+  auto c = static_cast<Client *>(user_data);
+
+  if (c->tls_handshake() != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  *ppkt_num = 1;
+
+  auto len = c->read_client_handshake(pdest, maxdestlen);
+
+  std::cerr << "Client Initial: " << len << " bytes" << std::endl;
+
+  return len;
+}
+} // namespace
+
+namespace {
+ssize_t send_client_cleartext(ngtcp2_conn *conn, uint32_t flags,
+                              const uint8_t **pdest, size_t maxdestlen,
+                              void *user_data) {
+  auto c = static_cast<Client *>(user_data);
+
+  if (c->tls_handshake() != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  auto len = c->read_client_handshake(pdest, maxdestlen);
+
+  std::cerr << "Client Cleartext: " << len << " bytes" << std::endl;
+
+  return len;
+}
+} // namespace
+
+namespace {
+int recv_handshake_data(ngtcp2_conn *conn, const uint8_t *data, size_t datalen,
+                        void *user_data) {
+  auto c = static_cast<Client *>(user_data);
+
+  c->write_server_handshake(data, datalen);
+
+  if (c->tls_handshake() != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+} // namespace
+
+int Client::init(int fd) {
+  int rv;
+
+  fd_ = fd;
+  ssl_ = SSL_new(ssl_ctx_);
+  auto bio = BIO_new(create_bio_method());
+  BIO_set_data(bio, this);
+  SSL_set_bio(ssl_, bio, bio);
+  SSL_set_app_data(ssl_, this);
+  SSL_set_connect_state(ssl_);
+
+  auto callbacks = ngtcp2_conn_callbacks{
+      send_client_initial, send_client_cleartext, nullptr, recv_handshake_data,
+  };
+
+  rv = ngtcp2_conn_client_new(&conn_, 1, 1, &callbacks, this);
+  if (rv != 0) {
+    std::cerr << "ngtcp2_conn_client_new: " << rv << std::endl;
+    return -1;
+  }
+
+  ev_io_set(&wev_, fd_, EV_WRITE);
+  ev_io_set(&rev_, fd_, EV_READ);
+
+  ev_io_start(loop_, &rev_);
+
+  return 0;
+}
+
+int Client::tls_handshake() {
+  ERR_clear_error();
+
+  auto rv = SSL_do_handshake(ssl_);
+  if (rv <= 0) {
+    auto err = SSL_get_error(ssl_, rv);
+    switch (err) {
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      break;
+    case SSL_ERROR_SSL:
+      std::cerr << "TLS handshake error: "
+                << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+      return -1;
+    default:
+      std::cerr << "TLS handshake error: " << err << std::endl;
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+int Client::feed_data(const uint8_t *data, size_t datalen) {
+  int rv;
+
+  rv = ngtcp2_conn_recv(conn_, data, datalen);
+  if (rv != 0) {
+    std::cerr << "ngtcp2_conn_recv: " << rv << std::endl;
+    return -1;
+  }
+
+  return 0;
+}
+
+int Client::on_read() {
+  std::array<uint8_t, 65536> buf;
+
+  auto nread =
+      recvfrom(fd_, buf.data(), buf.size(), MSG_DONTWAIT, nullptr, nullptr);
+
+  if (nread == -1) {
+    std::cerr << "recvfrom: " << strerror(errno) << std::endl;
+    return 0;
+  }
+
+  std::cerr << "Read " << nread << " from socket " << fd_ << std::endl;
+
+  if (feed_data(buf.data(), nread) != 0) {
+    return -1;
+  }
+
+  return on_write();
+}
+
+int Client::on_write() {
+  std::array<uint8_t, 1280> buf;
+  auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size());
+  if (n < 0) {
+    return -1;
+  }
+  if (n == 0) {
+    return 0;
+  }
+
+  std::cerr << "Write " << n << " bytes of UDP payload" << std::endl;
+
+  auto nwrite = write(fd_, buf.data(), n);
+  if (nwrite == -1) {
+    std::cerr << "write: " << strerror(errno) << std::endl;
+    return -1;
+  }
+
+  std::cerr << "Wrote " << nwrite << " bytes" << std::endl;
+
+  return 0;
+}
+
+void Client::write_client_handshake(const uint8_t *data, size_t datalen) {
+  std::copy_n(data, datalen, std::back_inserter(chandshake_));
+}
+
+size_t Client::read_client_handshake(const uint8_t **pdest, size_t maxdestlen) {
+  auto n = std::min(maxdestlen, chandshake_.size() - ncread_);
+  *pdest = chandshake_.data() + ncread_;
+  ncread_ += n;
+  return n;
+}
+
+size_t Client::read_server_handshake(uint8_t *buf, size_t buflen) {
+  auto n = std::min(buflen, shandshake_.size() - nsread_);
+  std::copy_n(std::begin(shandshake_) + nsread_, n, buf);
+  nsread_ += n;
+  return n;
+}
+
+void Client::write_server_handshake(const uint8_t *data, size_t datalen) {
+  std::copy_n(data, datalen, std::back_inserter(shandshake_));
+}
+
+namespace {
+SSL_CTX *create_ssl_ctx() {
+  auto ssl_ctx = SSL_CTX_new(TLS_method());
+
+  SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+  SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+
+  SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+  return ssl_ctx;
+}
+} // namespace
+
+namespace {
+int create_sock(const char *addr, const char *port) {
+  addrinfo hints{};
+  addrinfo *res, *rp;
+  int rv;
+
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+
+  rv = getaddrinfo(addr, port, &hints, &res);
+  if (rv != 0) {
+    std::cerr << "getaddrinfo: " << gai_strerror(rv) << std::endl;
+    return -1;
+  }
+
+  auto res_d = defer(freeaddrinfo, res);
+
+  int fd = -1;
+
+  for (rp = res; rp; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if (fd == -1) {
+      continue;
+    }
+
+    if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
+      goto next;
+    }
+
+    break;
+
+  next:
+    close(fd);
+  }
+
+  if (!rp) {
+    std::cerr << "Could not connect" << std::endl;
+    return -1;
+  }
+
+  auto val = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
+                 static_cast<socklen_t>(sizeof(val))) == -1) {
+    return -1;
+  }
+
+  return fd;
+}
+
+} // namespace
+
+namespace {
+int run(Client &c, const char *addr, const char *port) {
+  int rv;
+
+  auto fd = create_sock(addr, port);
+  if (fd == -1) {
+    return -1;
+  }
+
+  if (c.init(fd) != 0) {
+    return -1;
+  }
+
+  c.on_write();
+
+  ev_run(EV_DEFAULT, 0);
+
+  return 0;
+}
+} // namespace
+
+namespace {
+void print_usage() { std::cerr << "Usage: client ADDR PORT" << std::endl; }
+} // namespace
+
+int main(int argc, char **argv) {
+  for (;;) {
+    static int flag = 0;
+    constexpr static option long_opts[] = {{nullptr, 0, nullptr, 0}};
+
+    auto optidx = 0;
+    auto c = getopt_long(argc, argv, "", long_opts, &optidx);
+    if (c == -1) {
+      break;
+    }
+    switch (c) {
+    case '?':
+      print_usage();
+      exit(EXIT_FAILURE);
+    default:
+      break;
+    };
+  }
+
+  if (argc - optind < 2) {
+    std::cerr << "Too few arguments" << std::endl;
+    print_usage();
+    exit(EXIT_FAILURE);
+  }
+
+  auto addr = argv[optind++];
+  auto port = argv[optind++];
+
+  auto ssl_ctx = create_ssl_ctx();
+  auto ssl_ctx_d = defer(SSL_CTX_free, ssl_ctx);
+
+  Client c(EV_DEFAULT, ssl_ctx);
+
+  if (run(c, addr, port) != 0) {
+    exit(EXIT_FAILURE);
+  }
+}
diff --git a/examples/client.h b/examples/client.h
new file mode 100644
index 00000000..d8d4843f
--- /dev/null
+++ b/examples/client.h
@@ -0,0 +1,73 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * 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.
+ */
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <vector>
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+
+#include <ev.h>
+
+class Client {
+public:
+  Client(struct ev_loop *loop, SSL_CTX *ssl_ctx);
+  ~Client();
+
+  int init(int fd);
+  void disconnect();
+
+  int tls_handshake();
+  int on_read();
+  int on_write();
+  int feed_data(const uint8_t *data, size_t datalen);
+
+  void write_client_handshake(const uint8_t *data, size_t datalen);
+  size_t read_client_handshake(const uint8_t **pdest, size_t maxdestlen);
+
+  size_t read_server_handshake(uint8_t *buf, size_t buflen);
+  void write_server_handshake(const uint8_t *data, size_t datalen);
+
+private:
+  ev_io wev_;
+  ev_io rev_;
+  struct ev_loop *loop_;
+  SSL_CTX *ssl_ctx_;
+  SSL *ssl_;
+  int fd_;
+  std::vector<uint8_t> chandshake_;
+  size_t ncread_;
+  std::vector<uint8_t> shandshake_;
+  size_t nsread_;
+  ngtcp2_conn *conn_;
+};
+
+#endif // CLIENT_H
diff --git a/examples/network.h b/examples/network.h
new file mode 100644
index 00000000..4e9a44de
--- /dev/null
+++ b/examples/network.h
@@ -0,0 +1,61 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2016 nghttp2 contributors
+ *
+ * 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.
+ */
+#ifndef NETWORK_H
+#define NETWORK_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif // HAVE_SYS_SOCKET_H
+#include <sys/un.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif // HAVE_NETINET_IN_H
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif // HAVE_ARPA_INET_H
+
+namespace ngtcp2 {
+
+union sockaddr_union {
+  sockaddr_storage storage;
+  sockaddr sa;
+  sockaddr_in6 in6;
+  sockaddr_in in;
+};
+
+struct Address {
+  size_t len;
+  union sockaddr_union su;
+};
+
+} // namespace ngtcp2
+
+#endif // NETWORK_H
diff --git a/examples/server.cc b/examples/server.cc
new file mode 100644
index 00000000..110db1d3
--- /dev/null
+++ b/examples/server.cc
@@ -0,0 +1,611 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * 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 <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <algorithm>
+
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <openssl/bio.h>
+
+#include "server.h"
+#include "template.h"
+#include "network.h"
+
+using namespace ngtcp2;
+
+namespace {
+void *BIO_get_data(BIO *bio) { return bio->ptr; }
+void BIO_set_data(BIO *bio, void *ptr) { bio->ptr = ptr; }
+void BIO_set_init(BIO *bio, int init) { bio->init = init; }
+} // namespace
+
+namespace {
+int bio_write(BIO *b, const char *buf, int len) {
+  BIO_clear_retry_flags(b);
+
+  auto h = static_cast<Handler *>(BIO_get_data(b));
+
+  h->write_server_handshake(reinterpret_cast<const uint8_t *>(buf), len);
+
+  std::cerr << "ServerHello: " << len << " bytes" << std::endl;
+  ;
+  return len;
+}
+} // namespace
+
+namespace {
+int bio_read(BIO *b, char *buf, int len) {
+  BIO_clear_retry_flags(b);
+
+  auto h = static_cast<Handler *>(BIO_get_data(b));
+
+  len = h->read_client_handshake(reinterpret_cast<uint8_t *>(buf), len);
+  if (len == 0) {
+    BIO_set_retry_read(b);
+    return -1;
+  }
+
+  std::cerr << "ClientHello: " << len << " bytes" << std::endl;
+  ;
+
+  return len;
+}
+} // namespace
+
+namespace {
+int bio_puts(BIO *b, const char *str) { return bio_write(b, str, strlen(str)); }
+} // namespace
+
+namespace {
+int bio_gets(BIO *b, char *buf, int len) { return -1; }
+} // namespace
+
+namespace {
+long bio_ctrl(BIO *b, int cmd, long num, void *ptr) {
+  switch (cmd) {
+  case BIO_CTRL_FLUSH:
+    return 1;
+  }
+
+  return 0;
+}
+} // namespace
+
+namespace {
+int bio_create(BIO *b) {
+  BIO_set_init(b, 1);
+  return 1;
+}
+} // namespace
+
+namespace {
+int bio_destroy(BIO *b) {
+  if (b == nullptr) {
+    return 0;
+  }
+
+  return 1;
+}
+} // namespace
+
+namespace {
+BIO_METHOD *create_bio_method() {
+  static auto meth = new BIO_METHOD{
+      BIO_TYPE_FD, "bio",    bio_write,  bio_read,    bio_puts,
+      bio_gets,    bio_ctrl, bio_create, bio_destroy,
+  };
+
+  return meth;
+}
+} // namespace
+
+namespace {
+void hwritecb(struct ev_loop *loop, ev_io *w, int revents) {
+  auto h = static_cast<Handler *>(w->data);
+
+  if (h->on_write() != 0) {
+    delete h;
+  }
+}
+} // namespace
+
+namespace {
+void hreadcb(struct ev_loop *loop, ev_io *w, int revents) {
+  auto h = static_cast<Handler *>(w->data);
+
+  if (h->on_read() != 0) {
+    delete h;
+  }
+}
+} // namespace
+
+Handler::Handler(struct ev_loop *loop, SSL_CTX *ssl_ctx)
+    : loop_(loop),
+      ssl_ctx_(ssl_ctx),
+      ssl_(nullptr),
+      fd_(-1),
+      ncread_(0),
+      nsread_(0),
+      conn_(nullptr) {
+  ev_io_init(&wev_, hwritecb, 0, EV_WRITE);
+  ev_io_init(&rev_, hreadcb, 0, EV_READ);
+  wev_.data = this;
+  rev_.data = this;
+}
+
+Handler::~Handler() {
+  ev_io_stop(loop_, &rev_);
+  ev_io_stop(loop_, &wev_);
+
+  if (conn_) {
+    ngtcp2_conn_del(conn_);
+  }
+
+  if (ssl_) {
+    SSL_free(ssl_);
+  }
+
+  if (fd_ != -1) {
+    close(fd_);
+  }
+  assert(0);
+}
+
+namespace {
+ssize_t send_server_cleartext(ngtcp2_conn *conn, uint32_t flags,
+                              uint64_t *ppkt_num, const uint8_t **pdest,
+                              size_t maxdestlen, void *user_data) {
+  auto h = static_cast<Handler *>(user_data);
+
+  if (h->tls_handshake() != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  if (ppkt_num) {
+    *ppkt_num = 1;
+  }
+
+  auto len = h->read_server_handshake(pdest, maxdestlen);
+
+  std::cerr << "Server Cleartext: " << len << " bytes" << std::endl;
+
+  return len;
+}
+} // namespace
+
+namespace {
+int recv_handshake_data(ngtcp2_conn *conn, const uint8_t *data, size_t datalen,
+                        void *user_data) {
+  auto h = static_cast<Handler *>(user_data);
+
+  h->write_client_handshake(data, datalen);
+
+  if (h->tls_handshake() != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+} // namespace
+
+int Handler::init(int fd) {
+  int rv;
+
+  fd_ = fd;
+  ssl_ = SSL_new(ssl_ctx_);
+  auto bio = BIO_new(create_bio_method());
+  BIO_set_data(bio, this);
+  SSL_set_bio(ssl_, bio, bio);
+  SSL_set_app_data(ssl_, this);
+  SSL_set_accept_state(ssl_);
+
+  auto callbacks = ngtcp2_conn_callbacks{
+      nullptr, nullptr, send_server_cleartext, recv_handshake_data,
+  };
+
+  rv = ngtcp2_conn_server_new(&conn_, 2, 1, &callbacks, this);
+  if (rv != 0) {
+    std::cerr << "ngtcp2_conn_server_new: " << rv << std::endl;
+    return -1;
+  }
+
+  ev_io_set(&wev_, fd_, EV_WRITE);
+  ev_io_set(&rev_, fd_, EV_READ);
+
+  ev_io_start(loop_, &rev_);
+
+  return 0;
+}
+
+int Handler::tls_handshake() {
+  ERR_clear_error();
+
+  auto rv = SSL_do_handshake(ssl_);
+  if (rv <= 0) {
+    auto err = SSL_get_error(ssl_, rv);
+    switch (err) {
+    case SSL_ERROR_WANT_READ:
+    case SSL_ERROR_WANT_WRITE:
+      break;
+    case SSL_ERROR_SSL:
+      std::cerr << "TLS handshake error: "
+                << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+      return -1;
+    default:
+      std::cerr << "TLS handshake error: " << err << std::endl;
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+void Handler::write_server_handshake(const uint8_t *data, size_t datalen) {
+  std::copy_n(data, datalen, std::back_inserter(chandshake_));
+}
+
+size_t Handler::read_server_handshake(const uint8_t **pdest,
+                                      size_t maxdestlen) {
+  auto n = std::min(maxdestlen, chandshake_.size() - ncread_);
+  *pdest = chandshake_.data() + ncread_;
+  ncread_ += n;
+  return n;
+}
+
+size_t Handler::read_client_handshake(uint8_t *buf, size_t buflen) {
+  auto n = std::min(buflen, shandshake_.size() - nsread_);
+  std::copy_n(std::begin(shandshake_) + nsread_, n, buf);
+  nsread_ += n;
+  return n;
+}
+
+void Handler::write_client_handshake(const uint8_t *data, size_t datalen) {
+  std::copy_n(data, datalen, std::back_inserter(shandshake_));
+}
+
+int Handler::feed_data(const uint8_t *data, size_t datalen) {
+  int rv;
+
+  rv = ngtcp2_conn_recv(conn_, data, datalen);
+  if (rv != 0) {
+    std::cerr << "ngtcp2_conn_recv: " << rv << std::endl;
+    return -1;
+  }
+
+  return 0;
+}
+
+int Handler::on_read() {
+  sockaddr_union su;
+  socklen_t addrlen = sizeof(su);
+  std::array<uint8_t, 1280> buf;
+
+  auto nread =
+      recvfrom(fd_, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+  if (nread == -1) {
+    std::cerr << "recvfrom: " << strerror(errno) << std::endl;
+    return 0;
+  }
+
+  std::cerr << "Read " << nread << " from socket " << fd_ << std::endl;
+
+  if (feed_data(buf.data(), nread) != 0) {
+    return -1;
+  }
+
+  return on_write();
+}
+
+int Handler::on_write() {
+  std::array<uint8_t, 1280> buf;
+  std::cerr << "on_write" << std::endl;
+
+  for (;;) {
+    auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size());
+    if (n < 0) {
+      return -1;
+    }
+    if (n == 0) {
+      return 0;
+    }
+
+    std::cerr << "Write " << n << " bytes of UDP payload" << std::endl;
+
+    auto nwrite = write(fd_, buf.data(), n);
+    if (nwrite == -1) {
+      std::cerr << "write: " << strerror(errno) << std::endl;
+      return -1;
+    }
+
+    std::cerr << "Wrote " << nwrite << " bytes" << std::endl;
+  }
+}
+
+void Handler::signal_write() { ev_feed_event(loop_, &wev_, EV_WRITE); }
+
+namespace {
+void swritecb(struct ev_loop *loop, ev_io *w, int revents) {}
+} // namespace
+
+namespace {
+void sreadcb(struct ev_loop *loop, ev_io *w, int revents) {
+  auto s = static_cast<Server *>(w->data);
+
+  s->on_read();
+}
+} // namespace
+
+Server::Server(struct ev_loop *loop, SSL_CTX *ssl_ctx)
+    : loop_(loop), ssl_ctx_(ssl_ctx), fd_(-1) {
+  ev_io_init(&wev_, swritecb, 0, EV_WRITE);
+  ev_io_init(&rev_, sreadcb, 0, EV_READ);
+  wev_.data = this;
+  rev_.data = this;
+}
+
+Server::~Server() {
+  ev_io_stop(loop_, &rev_);
+  ev_io_stop(loop_, &wev_);
+
+  if (fd_ != -1) {
+    close(fd_);
+  }
+}
+
+int Server::init(int fd) {
+  fd_ = fd;
+
+  ev_io_set(&wev_, fd_, EV_WRITE);
+  ev_io_set(&rev_, fd_, EV_READ);
+
+  ev_io_start(loop_, &rev_);
+
+  return 0;
+}
+
+int Server::on_read() {
+  sockaddr_union su;
+  socklen_t addrlen = sizeof(su);
+  std::array<uint8_t, 1280> buf;
+
+  auto nread =
+      recvfrom(fd_, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+  if (nread == -1) {
+    std::cerr << "recvfrom: " << strerror(errno) << std::endl;
+    // TODO Handle running out of fd
+    return 0;
+  }
+
+  auto fd = socket(su.storage.ss_family, SOCK_DGRAM, 0);
+  if (fd == -1) {
+    std::cerr << "socket: " << strerror(errno) << std::endl;
+    return 0;
+  }
+
+  auto val = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
+                 static_cast<socklen_t>(sizeof(val))) == -1) {
+    close(fd);
+    return 0;
+  }
+
+  {
+    sockaddr_union su;
+    socklen_t addrlen = sizeof(su);
+
+    if (getsockname(fd_, &su.sa, &addrlen) == -1) {
+      std::cerr << "getsockname: " << strerror(errno) << std::endl;
+    }
+
+    if (bind(fd, &su.sa, addrlen) == -1) {
+      std::cerr << "bind: " << strerror(errno) << std::endl;
+    }
+  }
+
+  if (connect(fd, &su.sa, addrlen) == -1) {
+    std::cerr << "connect: " << strerror(errno) << std::endl;
+    close(fd);
+    return 0;
+  }
+
+  auto h = std::make_unique<Handler>(loop_, ssl_ctx_);
+  h->init(fd);
+  if (h->feed_data(buf.data(), nread) != 0) {
+    return 0;
+  }
+  h->signal_write();
+  h.release();
+
+  return 0;
+}
+
+namespace {
+SSL_CTX *create_ssl_ctx(const char *private_key_file, const char *cert_file) {
+  auto ssl_ctx = SSL_CTX_new(TLS_method());
+
+  constexpr auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
+                            SSL_OP_SINGLE_ECDH_USE |
+                            SSL_OP_CIPHER_SERVER_PREFERENCE;
+
+  SSL_CTX_set_options(ssl_ctx, ssl_opts);
+  SSL_CTX_set1_curves_list(ssl_ctx, "p-256");
+  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
+
+  SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+  SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+
+  SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+  if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file,
+                                  SSL_FILETYPE_PEM) != 1) {
+    std::cerr << "SSL_CTX_use_PrivateKey_file: "
+              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+    goto fail;
+  }
+
+  if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
+    std::cerr << "SSL_CTX_use_certificate_file: "
+              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+    goto fail;
+  }
+
+  if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
+    std::cerr << "SSL_CTX_check_private_key: "
+              << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
+    goto fail;
+  }
+
+  return ssl_ctx;
+
+fail:
+  SSL_CTX_free(ssl_ctx);
+  return nullptr;
+}
+} // namespace
+
+namespace {
+int create_sock(const char *addr, const char *port) {
+  addrinfo hints{};
+  addrinfo *res, *rp;
+  int rv;
+
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = AI_PASSIVE;
+
+  rv = getaddrinfo(addr, port, &hints, &res);
+  if (rv != 0) {
+    std::cerr << "getaddrinfo: " << gai_strerror(rv) << std::endl;
+    return -1;
+  }
+
+  auto res_d = defer(freeaddrinfo, res);
+
+  int fd = -1;
+
+  for (rp = res; rp; rp = rp->ai_next) {
+    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+    if (fd == -1) {
+      continue;
+    }
+
+    if (bind(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+      break;
+    }
+
+    close(fd);
+  }
+
+  if (!rp) {
+    std::cerr << "Could not bind" << std::endl;
+    return -1;
+  }
+
+  auto val = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
+                 static_cast<socklen_t>(sizeof(val))) == -1) {
+    return -1;
+  }
+
+  return fd;
+}
+
+} // namespace
+
+namespace {
+int serve(Server &s, const char *addr, const char *port) {
+  int rv;
+
+  auto fd = create_sock(addr, port);
+  if (fd == -1) {
+    return -1;
+  }
+
+  if (s.init(fd) != 0) {
+    return -1;
+  }
+
+  ev_run(EV_DEFAULT, 0);
+
+  return 0;
+}
+} // namespace
+
+namespace {
+void print_usage() {
+  std::cerr << "Usage: server ADDR PORT PRIVATE_KEY_FILE CERTIFICATE_FILE"
+            << std::endl;
+}
+} // namespace
+
+int main(int argc, char **argv) {
+  for (;;) {
+    static int flag = 0;
+    constexpr static option long_opts[] = {{nullptr, 0, nullptr, 0}};
+
+    auto optidx = 0;
+    auto c = getopt_long(argc, argv, "", long_opts, &optidx);
+    if (c == -1) {
+      break;
+    }
+    switch (c) {
+    case '?':
+      print_usage();
+      exit(EXIT_FAILURE);
+    default:
+      break;
+    };
+  }
+
+  if (argc - optind < 4) {
+    std::cerr << "Too few arguments" << std::endl;
+    print_usage();
+    exit(EXIT_FAILURE);
+  }
+
+  auto addr = argv[optind++];
+  auto port = argv[optind++];
+  auto private_key_file = argv[optind++];
+  auto cert_file = argv[optind++];
+
+  auto ssl_ctx = create_ssl_ctx(private_key_file, cert_file);
+  if (ssl_ctx == nullptr) {
+    exit(EXIT_FAILURE);
+  }
+
+  auto ssl_ctx_d = defer(SSL_CTX_free, ssl_ctx);
+
+  Server s(EV_DEFAULT, ssl_ctx);
+
+  if (serve(s, addr, port) != 0) {
+    exit(EXIT_FAILURE);
+  }
+}
diff --git a/examples/server.h b/examples/server.h
new file mode 100644
index 00000000..532555b1
--- /dev/null
+++ b/examples/server.h
@@ -0,0 +1,88 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * 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.
+ */
+#ifndef SERVER_H
+#define SERVER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif // HAVE_CONFIG_H
+
+#include <vector>
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+#include <ev.h>
+
+class Handler {
+public:
+  Handler(struct ev_loop *loop, SSL_CTX *ssl_ctx);
+  ~Handler();
+
+  int init(int fd);
+  int tls_handshake();
+  int on_read();
+  int on_write();
+  int feed_data(const uint8_t *data, size_t datalen);
+  void signal_write();
+
+  void write_server_handshake(const uint8_t *data, size_t datalen);
+  size_t read_server_handshake(const uint8_t **pdest, size_t maxdestlen);
+
+  size_t read_client_handshake(uint8_t *buf, size_t buflen);
+  void write_client_handshake(const uint8_t *data, size_t datalen);
+
+private:
+  struct ev_loop *loop_;
+  SSL_CTX *ssl_ctx_;
+  SSL *ssl_;
+  int fd_;
+  ev_io wev_;
+  ev_io rev_;
+  std::vector<uint8_t> chandshake_;
+  size_t ncread_;
+  std::vector<uint8_t> shandshake_;
+  size_t nsread_;
+  ngtcp2_conn *conn_;
+};
+
+class Server {
+public:
+  Server(struct ev_loop *loop, SSL_CTX *ssl_ctx);
+  ~Server();
+
+  int init(int fd);
+  int on_read();
+
+private:
+  struct ev_loop *loop_;
+  SSL_CTX *ssl_ctx_;
+  int fd_;
+  ev_io wev_;
+  ev_io rev_;
+};
+
+
+#endif // SERVER_H
diff --git a/examples/template.h b/examples/template.h
new file mode 100644
index 00000000..39f6ef09
--- /dev/null
+++ b/examples/template.h
@@ -0,0 +1,50 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2015 ngttp2 contributors
+ *
+ * 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.
+ */
+#ifndef TEMPLATE_H
+#define TEMPLATE_H
+
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+// inspired by <http://blog.korfuri.fr/post/go-defer-in-cpp/>, but our
+// template can take functions returning other than void.
+template <typename F, typename... T> struct Defer {
+  Defer(F &&f, T &&... t)
+      : f(std::bind(std::forward<F>(f), std::forward<T>(t)...)) {}
+  Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
+  ~Defer() { f(); }
+
+  using ResultType = typename std::result_of<typename std::decay<F>::type(
+      typename std::decay<T>::type...)>::type;
+  std::function<ResultType()> f;
+};
+
+template <typename F, typename... T> Defer<F, T...> defer(F &&f, T &&... t) {
+  return Defer<F, T...>(std::forward<F>(f), std::forward<T>(t)...);
+}
+
+#endif // TEMPLATE_H
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f930ea46..d955dca0 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -23,7 +23,7 @@
 SUBDIRS = includes
 
 AM_CFLAGS = $(WARNCFLAGS) $(EXTRACFLAG)
-AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes
+AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes -DBUILDING_NGTCP2
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libngtcp2.pc
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index d55b1392..29c92778 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -116,7 +116,8 @@ static ssize_t ngtcp2_conn_send_client_initial(ngtcp2_conn *conn, uint8_t *dest,
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
-  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_PKT_MDLEN;
+  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_STREAM_OVERHEAD -
+                  NGTCP2_PKT_MDLEN;
 
   payloadlen = conn->callbacks.send_client_initial(
       conn, NGTCP2_CONN_FLAG_NONE, &pkt_num, &payload, maxpayloadlen,
@@ -153,7 +154,7 @@ static ssize_t ngtcp2_conn_send_client_initial(ngtcp2_conn *conn, uint8_t *dest,
 
   ngtcp2_upe_padding(&upe);
 
-  conn->strm0.offset += (size_t)payloadlen;
+  conn->strm0.tx_offset += (size_t)payloadlen;
 
   return (ssize_t)ngtcp2_upe_final(&upe, NULL);
 }
@@ -173,13 +174,17 @@ static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
-  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_PKT_MDLEN;
+  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_STREAM_OVERHEAD -
+                  NGTCP2_PKT_MDLEN;
 
   payloadlen = conn->callbacks.send_client_cleartext(
       conn, NGTCP2_CONN_FLAG_NONE, &payload, maxpayloadlen, conn->user_data);
-  if (payloadlen <= 0) {
+  if (payloadlen < 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
+  if (payloadlen == 0) {
+    return 0;
+  }
 
   ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_CLIENT_INITIAL,
                      conn->conn_id, conn->next_out_pkt_num++, conn->version);
@@ -195,7 +200,7 @@ static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
   fr.stream.flags = 0;
   fr.stream.fin = 0;
   fr.stream.stream_id = 0;
-  fr.stream.offset = conn->strm0.offset;
+  fr.stream.offset = conn->strm0.tx_offset;
   fr.stream.datalen = (size_t)payloadlen;
   fr.stream.data = payload;
 
@@ -204,7 +209,7 @@ static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
     return rv;
   }
 
-  conn->strm0.offset += (size_t)payloadlen;
+  conn->strm0.tx_offset += (size_t)payloadlen;
 
   return (ssize_t)ngtcp2_upe_final(&upe, NULL);
 }
@@ -225,7 +230,8 @@ static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
-  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_PKT_MDLEN;
+  maxpayloadlen = destlen - NGTCP2_LONG_HEADERLEN - NGTCP2_STREAM_OVERHEAD -
+                  NGTCP2_PKT_MDLEN;
 
   payloadlen = conn->callbacks.send_server_cleartext(
       conn, NGTCP2_CONN_FLAG_NONE, initial ? &pkt_num : NULL, &payload,
@@ -263,7 +269,7 @@ static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
   fr.stream.flags = 0;
   fr.stream.fin = 0;
   fr.stream.stream_id = 0;
-  fr.stream.offset = conn->strm0.offset;
+  fr.stream.offset = conn->strm0.tx_offset;
   fr.stream.datalen = (size_t)payloadlen;
   fr.stream.data = payload;
 
@@ -272,13 +278,13 @@ static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
     return rv;
   }
 
-  conn->strm0.offset += (size_t)payloadlen;
+  conn->strm0.tx_offset += (size_t)payloadlen;
 
   return (ssize_t)ngtcp2_upe_final(&upe, NULL);
 }
 
 ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
-  ssize_t rv;
+  ssize_t rv = 0;
 
   switch (conn->state) {
   case NGTCP2_CS_CLIENT_INITIAL:
@@ -311,7 +317,7 @@ ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
     break;
   }
 
-  return 0;
+  return rv;
 }
 
 static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, const uint8_t *pkt,
@@ -341,7 +347,7 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, const uint8_t *pkt,
     conn->conn_id = hd.conn_id;
   }
 
-  for (;;) {
+  for (; pktlen;) {
     nread = ngtcp2_pkt_decode_frame(&fr, pkt, pktlen);
     if (nread < 0) {
       return (int)nread;
@@ -351,12 +357,12 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, const uint8_t *pkt,
     pktlen -= (size_t)nread;
 
     if (fr.type != NGTCP2_FRAME_STREAM || fr.stream.stream_id != 0 ||
-        conn->strm0.offset >= fr.stream.offset + fr.stream.datalen) {
+        conn->strm0.rx_offset >= fr.stream.offset + fr.stream.datalen) {
       continue;
     }
 
-    if (conn->strm0.offset == fr.stream.offset) {
-      conn->strm0.offset += fr.stream.datalen;
+    if (conn->strm0.rx_offset == fr.stream.offset) {
+      conn->strm0.rx_offset += fr.stream.datalen;
 
       rv = conn->callbacks.recv_handshake_data(
           conn, fr.stream.data, fr.stream.datalen, conn->user_data);
@@ -426,7 +432,7 @@ int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
     break;
   }
 
-  return -1;
+  return rv;
 }
 
 int ngtcp2_conn_emit_pending_recv_handshake(ngtcp2_conn *conn,
@@ -436,11 +442,13 @@ int ngtcp2_conn_emit_pending_recv_handshake(ngtcp2_conn *conn,
   int rv;
 
   for (;;) {
-    datalen = ngtcp2_rob_data_at(&strm->rob, &data, strm->offset);
+    datalen = ngtcp2_rob_data_at(&strm->rob, &data, strm->rx_offset);
     if (datalen == 0) {
       return 0;
     }
 
+    strm->rx_offset += datalen;
+
     rv = conn->callbacks.recv_handshake_data(conn, data, datalen,
                                              conn->user_data);
     if (rv != 0) {
@@ -452,7 +460,8 @@ int ngtcp2_conn_emit_pending_recv_handshake(ngtcp2_conn *conn,
 }
 
 int ngtcp2_strm_init(ngtcp2_strm *strm, ngtcp2_mem *mem) {
-  strm->offset = 0;
+  strm->tx_offset = 0;
+  strm->rx_offset = 0;
   strm->nbuffered = 0;
   strm->mem = mem;
   return ngtcp2_rob_init(&strm->rob, mem);
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index 07c6021e..9242982a 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -52,7 +52,8 @@ typedef enum {
 } ngtcp2_conn_state;
 
 typedef struct {
-  uint64_t offset;
+  uint64_t rx_offset;
+  uint64_t tx_offset;
   ngtcp2_rob rob;
   ngtcp2_mem *mem;
   size_t nbuffered;
diff --git a/lib/ngtcp2_pkt.h b/lib/ngtcp2_pkt.h
index 1eabb24c..a940c5f8 100644
--- a/lib/ngtcp2_pkt.h
+++ b/lib/ngtcp2_pkt.h
@@ -45,6 +45,11 @@
 #define NGTCP2_STREAM_OO_MASK 0x06
 #define NGTCP2_STREAM_D_BIT 0x01
 
+/* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required
+   other than payload for STREAM frame.  That is from type field to
+   the beginning of the payload. */
+#define NGTCP2_STREAM_OVERHEAD 15
+
 #define NGTCP2_ACK_N_BIT 0x10
 #define NGTCP2_ACK_LL_MASK 0x0c
 #define NGTCP2_ACK_MM_MASK 0x03
-- 
GitLab