[
  {
    "path": ".gitignore",
    "content": "*~\n*.o\n*.lo\n*.la\ndepcomp\n*.m4\nMakefile\nMakefile.in\nlibtool\nmissing\nautom4te.cache/\nconfig.guess\nconfig.h\nconfig.h.in\nconfig.log\nconfig.status\nconfig.sub\nconfigure\ninstall-sh\n.deps/\n.libs\nlib/includes/spdylay/spdylayver.h\nlib/libspdylay.pc\nltmain.sh\nstamp-h1\n.deps/\nINSTALL\n.DS_STORE\ntests/main\ntests/failmalloc\ncompile\ntest-driver\n"
  },
  {
    "path": "AUTHORS",
    "content": "Tatsuhiro Tsujikawa <t-tujikawa at users dot sourceforge dot net>\n"
  },
  {
    "path": "COPYING",
    "content": "The MIT License\n\nCopyright (c) 2012, 2014 Tatsuhiro Tsujikawa\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "ChangeLog",
    "content": ""
  },
  {
    "path": "Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nSUBDIRS = lib src tests examples doc python\n\nACLOCAL_AMFLAGS = -I m4\n\ndist_doc_DATA = README.rst\n\nEXTRA_DIST = shrpx.conf.sample proxy.pac.sample android-config android-make\n"
  },
  {
    "path": "NEWS",
    "content": "spdylay 1.4.0\n=============\n\nRelease Note\n------------\n\nThis release removes CREDENTIAL frame support.  The API functions are\nstill there, but they are now noop.\n\nChanges\n-------\n\n* Renew test key pair\n\n* Fix OpenSSL 1.1.0 deprecation warnings\n\n* spdylay: compile against openssl-1.1.0\n\n  It fails to compile against openssl 1.1.0 due to things like\n  |shrpx_client_handler.cc:90:30: error: 'strerror' was not declared in this scope\n  |shrpx_listen_handler.cc:112:32: error: 'memset' was not declared in this scope\n  |shrpx_listen_handler.cc:114:43: error: 'memcpy' was not declared in this scope\n\n  This resolves it.\n\n  Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>\n\n  Patch from Sebastian Andrzej Siewior\n\n* spdycat: Fix leak in SpdySession.reqvec\n\n* Compile with IRIX 6.5.22 using GCC-4.7.4\n\n  Based on the patch from Klaus Ziegler\n\n* Remove CREDENTIAL frame processing completely\n\n  We just left API as is, but related functions just do nothing now.\n\n* Allocate stream ID when spdylay_submit_{syn_stream,request} is called\n\n  This commit allocates stream ID when spdylay_submit_syn_stream and\n  spdylay_submit_request is called.  Also create stream when\n  spdylay_session_predicate_syn_stream_send is failed, to provide\n  stream to user callback (e.g., on_ctrl_not_send_callback).\n  Allocating stream ID early ensures that we can create stream because\n  we can catch stream ID exhaustion early and fail fast.  Since stream\n  ID is allocated serially, we have to send SYN_STREAM in the order\n  they queued.  So now all queued syn_stream have the same priority\n  (lowest).  The DATA frame has given priority by application.  This\n  does not work well with CREDENTIAL frame, since SYN_STREAM may wait\n  for CREDENTIAL, which results in out of order transmission.  Since\n  CREDENTIAL frame was deprecated in SPDY/3.1, and no one use it, we\n  remove its functionality in the later commit.\n\n* spdycat: --proxy-port, not --proxyport\n\n  Fixes GH-132\n\n* spdycat: Check :host header field for SNI, since Host header is not allowed\n\n* spdycat: Update spdycat --help output for --header\n\n  Patch from Chris Adams\n\n* spdycat: Fix resource leak found by coverity scan\n"
  },
  {
    "path": "README",
    "content": "See README.rst\n"
  },
  {
    "path": "README.rst",
    "content": "Spdylay - SPDY C Library\n========================\n\nThis is an experimental implementation of Google's SPDY protocol in C.\n\nThis library provides SPDY version 2, 3 and 3.1 framing layer\nimplementation.  It does not perform any I/O operations.  When the\nlibrary needs them, it calls the callback functions provided by the\napplication. It also does not include any event polling mechanism, so\nthe application can freely choose the way of handling events. This\nlibrary code does not depend on any particular SSL library (except for\nexample programs which depend on OpenSSL 1.0.1 or later).\n\nThis project also develops SPDY client, server and proxy on top of\nSpdylay library. See `SPDY Client and Server Programs`_ section.\n\nDevelopment Status\n------------------\n\nMost of the SPDY/2, SPDY/3 and SPDY/3.1 functionality has been\nimplemented.  In both versions, the direct support of server-push has\nnot been available yet.  The application can achieve server-push using\nprimitive APIs though.\n\nAs described below, we can create SPDY client and server with the\ncurrent Spdylay API.\n\nRequirements\n------------\n\nThe following packages are needed to build the library:\n\n* pkg-config >= 0.20\n* zlib >= 1.2.3\n\nTo build and run the unit test programs, the following packages are\nneeded:\n\n* cunit >= 2.1\n\nTo build and run the example programs, the following packages are\nneeded:\n\n* OpenSSL >= 1.0.1\n\nTo enable ``-a`` option (getting linked assets from the downloaded\nresource) in ``spdycat`` (one of the example program), the following\npackages are needed:\n\n* libxml2 >= 2.7.7\n\nTo build SPDY/HTTPS to HTTP reverse proxy ``shrpx`` (one of the\nexample program), the following packages are needed:\n\n* libevent-openssl >= 2.0.8\n\nIf you are using Ubuntu 12.04, you need the following packages\ninstalled::\n\n    $ apt-get install autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libevent-dev\n\nBuild from git\n--------------\n\nBuilding from git is easy, but please be sure that at least autoconf 2.68 is\nused::\n\n    $ autoreconf -i\n    $ automake\n    $ autoconf\n    $ ./configure\n    $ make\n\nBuilding documentation\n----------------------\n\nTo build documentation, run::\n\n    $ make html\n\nThe documents will be generated under ``doc/manual/html/``.\n\nThe generated documents will not be installed with ``make install``.\n\nBuilding Android binary\n------------------------\n\nIn this section, we briefly describe how to build Android binary using\n`Android NDK <http://developer.android.com/tools/sdk/ndk/index.html>`_\ncross-compiler on Debian Linux.\n\nWe offer ``android-config`` and ``android-make`` scripts to make the\nbuild easier. To make these script work, NDK toolchain must be\ninstalled in the following way. First, let introduce ``ANDROID_HOME``\nenvironment variable. We need to install toolchain under\n``$ANDROID_HOME/toolchain``. An user can freely choose the path for\n``ANDROID_HOME``.  For example, to install toolchain under\n``$ANDROID_HOME/toolchain``, do this in the the directory where NDK is\nunpacked::\n\n    $ build/tools/make-standalone-toolchain.sh --platform=android-9 --install-dir=$ANDROID_HOME/toolchain\n\nThe platform level is not important here because we don't use Android\nspecific C/C++ API.\n\nThe dependent libraries, such as OpenSSL and libevent should be built\nwith the toolchain and installed under ``$ANDROID_HOME/usr/local``.\nWe recommend to build these libraries as static library to make the\ndeployment easier. libxml2 support is currently disabled.\n\nWe use zlib which comes with Android NDK, so we don't have to build it\nby ourselves.\n\nBefore running ``android-config`` and ``android-make``,\n``ANDOIRD_HOME`` environment variable must be set to point to the\ncorrect path.\n\nAfter ``android-config``, run ``android-make`` to compile sources.\n``android-make`` is just include path to cross compiler in ``PATH``\nand run make. So if you include path to corss compiler by yourself,\nyou can just run make to build spdylay and tools as usual.\n\nAPI\n---\n\nThe public API reference is available on online. Visit\nhttp://tatsuhiro-t.github.io/spdylay/.  All public APIs are in\n*spdylay/spdylay.h*. All public API functions as well as the callback\nfunction typedefs are documented.\n\nSPDY Client and Server Programs\n-------------------------------\n\nThe *src* directory contains SPDY client and server implementations\nusing Spdylay library. These programs are intended to make sure that\nSpdylay API is acutally usable for real implementation and also for\ndebugging purposes. Please note that OpenSSL with `NPN\n<http://technotes.googlecode.com/git/nextprotoneg.html>`_ support is\nrequired in order to build and run these programs.  At the time of\nthis writing, the OpenSSL 1.0.1 supports NPN.\n\nSpdycat - SPDY client\n+++++++++++++++++++++\n\nThe SPDY client is called ``spdycat``. It is a dead simple downloader\nlike wget/curl. It connects to SPDY server and gets resources given in\nthe command-line::\n\n    $ src/spdycat -h\n    Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n                   [--key=<KEY>] [--no-tls] [-d <FILE>] [-m <N>] [-p <PROXY_HOST>]\n                   [-P <PROXY_PORT>] <URI>...\n\n    OPTIONS:\n        -v, --verbose      Print debug information such as reception/\n                           transmission of frames and name/value pairs.\n        -n, --null-out     Discard downloaded data.\n        -O, --remote-name  Save download data in the current directory.\n                           The filename is dereived from URI. If URI\n                           ends with '/', 'index.html' is used as a\n                           filename. Not implemented yet.\n        -2, --spdy2        Only use SPDY/2.\n        -3, --spdy3        Only use SPDY/3.\n        --spdy3-1          Only use SPDY/3.1.\n        -t, --timeout=<N>  Timeout each request after <N> seconds.\n        -w, --window-bits=<N>\n                           Sets the initial window size to 2**<N>.\n        -a, --get-assets   Download assets such as stylesheets, images\n                           and script files linked from the downloaded\n                           resource. Only links whose origins are the\n                           same with the linking resource will be\n                           downloaded.\n        -s, --stat         Print statistics.\n        -H, --header       Add a header to the requests.\n        --cert=<CERT>      Use the specified client certificate file.\n                           The file must be in PEM format.\n        --key=<KEY>        Use the client private key file. The file\n                           must be in PEM format.\n        --no-tls           Disable SSL/TLS. Use -2, -3 or --spdy3-1 to\n                           specify SPDY protocol version to use.\n        -d, --data=<FILE>  Post FILE to server. If - is given, data\n                           will be read from stdin.\n        -m, --multiply=<N> Request each URI <N> times. By default, same\n                           URI is not requested twice. This option\n                           disables it too.\n        -p, --proxy=<HOST> Use this host as a SPDY proxy\n        -P, --proxy-port=<PORT>\n                           Use this as the port of the SPDY proxy if\n                           one is set\n        --color            Force colored log output.\n\n    $ src/spdycat -nv https://www.google.com/\n    [  0.021] NPN select next protocol: the remote server offers:\n              * spdy/4a4\n              * spdy/3.1\n              * spdy/3\n              * http/1.1\n              NPN selected the protocol: spdy/3.1\n    [  0.029] Handshake complete\n    [  0.029] recv SETTINGS frame <version=3, flags=0, length=20>\n              (niv=2)\n              [4(1):100]\n              [7(0):1048576]\n    [  0.029] recv WINDOW_UPDATE frame <version=3, flags=0, length=8>\n              (stream_id=0, delta_window_size=983040)\n    [  0.029] send SYN_STREAM frame <version=3, flags=1, length=221>\n              (stream_id=1, assoc_stream_id=0, pri=3)\n              :host: www.google.com\n              :method: GET\n              :path: /\n              :scheme: https\n              :version: HTTP/1.1\n              accept: */*\n              accept-encoding: gzip, deflate\n              user-agent: spdylay/1.2.0-DEV\n    [  0.080] recv SYN_REPLY frame <version=3, flags=0, length=619>\n              (stream_id=1)\n              :status: 302 Found\n              :version: HTTP/1.1\n              alternate-protocol: 443:quic\n              cache-control: private\n              content-length: 262\n              content-type: text/html; charset=UTF-8\n              date: Tue, 19 Nov 2013 13:47:18 GMT\n              location: https://www.google.co.jp/\n              server: gws\n              x-frame-options: SAMEORIGIN\n              x-xss-protection: 1; mode=block\n    [  0.080] recv DATA frame (stream_id=1, flags=1, length=262)\n    [  0.080] send GOAWAY frame <version=3, flags=0, length=8>\n              (last_good_stream_id=0)\n\nSpdyd - SPDY server\n+++++++++++++++++++\n\nSPDY server is called ``spdyd`` and serves static files. It is single\nthreaded and multiplexes connections using non-blocking socket. The\nstatic files are read using blocking I/O system call, ``read(2)``. It\nspeaks SPDY/2 and SPDY/3::\n\n    $ src/spdyd --htdocs=/your/htdocs/ -v 3000 server.key server.crt\n    IPv4: listen on port 3000\n    IPv6: listen on port 3000\n    The negotiated next protocol: spdy/3.1\n    [id=1] [  1.296] send SETTINGS frame <version=3, flags=0, length=12>\n              (niv=1)\n              [4(0):100]\n    [id=1] [  1.297] recv SYN_STREAM frame <version=3, flags=1, length=228>\n              (stream_id=1, assoc_stream_id=0, pri=3)\n              :host: localhost:3000\n              :method: GET\n              :path: /README\n              :scheme: https\n              :version: HTTP/1.1\n              accept: */*\n              accept-encoding: gzip, deflate\n              user-agent: spdylay/1.2.0-DEV\n    [id=1] [  1.297] send SYN_REPLY frame <version=3, flags=0, length=116>\n              (stream_id=1)\n              :status: 200 OK\n              :version: HTTP/1.1\n              cache-control: max-age=3600\n              content-length: 66\n              date: Tue, 19 Nov 2013 14:35:24 GMT\n              last-modified: Tue, 17 Jan 2012 15:39:01 GMT\n              server: spdyd spdylay/1.2.0-DEV\n    [id=1] [  1.297] send DATA frame (stream_id=1, flags=0, length=66)\n    [id=1] [  1.297] send DATA frame (stream_id=1, flags=1, length=0)\n    [id=1] [  1.297] stream_id=1 closed\n    [id=1] [  1.297] recv GOAWAY frame <version=3, flags=0, length=8>\n              (last_good_stream_id=0)\n    [id=1] [  1.297] closed\n\nCurrently, ``spdyd`` needs ``epoll`` or ``kqueue``.\n\nShrpx - A reverse proxy for SPDY/HTTPS\n++++++++++++++++++++++++++++++++++++++\n\nFor shrpx users who uses shrpx as SPDY proxy: Please consider\nmigrating to nghttpx developed at `nghttp2 project\n<https://nghttp2.org>`_.  nghttpx supports SPDY proxy too.\n\nThe ``shrpx`` is a multi-threaded reverse proxy for SPDY/HTTPS.  It\nconverts SPDY/HTTPS traffic to plain HTTP.  It is initially developed\nas a reverse proxy, but now it has other operation modes such as a\nfrontend forward proxy.  For example, with ``--spdy-proxy`` (``-s`` in\nshorthand) option, it can be used as secure SPDY proxy with a proxy\n(e.g., Squid) in the backend.  With ``--cliet-proxy`` (``-p``) option,\nit acts like an ordinaly forward proxy but expects secure SPDY proxy\nin the backend. Thus it becomes an adapter to secure SPDY proxy for\nclients which does not support secure SPDY proxy. The another notable\noperation mode is ``--spdy-relay``, which just relays SPDY/HTTPS\ntraffic to the backend in SPDY. The following table summarizes the\noperation modes.\n\n================== ========== ======= =============\nMode option        Frontend   Backend Note\n================== ========== ======= =============\ndefault            SPDY/HTTPS HTTP    Reverse proxy\n``--spdy``         SPDY/HTTPS HTTP    SPDY proxy\n``--spdy-relay``   SPDY/HTTPS SPDY\n``--client``       HTTP       SPDY\n``--client-proxy`` HTTP       SPDY    Forward proxy\n================== ========== ======= =============\n\nThe ``shrpx`` supports configuration file. See ``--conf`` option and\nsample configuration file ``shrpx.conf.sample``.\n\nWe briefly describe the architecture of ``shrpx`` here.  It has a\ndedicated thread which listens on server sockets.  When it accepted\nthe incoming connection, it passes the file descriptor of the incoming\nconnection to one of the worker thread.  Each worker thread has its\nown event loop and can handle many connections using non-blocking I/O.\nThe number of worker thread can be specified using the command-line\noption. The `libevent <http://libevent.org/>`_ is used to handle\nlow-level network I/O.\n\nHere is the command-line options::\n\n    $ src/shrpx -h\n    Usage: shrpx [-Dh] [-s|--client|-p] [-b <HOST,PORT>]\n                 [-f <HOST,PORT>] [-n <CORES>] [-c <NUM>] [-L <LEVEL>]\n                 [OPTIONS...] [<PRIVATE_KEY> <CERT>]\n\n    A reverse proxy for SPDY/HTTPS.\n\n    Positional arguments:\n        <PRIVATE_KEY>      Set path to server's private key. Required\n                           unless either -p or --client is specified.\n        <CERT>             Set path to server's certificate. Required\n                           unless either -p or --client is specified.\n\n    OPTIONS:\n\n      Connections:\n        -b, --backend=<HOST,PORT>\n                           Set backend host and port.\n                           Default: '127.0.0.1,80'\n        -f, --frontend=<HOST,PORT>\n                           Set frontend host and port.\n                           Default: '0.0.0.0,3000'\n        --backlog=<NUM>    Set listen backlog size.\n                           Default: 256\n        --backend-ipv4     Resolve backend hostname to IPv4 address\n                           only.\n        --backend-ipv6     Resolve backend hostname to IPv6 address\n                           only.\n\n      Performance:\n        -n, --workers=<CORES>\n                           Set the number of worker threads.\n                           Default: 1\n        --read-rate=<RATE> Set maximum average read rate on frontend\n                           connection. Setting 0 to this option means\n                           read rate is unlimited.\n                           Default: 1048576\n        --read-burst=<SIZE>\n                           Set maximum read burst size on frontend\n                           connection. Setting 0 to this option means\n                           read burst size is unlimited.\n                           Default: 4194304\n        --write-rate=<RATE>\n                           Set maximum average write rate on frontend\n                           connection. Setting 0 to this option means\n                           write rate is unlimited.\n                           Default: 0\n        --write-burst=<SIZE>\n                           Set maximum write burst size on frontend\n                           connection. Setting 0 to this option means\n                           write burst size is unlimited.\n                           Default: 0\n\n      Timeout:\n        --frontend-spdy-read-timeout=<SEC>\n                           Specify read timeout for SPDY frontend\n                           connection. Default: 180\n        --frontend-read-timeout=<SEC>\n                           Specify read timeout for non-SPDY frontend\n                           connection. Default: 180\n        --frontend-write-timeout=<SEC>\n                           Specify write timeout for both SPDY and\n                           non-SPDY frontends.\n                           connection. Default: 60\n        --backend-read-timeout=<SEC>\n                           Specify read timeout for backend connection.\n                           Default: 900\n        --backend-write-timeout=<SEC>\n                           Specify write timeout for backend\n                           connection. Default: 60\n        --backend-keep-alive-timeout=<SEC>\n                           Specify keep-alive timeout for backend\n                           connection. Default: 60\n        --backend-http-proxy-uri=<URI>\n                           Specify proxy URI in the form\n                           http://[<USER>:<PASS>@]<PROXY>:<PORT>. If\n                           a proxy requires authentication, specify\n                           <USER> and <PASS>. Note that they must be\n                           properly percent-encoded. This proxy is used\n                           when the backend connection is SPDY. First,\n                           make a CONNECT request to the proxy and\n                           it connects to the backend on behalf of\n                           shrpx. This forms tunnel. After that, shrpx\n                           performs SSL/TLS handshake with the\n                           downstream through the tunnel. The timeouts\n                           when connecting and making CONNECT request\n                           can be specified by --backend-read-timeout\n                           and --backend-write-timeout options.\n\n      SSL/TLS:\n        --ciphers=<SUITE>  Set allowed cipher list. The format of the\n                           string is described in OpenSSL ciphers(1).\n                           If this option is used, --honor-cipher-order\n                           is implicitly enabled.\n        --honor-cipher-order\n                           Honor server cipher order, giving the\n                           ability to mitigate BEAST attacks.\n        -k, --insecure     When used with -p or --client, don't verify\n                           backend server's certificate.\n        --cacert=<PATH>    When used with -p or --client, set path to\n                           trusted CA certificate file.\n                           The file must be in PEM format. It can\n                           contain multiple certificates. If the\n                           linked OpenSSL is configured to load system\n                           wide certificates, they are loaded\n                           at startup regardless of this option.\n        --private-key-passwd-file=<FILEPATH>\n                           Path to file that contains password for the\n                           server's private key. If none is given and\n                           the private key is password protected it'll\n                           be requested interactively.\n        --subcert=<KEYPATH>:<CERTPATH>\n                           Specify additional certificate and private\n                           key file. Shrpx will choose certificates\n                           based on the hostname indicated by client\n                           using TLS SNI extension. This option can be\n                           used multiple times.\n        --backend-tls-sni-field=<HOST>\n                           Explicitly set the content of the TLS SNI\n                           extension.  This will default to the backend\n                           HOST name.\n        --dh-param-file=<PATH>\n                           Path to file that contains DH parameters in\n                           PEM format. Without this option, DHE cipher\n                           suites are not available.\n        --verify-client    Require and verify client certificate.\n        --verify-client-cacert=<PATH>\n                           Path to file that contains CA certificates\n                           to verify client certificate.\n                           The file must be in PEM format. It can\n                           contain multiple certificates.\n        --client-private-key-file=<PATH>\n                           Path to file that contains client private\n                           key used in backend client authentication.\n        --client-cert-file=<PATH>\n                           Path to file that contains client\n                           certificate used in backend client\n                           authentication.\n        --tls-proto-list=<LIST>\n                           Comma delimited list of SSL/TLS protocol to\n                           be enabled.\n                           The following protocols are available:\n                           TLSv1.2, TLSv1.1, TLSv1.0, SSLv3\n                           The name matching is done in case-insensitive\n                           manner.\n                           The parameter must be delimited by a single\n                           comma only and any white spaces are treated\n                           as a part of protocol string.\n                           Default: TLSv1.2,TLSv1.1,TLSv1.0\n\n      SPDY:\n        -c, --spdy-max-concurrent-streams=<NUM>\n                           Set the maximum number of the concurrent\n                           streams in one SPDY session.\n                           Default: 100\n        --frontend-spdy-window-bits=<N>\n                           Sets the per-stream initial window size of\n                           SPDY frontend connection to 2**<N>.\n                           Default: 16\n        --frontend-spdy-connection-window-bits=<N>\n                           Sets the per-connection window size of SPDY\n                           frontend connection to 2**<N>.\n                           Default: 16\n        --frontend-spdy-no-tls\n                           Disable SSL/TLS on frontend SPDY\n                           connections. SPDY protocol must be specified\n                           using --frontend-spdy-proto. This option\n                           also disables frontend HTTP/1.1.\n        --frontend-spdy-proto\n                           Specify SPDY protocol used in frontend\n                           connection if --frontend-spdy-no-tls is\n                           used. Default: spdy/3.1\n        --backend-spdy-window-bits=<N>\n                           Sets the per-stream initial window size of\n                           SPDY backend connection to 2**<N>.\n                           Default: 16\n        --backend-spdy-connection-window-bits=<N>\n                           Sets the per-connection window size of SPDY\n                           backend connection to 2**<N>.\n                           Default: 16\n        --backend-spdy-no-tls\n                           Disable SSL/TLS on backend SPDY connections.\n                           SPDY protocol must be specified using\n                           --backend-spdy-proto\n        --backend-spdy-proto\n                           Specify SPDY protocol used in backend\n                           connection if --backend-spdy-no-tls is used.\n                           Default: spdy/3.1\n\n      Mode:\n        -s, --spdy-proxy   Enable secure SPDY proxy mode.\n        --spdy-bridge      Communicate with the backend in SPDY. Thus\n                           the incoming SPDY/HTTPS connections are\n                           converted to SPDY connection and relayed to\n                           the backend. See --backend-http-proxy-uri\n                           option if you are behind the proxy and want\n                           to connect to the outside SPDY proxy.\n        --client           Instead of accepting SPDY/HTTPS connection,\n                           accept HTTP connection and communicate with\n                           backend server in SPDY. To use shrpx as\n                           a forward proxy, use -p option instead.\n        -p, --client-proxy Like --client option, but it also requires\n                           the request path from frontend must be\n                           an absolute URI, suitable for use as a\n                           forward proxy.\n\n      Logging:\n        -L, --log-level=<LEVEL>\n                           Set the severity level of log output.\n                           INFO, WARNING, ERROR and FATAL.\n                           Default: WARNING\n        --accesslog        Print simple accesslog to stderr.\n        --syslog           Send log messages to syslog.\n        --syslog-facility=<FACILITY>\n                           Set syslog facility.\n                           Default: daemon\n\n      Misc:\n        --add-x-forwarded-for\n                           Append X-Forwarded-For header field to the\n                           downstream request.\n        --no-via           Don't append to Via header field. If Via\n                           header field is received, it is left\n                           unaltered.\n        -D, --daemon       Run in a background. If -D is used, the\n                           current working directory is changed to '/'.\n        --pid-file=<PATH>  Set path to save PID of this program.\n        --user=<USER>      Run this program as USER. This option is\n                           intended to be used to drop root privileges.\n        --conf=<PATH>      Load configuration from PATH.\n                           Default: /etc/shrpx/shrpx.conf\n        -v, --version      Print version and exit.\n        -h, --help         Print this help and exit.\n\nFor those of you who are curious, ``shrpx`` is an abbreviation of\n\"Spdy/https to Http Reverse ProXy\".\n\nWithout any of ``-s``, ``--spdy-bridge``, ``-p`` and ``--client``\noptions, ``shrpx`` works as reverse proxy to the backend server::\n\n    Client <-- (SPDY, HTTPS) --> Shrpx <-- (HTTP) --> Web Server\n                            [reverse proxy]\n\nWith ``-s`` option, it works as secure SPDY proxy::\n\n    Client <-- (SPDY, HTTPS) --> Shrpx <-- (HTTP) --> Proxy\n                              [SPDY proxy]            (e.g., Squid)\n\nThe ``Client`` in the above is needs to be configured to use shrpx as\nsecure SPDY proxy.\n\nAt the time of this writing, Chrome is the only browser which supports\nsecure SPDY proxy. The one way to configure Chrome to use secure SPDY\nproxy is create proxy.pac script like this::\n\n    function FindProxyForURL(url, host) {\n        return \"HTTPS SERVERADDR:PORT\";\n    }\n\n``SERVERADDR`` and ``PORT`` is the hostname/address and port of the\nmachine shrpx is running.  Please note that Chrome requires valid\ncertificate for secure SPDY proxy.\n\nThen run chrome with the following arguments::\n\n    $ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn\n\n.. note::\n\n   At the time of this writing, Chrome 24 limits the maximum\n   concurrent connections to the proxy to 32. And due to the\n   limitation of socket pool handling in Chrome, it is quickly filled\n   up if SPDY proxy is used and many SPDY sessions are established. If\n   it reaches the limit, the new connections are simply blocked until\n   existing connections are timed out. (See `Chrome Issue 92244\n   <https://code.google.com/p/chromium/issues/detail?id=92244>`_). The\n   workaround is make the number of maximum connections high, say, 99,\n   which is the highest. To do this, you need to change so called\n   Policy setup.  See `Policy Templates\n   <http://dev.chromium.org/administrators/policy-templates>`_ for\n   details how to change Policy setup on the platform you use.  The\n   Policy name we are looking for is `MaxConnectionsPerProxy\n   <http://dev.chromium.org/administrators/policy-list-3#MaxConnectionsPerProxy>`_\n   For example, if you are using Linux, follow the instruction\n   described in `Linux Quick Start\n   <http://dev.chromium.org/administrators/linux-quick-start>`_ and\n   create ``/etc/opt/chrome/policies/managed/test_policy.json`` file\n   with the following content and restart Chrome::\n\n       {\n           \"MaxConnectionsPerProxy\" :99\n       }\n\nWith ``--spdy-bridge``, it accepts SPDY/HTTPS connections and\ncommunicates with backend in SPDY::\n\n    Client <-- (SPDY, HTTPS) --> Shrpx <-- (SPDY) --> Web or SPDY Proxy etc\n                              [SPDY bridge]           (e.g., shrpx -s)\n\nWith ``-p`` option, it works as forward proxy and expects that the\nbackend is secure SPDY proxy::\n\n    Client <-- (HTTP) --> Shrpx <-- (SPDY) --> Secure SPDY Proxy\n                     [forward proxy]         (e.g., shrpx -s or node-spdyproxy)\n\nThe ``Client`` is needs to be configured to use shrpx as forward proxy.\n\nIn this configuration, clients which do not support secure SPDY proxy\ncan use secure SPDY proxy through ``shrpx``. Putting ``shrpx`` in the\nsame box or same network with the clients, this configuration can\nbring the benefits of secure SPDY proxy to those clients. Since the\nmaximum number of connections per server still applies in proxy\nconnection, the performance gain is not obvious. For example, if the\nmaximum number of connections per server is 6, after sending 6\nrequests to the proxy, client blocks further requests, which kills\nperformance which might be gained in SPDY connection.  For clients\nwhich can tweak these values (e.g.,\n``network.http.max-connections-per-server`` in Firefox), increasing\nthem may improve the performance.\n\nWith ``--client`` option, it works as reverse proxy and expects that\nthe backend is SPDY-enabled Web server::\n\n    Client <-- (HTTP) --> Shrpx <-- (SPDY) --> Web Server\n                     [reverse proxy]\n\nFor the operation modes which talk to the backend in SPDY, the backend\nconnections can be tunneled though HTTP proxy. The proxy is specified\nusing ``--backend-http-proxy-uri`` option. The following figure\nillustrates the example of ``--spdy-bridge`` and\n``--backend-http-proxy-uri`` option to talk to the outside SPDY proxy\nthrough HTTP proxy::\n\n    Client <-- (SPDY, HTTPS) --> Shrpx <-- (SPDY) --\n                             [SPDY bridge]\n\n            --===================---> SPDY Proxy\n              (HTTP proxy tunnel)     (e.g., shrpx -s)\n\nExamples\n--------\n\nThe *examples* directory contains a simple SPDY client implementation\nin C.\n\nPython-Spdylay - Python Wrapper\n-------------------------------\n\nThe library comes with Python wrapper ``python-spdylay``. See\n``python`` directory.\n"
  },
  {
    "path": "android-config",
    "content": "#!/bin/sh\n#\n# Spdylay - SPDY Library\n#\n# Copyright (c) 2013 Tatsuhiro Tsujikawa\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nif [ -z \"$ANDROID_HOME\" ]; then\n    echo 'No $ANDROID_HOME specified.'\n    exit 1\nfi\nPREFIX=$ANDROID_HOME/usr/local\nTOOLCHAIN=$ANDROID_HOME/toolchain\nPATH=$TOOLCHAIN/bin:$PATH\n\n./configure \\\n    --disable-shared \\\n    --host=arm-linux-androideabi \\\n    --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \\\n    --without-libxml2 \\\n    CPPFLAGS=\"-I$PREFIX/include\" \\\n    PKG_CONFIG_LIBDIR=\"$PREFIX/lib/pkgconfig\" \\\n    LDFLAGS=\"-L$PREFIX/lib\"\n"
  },
  {
    "path": "android-make",
    "content": "#!/bin/sh\n#\n# Spdylay - SPDY Library\n#\n# Copyright (c) 2013 Tatsuhiro Tsujikawa\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nif [ -z \"$ANDROID_HOME\" ]; then\n    echo 'No $ANDROID_HOME specified.'\n    exit 1\nfi\nTOOLCHAIN=$ANDROID_HOME/toolchain\nPATH=$TOOLCHAIN/bin:$PATH\n\nmake \"$@\"\n"
  },
  {
    "path": "configure.ac",
    "content": "dnl Spdylay - SPDY Library\n\ndnl Copyright (c) 2012 Tatsuhiro Tsujikawa\n\ndnl Permission is hereby granted, free of charge, to any person obtaining\ndnl a copy of this software and associated documentation files (the\ndnl \"Software\"), to deal in the Software without restriction, including\ndnl without limitation the rights to use, copy, modify, merge, publish,\ndnl distribute, sublicense, and/or sell copies of the Software, and to\ndnl permit persons to whom the Software is furnished to do so, subject to\ndnl the following conditions:\n\ndnl The above copyright notice and this permission notice shall be\ndnl included in all copies or substantial portions of the Software.\n\ndnl THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\ndnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\ndnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\ndnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\ndnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\ndnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\ndnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nAC_PREREQ(2.61)\nAC_INIT([spdylay], [1.4.1-DEV], [t-tujikawa@users.sourceforge.net])\nLT_PREREQ([2.2.6])\nLT_INIT()\ndnl See versioning rule:\ndnl  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html\nAC_SUBST(LT_CURRENT, 9)\nAC_SUBST(LT_REVISION, 1)\nAC_SUBST(LT_AGE, 2)\n\nAC_CANONICAL_BUILD\nAC_CANONICAL_HOST\nAC_CANONICAL_TARGET\n\nAC_CONFIG_MACRO_DIR([m4])\n\nAM_INIT_AUTOMAKE([subdir-objects])\nAC_CONFIG_HEADERS([config.h])\n\ndnl Checks for command-line options\nAC_ARG_ENABLE([maintainer-mode],\n    [AS_HELP_STRING([--enable-maintainer-mode],\n                    [Turn on compile time warnings])],\n    [maintainer_mode=$enableval], [maintainer_mode=no])\n\nAC_ARG_ENABLE([src],\n    [AS_HELP_STRING([--enable-src],\n                    [Build installable SPDY client/server programs in src])],\n    [request_src=$enableval], [request_src=yes])\n\nAC_ARG_ENABLE([examples],\n    [AS_HELP_STRING([--enable-examples],\n                    [Build example programs])],\n    [request_examples=$enableval], [request_examples=yes])\n\nAC_ARG_WITH([libxml2],\n    [AS_HELP_STRING([--without-libxml2],\n                    [disable support for libxml2])],\n    [], [with_libxml2=yes])\n\ndnl Checks for programs\nAC_PROG_CC\nAC_PROG_CXX\nAC_PROG_INSTALL\nAC_PROG_LN_S\nAC_PROG_MAKE_SET\nAM_PROG_CC_C_O\nPKG_PROG_PKG_CONFIG([0.20])\n\n#\n# If we're running GCC or clang define _U_ to be \"__attribute__((unused))\"\n# so we can use _U_ to flag unused function parameters and not get warnings\n# about them. Otherwise, define _U_ to be an empty string so that _U_ used\n# to flag an unused function parameters will compile with other compilers.\n#\n# XXX - similar hints for other compilers?\n#\nif test \"x$GCC\" = \"xyes\" -o \"x$CC\" = \"xclang\" ; then\n  AC_DEFINE([_U_], [__attribute__((unused))], [Hint to the compiler that a function parameters is not used])\nelse\n  AC_DEFINE([_U_], , [Hint to the compiler that a function parameters is not used])\nfi\n\nAC_COMPILE_STDCXX_11\nAM_CONDITIONAL([HAVE_STDCXX_11],\n               [ test \"x$ac_cv_cxx_compile_cxx11_cxx\" = \"xyes\" ])\n\n# Checks for libraries.\n\n# Additional libraries required for tests.\nTESTS_LIBS=\n\n# Additional libraries required for programs under src directory.\nSRC_LIBS=\n\nLIBS_OLD=$LIBS\n# Search for dlsym function, which is used in tests. Linux needs -ldl,\n# but netbsd does not need it.\nAC_SEARCH_LIBS([dlsym], [dl])\nTESTS_LIBS=\"$LIBS $TESTS_LIBS\"\nLIBS=$LIBS_OLD\n\nLIBS_OLD=$LIBS\nAC_SEARCH_LIBS([clock_gettime], [rt],\n               [AC_DEFINE([HAVE_CLOCK_GETTIME], [1],\n                          [Define to 1 if you have the `clock_gettime`.])])\nSRC_LIBS=\"$LIBS $SRC_LIBS\"\nLIBS=$LIBS_OLD\n\ncase \"$host\" in\n  *android*)\n    android_build=yes\n    # android does not need -pthread, but needs followng 2 libs for C++\n    SRC_LIBS=\"$SRC_LIBS -lstdc++ -lsupc++\"\n    ;;\n  *)\n    SRC_LIBS=\"$SRC_LIBS -pthread\"\n    ;;\nesac\n\n# zlib\nif test \"x$android_build\" = \"xyes\"; then\n  # Use zlib provided by NDK\n  LIBS=\"-lz $LIBS\"\nelse\n  PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3])\n  LIBS=\"$ZLIB_LIBS $LIBS\"\n  CFLAGS=\"$CFLAGS $ZLIB_CFLAGS\"\nfi\n\n# cunit\nPKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])\n# If pkg-config does not find cunit, check it using AC_CHECK_LIB.  We\n# do this because Debian (Ubuntu) lacks pkg-config file for cunit.\nif test \"x${have_cunit}\" = \"xno\"; then\n  AC_MSG_WARN([${CUNIT_PKG_ERRORS}])\n  AC_CHECK_LIB([cunit], [CU_initialize_registry],\n               [have_cunit=yes], [have_cunit=no])\n  if test \"x${have_cunit}\" = \"xyes\"; then\n    CUNIT_LIBS=\"-lcunit\"\n    CUNIT_CFLAGS=\"\"\n    AC_SUBST([CUNIT_LIBS])\n    AC_SUBST([CUNIT_CFLAGS])\n  fi\nfi\nif test \"x${have_cunit}\" = \"xyes\"; then\n  # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test\n  # program can be built without -lncurses, but it emits runtime\n  # error.\n  case \"${build}\" in\n    *-apple-darwin*)\n      CUNIT_LIBS=\"$CUNIT_LIBS -lncurses\"\n      AC_SUBST([CUNIT_LIBS])\n      ;;\n  esac\nfi\n\nAM_CONDITIONAL([HAVE_CUNIT], [ test \"x${have_cunit}\" = \"xyes\" ])\n\n# openssl (for examples)\nPKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1],\n                  [have_openssl=yes], [have_openssl=no])\nif test \"x${have_openssl}\" = \"xno\"; then\n  AC_MSG_NOTICE($OPENSSL_PKG_ERRORS)\n  AC_MSG_NOTICE([The example programs will not be built.])\nfi\n\n# libevent_openssl\n# 2.0.8 is required because we use evconnlistener_set_error_cb()\nPKG_CHECK_MODULES([LIBEVENT_OPENSSL], [libevent_openssl >= 2.0.8],\n                  [have_libevent_openssl=yes], [have_libevent_openssl=no])\nif test \"x${have_libevent_openssl}\" = \"xno\"; then\n  AC_MSG_NOTICE($LIBEVENT_OPENSSL_PKG_ERRORS)\n  AC_MSG_NOTICE([Shrpx example program will not be built.])\nfi\nAM_CONDITIONAL([HAVE_LIBEVENT_OPENSSL],\n               [ test \"x${have_libevent_openssl}\" = \"xyes\" ])\n\n# libxml2 (for examples/spdycat)\nhave_libxml2=no\nif test \"x$with_libxml2\" != \"xno\"; then\n  AM_PATH_XML2(2.7.7, [have_libxml2=yes], [have_libxml2=no])\n  if test \"x${have_libxml2}\" = \"xyes\"; then\n    AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have `libxml2` library.])\n  fi\nfi\nAM_CONDITIONAL([HAVE_LIBXML2], [ test \"x${have_libxml2}\" = \"xyes\" ])\n\n# The src programs depend on OpenSSL\nenable_src=no\nif test \"x${request_src}\" = \"xyes\" &&\n   test \"x${have_openssl}\" = \"xyes\"; then\n    enable_src=yes\nfi\n\nAM_CONDITIONAL([ENABLE_SRC], [ test \"x${enable_src}\" = \"xyes\" ])\n\n# The example programs depend on OpenSSL\nenable_examples=no\nif test \"x${request_examples}\" = \"xyes\" &&\n   test \"x${have_openssl}\" = \"xyes\"; then\n    enable_examples=yes\nfi\n\nAM_CONDITIONAL([ENABLE_EXAMPLES], [ test \"x${enable_examples}\" = \"xyes\" ])\n\n# Checks for header files.\nAC_CHECK_HEADERS([ \\\n  arpa/inet.h \\\n  netinet/in.h \\\n  pwd.h \\\n  stddef.h \\\n  stdint.h \\\n  stdlib.h \\\n  string.h \\\n  time.h \\\n  unistd.h \\\n])\n\ncase \"${host}\" in\n  *mingw*)\n    # For ntohl, ntohs in Windows\n    AC_CHECK_HEADERS([winsock2.h])\n    ;;\nesac\n\n# Checks for typedefs, structures, and compiler characteristics.\nAC_TYPE_SIZE_T\nAC_TYPE_SSIZE_T\nAC_TYPE_UINT8_T\nAC_TYPE_UINT16_T\nAC_TYPE_UINT32_T\nAC_TYPE_UINT64_T\nAC_CHECK_TYPES([ptrdiff_t])\nAC_C_BIGENDIAN\nAC_SYS_LARGEFILE\n\n# Checks for library functions.\nif test \"x$cross_compiling\" != \"xyes\"; then\n  AC_FUNC_MALLOC\nfi\nAC_CHECK_FUNCS([ \\\n  getpwnam \\\n  memmove \\\n  memset \\\n  timegm \\\n])\n\nAX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no])\nif test \"x${have_epoll}\" = \"xyes\"; then\n  AC_DEFINE([HAVE_EPOLL], [1], [Define to 1 if you have the `epoll`.])\nfi\nAM_CONDITIONAL([HAVE_EPOLL], [ test \"x${have_epoll}\" = \"xyes\" ])\n\nAC_CHECK_FUNCS([kqueue], [have_kqueue=yes])\nAM_CONDITIONAL([HAVE_KQUEUE], [ test \"x${have_kqueue}\" = \"xyes\" ])\n\nAM_CONDITIONAL([ENABLE_SPDYD],\n         [ test \"x${have_epoll}\" = \"xyes\" || test \"x${have_kqueue}\" = \"xyes\" ])\n\nAC_LANG_PUSH(C++)\nAC_MSG_CHECKING([whether struct kevent.udata is intptr_t])\nAC_COMPILE_IFELSE([AC_LANG_PROGRAM([[\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\n]],\n[[\nstruct kevent event;\nevent.udata = (intptr_t)(&event);\n]])],\n[kevent_udata_intptr_t=yes], [kevent_udata_intptr_t=no])\nAC_MSG_RESULT([$kevent_udata_intptr_t])\nif test \"x$kevent_udata_intptr_t\" = \"xyes\"; then\n  AC_DEFINE([KEVENT_UDATA_INTPTR_T], [1],\n            [Define to 1 if struct kevent.udata is intptr_t])\nfi\nAC_LANG_POP()\n\ndnl Windows library for winsock2\ncase \"${host}\" in\n  *mingw*)\n    LIBS=\"$LIBS -lws2_32\"\n    ;;\nesac\n\nif test \"x$maintainer_mode\" != \"xno\"; then\n    AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS=\"$CFLAGS -Wall\"])\n    AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS=\"$CFLAGS -Wextra\"])\n    AX_CHECK_COMPILE_FLAG([-Werror], [CFLAGS=\"$CFLAGS -Werror\"])\n    AX_CHECK_COMPILE_FLAG([-Wmissing-prototypes], [CFLAGS=\"$CFLAGS -Wmissing-prototypes\"])\n    AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], [CFLAGS=\"$CFLAGS -Wstrict-prototypes\"])\n    AX_CHECK_COMPILE_FLAG([-Wmissing-declarations], [CFLAGS=\"$CFLAGS -Wmissing-declarations\"])\n    AX_CHECK_COMPILE_FLAG([-Wpointer-arith], [CFLAGS=\"$CFLAGS -Wpointer-arith\"])\n    AX_CHECK_COMPILE_FLAG([-Wdeclaration-after-statement], [CFLAGS=\"$CFLAGS -Wdeclaration-after-statement\"])\n    AX_CHECK_COMPILE_FLAG([-Wformat-security], [CFLAGS=\"$CFLAGS -Wformat-security\"])\n    AX_CHECK_COMPILE_FLAG([-Wwrite-strings], [CFLAGS=\"$CFLAGS -Wwrite-strings\"])\n    AX_CHECK_COMPILE_FLAG([-Wshadow], [CFLAGS=\"$CFLAGS -Wshadow\"])\n    AX_CHECK_COMPILE_FLAG([-Winline], [CFLAGS=\"$CFLAGS -Winline\"])\n    AX_CHECK_COMPILE_FLAG([-Wnested-externs], [CFLAGS=\"$CFLAGS -Wnested-externs\"])\n    AX_CHECK_COMPILE_FLAG([-Wfloat-equal], [CFLAGS=\"$CFLAGS -Wfloat-equal\"])\n    AX_CHECK_COMPILE_FLAG([-Wundef], [CFLAGS=\"$CFLAGS -Wundef\"])\n    AX_CHECK_COMPILE_FLAG([-Wendif-labels], [CFLAGS=\"$CFLAGS -Wendif-labels\"])\n    AX_CHECK_COMPILE_FLAG([-Wempty-body], [CFLAGS=\"$CFLAGS -Wempty-body\"])\n    AX_CHECK_COMPILE_FLAG([-Wcast-align], [CFLAGS=\"$CFLAGS -Wcast-align\"])\n    AX_CHECK_COMPILE_FLAG([-Wclobbered], [CFLAGS=\"$CFLAGS -Wclobbered\"])\n    AX_CHECK_COMPILE_FLAG([-Wvla], [CFLAGS=\"$CFLAGS -Wvla\"])\n    AX_CHECK_COMPILE_FLAG([-Wpragmas], [CFLAGS=\"$CFLAGS -Wpragmas\"])\n    AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [CFLAGS=\"$CFLAGS -Wunreachable-code\"])\n    AX_CHECK_COMPILE_FLAG([-Waddress], [CFLAGS=\"$CFLAGS -Waddress\"])\n    AX_CHECK_COMPILE_FLAG([-Wattributes], [CFLAGS=\"$CFLAGS -Wattributes\"])\n    AX_CHECK_COMPILE_FLAG([-Wdiv-by-zero], [CFLAGS=\"$CFLAGS -Wdiv-by-zero\"])\n    AX_CHECK_COMPILE_FLAG([-Wshorten-64-to-32], [CFLAGS=\"$CFLAGS -Wshorten-64-to-32\"])\n\n    # Only work with Clang for the moment\n    AX_CHECK_COMPILE_FLAG([-Wheader-guard], [CFLAGS=\"$CFLAGS -Wheader-guard\"])\nfi\n\nAC_SUBST([TESTS_LIBS])\nAC_SUBST([SRC_LIBS])\n\nAC_CONFIG_FILES([\n  Makefile\n  lib/Makefile\n  lib/libspdylay.pc\n  lib/includes/Makefile\n  lib/includes/spdylay/spdylayver.h\n  tests/Makefile\n  tests/testdata/Makefile\n  src/Makefile\n  examples/Makefile\n  doc/Makefile\n  doc/conf.py\n  python/Makefile\n])\nAC_OUTPUT\n\nAC_MSG_NOTICE([summary of build options:\n\n    Version:        ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE\n    Host type:      ${host}\n    Install prefix: ${prefix}\n    C compiler:     ${CC}\n    CFLAGS:         ${CFLAGS}\n    LDFLAGS:        ${LDFLAGS}\n    LIBS:           ${LIBS}\n    CPPFLAGS:       ${CPPFLAGS}\n    C preprocessor: ${CPP}\n    C++ compiler:   ${CXX}\n    CXXFLAGS:       ${CXXFLAGS}\n    CXXCPP:         ${CXXCPP}\n    Library types:  Shared=${enable_shared}, Static=${enable_static}\n    CUnit:          ${have_cunit}\n    OpenSSL:        ${have_openssl}\n    Libxml2:        ${have_libxml2}\n    Libevent(SSL):  ${have_libevent_openssl}\n    Src:            ${enable_src}\n    Examples:       ${enable_examples}\n])\n"
  },
  {
    "path": "doc/.gitignore",
    "content": "apiref.rst\nconf.py\nmanual\n"
  },
  {
    "path": "doc/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nEXTRA_DIST = README.rst apiref-header.rst index.rst mkapiref.py python.rst \\\n\tpackage_README.rst android-spdy-proxy.rst \\\n\t_themes/sphinx_rtd_theme/footer.html \\\n\t_themes/sphinx_rtd_theme/theme.conf \\\n\t_themes/sphinx_rtd_theme/layout_old.html \\\n\t_themes/sphinx_rtd_theme/__init__.py \\\n\t_themes/sphinx_rtd_theme/layout.html \\\n\t_themes/sphinx_rtd_theme/search.html \\\n\t_themes/sphinx_rtd_theme/breadcrumbs.html \\\n\t_themes/sphinx_rtd_theme/versions.html \\\n\t_themes/sphinx_rtd_theme/searchbox.html \\\n\t_themes/sphinx_rtd_theme/static/fonts/FontAwesome.otf \\\n\t_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.svg \\\n\t_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.woff \\\n\t_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.eot \\\n\t_themes/sphinx_rtd_theme/static/fonts/fontawesome-webfont.ttf \\\n\t_themes/sphinx_rtd_theme/static/js/theme.js \\\n\t_themes/sphinx_rtd_theme/static/css/theme.css \\\n\t_themes/sphinx_rtd_theme/static/css/badge_only.css\n\n# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = manual\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\napiref.rst: $(top_builddir)/lib/includes/spdylay/spdylayver.h \\\n\t$(top_builddir)/lib/includes/spdylay/spdylay.h\n\t$(builddir)/mkapiref.py --header apiref-header.rst $^ > $@\n\nclean:\n\t-rm apiref.rst\n\t-rm -rf $(BUILDDIR)/*\n\nhtml: apiref.rst\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/Spdylay.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/Spdylay.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/Spdylay\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Spdylay\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "doc/README.rst",
    "content": "Spdylay Documentation\n=====================\n\nThe documentation of Spdylay is generated using Sphinx.  This\ndirectory contains the source files to be processed by Sphinx.  The\nsource file for API reference is generated using a script called\n``mkapiref.py`` from the Spdylay C source code.\n\nGenerating API reference\n------------------------\n\nAs described earlier, we use ``mkapiref.py`` to generate rst formatted\ntext of API reference from C source code.  The ``mkapiref.py`` is not\nso flexible and it requires that C source code is formatted in rather\nstrict rules.\n\nTo generate API reference, just run ``make html``. It runs\n``mkapiref.py`` and then run Sphinx to build the entire document.\n\nThe ``mkapiref.py`` reads C source code and searches the comment block\nstarts with ``/**``. In other words, it only processes the comment\nblock starting ``/**``. The comment block must end with ``*/``. The\n``mkapiref.py`` requires that which type of the object this comment\nblock refers to.  To specify the type of the object, the next line\nmust contain the so-caled action keyword.  Currently, the following\naction keywords are supported: ``@function``, ``@functypedef``,\n``@enum``, ``@struct`` and ``@union``. The following sections\ndescribes each action keyword.\n\n@function\n#########\n\n``@function`` is used to refer to the function.  The comment block is\nused for the document for the function.  After the script sees the end\nof the comment block, it consumes the lines as the function\ndeclaration until the line which ends with ``;`` is encountered.\n\nIn Sphinx doc, usually the function argument is formatted like\n``*this*``.  But in C, ``*`` is used for dereferencing a pointer and\nwe must escape ``*`` with a back slash. To avoid this, we format the\nargument like ``|this|``. The ``mkapiref.py`` translates it with\n``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary.\nNote that this shadows the substitution feature of Sphinx.\n\nThe example follows::\n\n    /**\n     * @function\n     *\n     * Submits PING frame to the |session|.\n     */\n    int spdylay_submit_ping(spdylay_session *session);\n\n\n@functypedef\n############\n\n``@functypedef`` is used to refer to the typedef of the function\npointer. The formatting rule is pretty much the same with\n``@function``, but this outputs ``type`` domain, rather than\n``function`` domain.\n\nThe example follows::\n\n    /**\n     * @functypedef\n     *\n     * Callback function invoked when |session| wants to send data to\n     * remote peer.\n     */\n    typedef ssize_t (*spdylay_send_callback)\n    (spdylay_session *session,\n     const uint8_t *data, size_t length, int flags, void *user_data);\n\n@enum\n#####\n\n``@enum`` is used to refer to the enum.  Currently, only enum typedefs\nare supported.  The comment block is used for the document for the\nenum type itself. To document each values, put comment block starting\nwith the line ``/**`` and ending with the ``*/`` just before the enum\nvalue.  When the line starts with ``}`` is encountered, the\n``mkapiref.py`` extracts strings next to ``}`` as the name of enum.\n\nAt the time of this writing, Sphinx does not support enum type. So we\nuse ``type`` domain for enum it self and ``macro`` domain for each\nvalue. To refer to the enum value, use ``:enum:`` pseudo role. The\n``mkapiref.py`` replaces it with ``:macro:``. By doing this, when\nSphinx will support enum officially, we can replace ``:enum:`` with\nthe official role easily.\n\nThe example follows::\n\n    /**\n     * @enum\n     * Error codes used in the Spdylay library.\n     */\n    typedef enum {\n      /**\n       * Invalid argument passed.\n       */\n      SPDYLAY_ERR_INVALID_ARGUMENT = -501,\n      /**\n       * Zlib error.\n       */\n      SPDYLAY_ERR_ZLIB = -502,\n    } spdylay_error;\n\n@struct\n#######\n\n``@struct`` is used to refer to the struct. Currently, only struct\ntypedefs are supported. The comment block is used for the document for\nthe struct type itself.To document each member, put comment block\nstarting with the line ``/**`` and ending with the ``*/`` just before\nthe member.  When the line starts with ``}`` is encountered, the\n``mkapiref.py`` extracts strings next to ``}`` as the name of struct.\nThe block-less typedef is also supported. In this case, typedef\ndeclaration must be all in one line and the ``mkapiref.py`` uses last\nword as the name of struct.\n\nSome examples follow::\n    \n    /**\n     * @struct\n     * The control frame header.\n     */\n    typedef struct {\n      /**\n       * SPDY protocol version.\n       */\n      uint16_t version;\n      /**\n       * The type of this control frame.\n       */\n      uint16_t type;\n      /**\n       * The control frame flags.\n       */\n      uint8_t flags;\n      /**\n       * The length field of this control frame.\n       */\n      int32_t length;\n    } spdylay_ctrl_hd;\n        \n    /**\n     * @struct\n     *\n     * The primary structure to hold the resources needed for a SPDY\n     * session. The details of this structure is hidden from the public\n     * API.\n     */\n    typedef struct spdylay_session spdylay_session;\n\n@union\n######\n\n``@union`` is used to refer to the union. Currently, ``@union`` is an\nalias of ``@struct``.\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/__init__.py",
    "content": "\"\"\"Sphinx ReadTheDocs theme.\n\nFrom https://github.com/ryan-roemer/sphinx-bootstrap-theme.\n\n\"\"\"\nimport os\n\nVERSION = (0, 1, 5)\n\n__version__ = \".\".join(str(v) for v in VERSION)\n__version_full__ = __version__\n\n\ndef get_html_theme_path():\n    \"\"\"Return list of HTML theme paths.\"\"\"\n    cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))\n    return cur_dir\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/breadcrumbs.html",
    "content": "<div role=\"navigation\" aria-label=\"breadcrumbs navigation\">\n  <ul class=\"wy-breadcrumbs\">\n    <li><a href=\"{{ pathto(master_doc) }}\">Docs</a> &raquo;</li>\n      {% for doc in parents %}\n          <li><a href=\"{{ doc.link|e }}\">{{ doc.title }}</a> &raquo;</li>\n      {% endfor %}\n    <li>{{ title }}</li>\n      <li class=\"wy-breadcrumbs-aside\">\n        {% if display_github %}\n          <a href=\"https://github.com/{{ github_user }}/{{ github_repo }}/blob/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}\" class=\"fa fa-github\"> Edit on GitHub</a>\n        {% elif display_bitbucket %}\n          <a href=\"https://bitbucket.org/{{ bitbucket_user }}/{{ bitbucket_repo }}/src/{{ bitbucket_version}}{{ conf_py_path }}{{ pagename }}{{ source_suffix }}\" class=\"fa fa-bitbucket\"> Edit on Bitbucket</a>\n        {% elif show_source and has_source and sourcename %}\n          <a href=\"{{ pathto('_sources/' + sourcename, true)|e }}\" rel=\"nofollow\"> View page source</a>\n        {% endif %}\n      </li>\n  </ul>\n  <hr/>\n</div>\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/footer.html",
    "content": "<footer>\n  {% if next or prev %}\n    <div class=\"rst-footer-buttons\" role=\"navigation\" aria-label=\"footer navigation\">\n      {% if next %}\n        <a href=\"{{ next.link|e }}\" class=\"btn btn-neutral float-right\" title=\"{{ next.title|striptags|e }}\">Next <span class=\"fa fa-arrow-circle-right\"></span></a>\n      {% endif %}\n      {% if prev %}\n        <a href=\"{{ prev.link|e }}\" class=\"btn btn-neutral\" title=\"{{ prev.title|striptags|e }}\"><span class=\"fa fa-arrow-circle-left\"></span> Previous</a>\n      {% endif %}\n    </div>\n  {% endif %}\n\n  <hr/>\n\n  <div role=\"contentinfo\">\n    <p>\n    {%- if show_copyright %}\n      {%- if hasdoc('copyright') %}\n        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href=\"{{ path }}\">Copyright</a> {{ copyright }}.{% endtrans %}\n      {%- else %}\n        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}\n      {%- endif %}\n    {%- endif %}\n\n    {%- if last_updated %}\n      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}\n    {%- endif %}\n    </p>\n  </div>\n\n  {% trans %}<a href=\"https://github.com/snide/sphinx_rtd_theme\">Sphinx theme</a> provided by <a href=\"https://readthedocs.org\">Read the Docs</a>{% endtrans %}\n</footer>\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/layout.html",
    "content": "{# TEMPLATE VAR SETTINGS #}\n{%- set url_root = pathto('', 1) %}\n{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}\n{%- if not embedded and docstitle %}\n  {%- set titlesuffix = \" &mdash; \"|safe + docstitle|e %}\n{%- else %}\n  {%- set titlesuffix = \"\" %}\n{%- endif %}\n\n<!DOCTYPE html>\n<!--[if IE 8]><html class=\"no-js lt-ie9\" lang=\"en\" > <![endif]-->\n<!--[if gt IE 8]><!--> <html class=\"no-js\" lang=\"en\" > <!--<![endif]-->\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  {% block htmltitle %}\n  <title>{{ title|striptags|e }}{{ titlesuffix }}</title>\n  {% endblock %}\n\n  {# FAVICON #}\n  {% if favicon %}\n    <link rel=\"shortcut icon\" href=\"{{ pathto('_static/' + favicon, 1) }}\"/>\n  {% endif %}\n\n  {# CSS #}\n  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>\n\n  {# OPENSEARCH #}\n  {% if not embedded %}\n    {% if use_opensearch %}\n      <link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}\" href=\"{{ pathto('_static/opensearch.xml', 1) }}\"/>\n    {% endif %}\n\n  {% endif %}\n\n  {# RTD hosts this file, so just load on non RTD builds #}\n  {% if not READTHEDOCS %}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/' + style, 1) }}\" type=\"text/css\" />\n  {% endif %}\n\n  {% for cssfile in css_files %}\n    <link rel=\"stylesheet\" href=\"{{ pathto(cssfile, 1) }}\" type=\"text/css\" />\n  {% endfor %}\n\n  {%- block linktags %}\n    {%- if hasdoc('about') %}\n        <link rel=\"author\" title=\"{{ _('About these documents') }}\"\n              href=\"{{ pathto('about') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('genindex') %}\n        <link rel=\"index\" title=\"{{ _('Index') }}\"\n              href=\"{{ pathto('genindex') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('search') %}\n        <link rel=\"search\" title=\"{{ _('Search') }}\" href=\"{{ pathto('search') }}\"/>\n    {%- endif %}\n    {%- if hasdoc('copyright') %}\n        <link rel=\"copyright\" title=\"{{ _('Copyright') }}\" href=\"{{ pathto('copyright') }}\"/>\n    {%- endif %}\n    <link rel=\"top\" title=\"{{ docstitle|e }}\" href=\"{{ pathto('index') }}\"/>\n    {%- if parents %}\n        <link rel=\"up\" title=\"{{ parents[-1].title|striptags|e }}\" href=\"{{ parents[-1].link|e }}\"/>\n    {%- endif %}\n    {%- if next %}\n        <link rel=\"next\" title=\"{{ next.title|striptags|e }}\" href=\"{{ next.link|e }}\"/>\n    {%- endif %}\n    {%- if prev %}\n        <link rel=\"prev\" title=\"{{ prev.title|striptags|e }}\" href=\"{{ prev.link|e }}\"/>\n    {%- endif %}\n  {%- endblock %}\n  {%- block extrahead %} {% endblock %}\n\n  {# Keep modernizr in head - http://modernizr.com/docs/#installing #}\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js\"></script>\n\n</head>\n\n<body class=\"wy-body-for-nav\" role=\"document\">\n\n  <div class=\"wy-grid-for-nav\">\n\n    {# SIDE NAV, TOGGLES ON MOBILE #}\n    <nav data-toggle=\"wy-nav-shift\" class=\"wy-nav-side\">\n      <div class=\"wy-side-nav-search\">\n        <a href=\"{{ pathto(master_doc) }}\" class=\"fa fa-home\"> {{ project }}</a>\n        {% include \"searchbox.html\" %}\n      </div>\n\n      <div class=\"wy-menu wy-menu-vertical\" data-spy=\"affix\" role=\"navigation\" aria-label=\"main navigation\">\n        {% set toctree = toctree(maxdepth=2, collapse=False, includehidden=True) %}\n        {% if toctree %}\n            {{ toctree }}\n        {% else %}\n            <!-- Local TOC -->\n            <div class=\"local-toc\">{{ toc }}</div>\n        {% endif %}\n      </div>\n      &nbsp;\n    </nav>\n\n    <section data-toggle=\"wy-nav-shift\" class=\"wy-nav-content-wrap\">\n\n      {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}\n      <nav class=\"wy-nav-top\" role=\"navigation\" aria-label=\"top navigation\">\n        <i data-toggle=\"wy-nav-top\" class=\"fa fa-bars\"></i>\n        <a href=\"{{ pathto(master_doc) }}\">{{ project }}</a>\n      </nav>\n\n\n      {# PAGE CONTENT #}\n      <div class=\"wy-nav-content\">\n        <div class=\"rst-content\">\n          {% include \"breadcrumbs.html\" %}\n          <div role=\"main\">\n            {% block body %}{% endblock %}\n          </div>\n          {% include \"footer.html\" %}\n        </div>\n      </div>\n\n    </section>\n\n  </div>\n  {% include \"versions.html\" %}\n\n  {% if not embedded %}\n\n    <script type=\"text/javascript\">\n        var DOCUMENTATION_OPTIONS = {\n            URL_ROOT:'{{ url_root }}',\n            VERSION:'{{ release|e }}',\n            COLLAPSE_INDEX:false,\n            FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',\n            HAS_SOURCE:  {{ has_source|lower }}\n        };\n    </script>\n    {%- for scriptfile in script_files %}\n      <script type=\"text/javascript\" src=\"{{ pathto(scriptfile, 1) }}\"></script>\n    {%- endfor %}\n\n  {% endif %}\n\n  {# RTD hosts this file, so just load on non RTD builds #}\n  {% if not READTHEDOCS %}\n    <script type=\"text/javascript\" src=\"{{ pathto('_static/js/theme.js', 1) }}\"></script>\n  {% endif %}\n\n  {# STICKY NAVIGATION #}\n  {% if theme_sticky_navigation %}\n  <script type=\"text/javascript\">\n      jQuery(function () {\n          SphinxRtdTheme.StickyNav.enable();\n      });\n  </script>\n  {% endif %}\n\n  {%- block footer %} {% endblock %}\n\n</body>\n</html>\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/layout_old.html",
    "content": "{#\n    basic/layout.html\n    ~~~~~~~~~~~~~~~~~\n\n    Master layout template for Sphinx themes.\n\n    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.\n    :license: BSD, see LICENSE for details.\n#}\n{%- block doctype -%}\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n{%- endblock %}\n{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}\n{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}\n{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and\n                         (sidebars != []) %}\n{%- set url_root = pathto('', 1) %}\n{# XXX necessary? #}\n{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}\n{%- if not embedded and docstitle %}\n  {%- set titlesuffix = \" &mdash; \"|safe + docstitle|e %}\n{%- else %}\n  {%- set titlesuffix = \"\" %}\n{%- endif %}\n\n{%- macro relbar() %}\n    <div class=\"related\">\n      <h3>{{ _('Navigation') }}</h3>\n      <ul>\n        {%- for rellink in rellinks %}\n        <li class=\"right\" {% if loop.first %}style=\"margin-right: 10px\"{% endif %}>\n          <a href=\"{{ pathto(rellink[0]) }}\" title=\"{{ rellink[1]|striptags|e }}\"\n             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>\n          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>\n        {%- endfor %}\n        {%- block rootrellink %}\n        <li><a href=\"{{ pathto(master_doc) }}\">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>\n        {%- endblock %}\n        {%- for parent in parents %}\n          <li><a href=\"{{ parent.link|e }}\" {% if loop.last %}{{ accesskey(\"U\") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>\n        {%- endfor %}\n        {%- block relbaritems %} {% endblock %}\n      </ul>\n    </div>\n{%- endmacro %}\n\n{%- macro sidebar() %}\n      {%- if render_sidebar %}\n      <div class=\"sphinxsidebar\">\n        <div class=\"sphinxsidebarwrapper\">\n          {%- block sidebarlogo %}\n          {%- if logo %}\n            <p class=\"logo\"><a href=\"{{ pathto(master_doc) }}\">\n              <img class=\"logo\" src=\"{{ pathto('_static/' + logo, 1) }}\" alt=\"Logo\"/>\n            </a></p>\n          {%- endif %}\n          {%- endblock %}\n          {%- if sidebars != None %}\n            {#- new style sidebar: explicitly include/exclude templates #}\n            {%- for sidebartemplate in sidebars %}\n            {%- include sidebartemplate %}\n            {%- endfor %}\n          {%- else %}\n            {#- old style sidebars: using blocks -- should be deprecated #}\n            {%- block sidebartoc %}\n            {%- include \"localtoc.html\" %}\n            {%- endblock %}\n            {%- block sidebarrel %}\n            {%- include \"relations.html\" %}\n            {%- endblock %}\n            {%- block sidebarsourcelink %}\n            {%- include \"sourcelink.html\" %}\n            {%- endblock %}\n            {%- if customsidebar %}\n            {%- include customsidebar %}\n            {%- endif %}\n            {%- block sidebarsearch %}\n            {%- include \"searchbox.html\" %}\n            {%- endblock %}\n          {%- endif %}\n        </div>\n      </div>\n      {%- endif %}\n{%- endmacro %}\n\n{%- macro script() %}\n    <script type=\"text/javascript\">\n      var DOCUMENTATION_OPTIONS = {\n        URL_ROOT:    '{{ url_root }}',\n        VERSION:     '{{ release|e }}',\n        COLLAPSE_INDEX: false,\n        FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',\n        HAS_SOURCE:  {{ has_source|lower }}\n      };\n    </script>\n    {%- for scriptfile in script_files %}\n    <script type=\"text/javascript\" src=\"{{ pathto(scriptfile, 1) }}\"></script>\n    {%- endfor %}\n{%- endmacro %}\n\n{%- macro css() %}\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/' + style, 1) }}\" type=\"text/css\" />\n    <link rel=\"stylesheet\" href=\"{{ pathto('_static/pygments.css', 1) }}\" type=\"text/css\" />\n    {%- for cssfile in css_files %}\n    <link rel=\"stylesheet\" href=\"{{ pathto(cssfile, 1) }}\" type=\"text/css\" />\n    {%- endfor %}\n{%- endmacro %}\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset={{ encoding }}\" />\n    {{ metatags }}\n    {%- block htmltitle %}\n    <title>{{ title|striptags|e }}{{ titlesuffix }}</title>\n    {%- endblock %}\n    {{ css() }}\n    {%- if not embedded %}\n    {{ script() }}\n    {%- if use_opensearch %}\n    <link rel=\"search\" type=\"application/opensearchdescription+xml\"\n          title=\"{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}\"\n          href=\"{{ pathto('_static/opensearch.xml', 1) }}\"/>\n    {%- endif %}\n    {%- if favicon %}\n    <link rel=\"shortcut icon\" href=\"{{ pathto('_static/' + favicon, 1) }}\"/>\n    {%- endif %}\n    {%- endif %}\n{%- block linktags %}\n    {%- if hasdoc('about') %}\n    <link rel=\"author\" title=\"{{ _('About these documents') }}\" href=\"{{ pathto('about') }}\" />\n    {%- endif %}\n    {%- if hasdoc('genindex') %}\n    <link rel=\"index\" title=\"{{ _('Index') }}\" href=\"{{ pathto('genindex') }}\" />\n    {%- endif %}\n    {%- if hasdoc('search') %}\n    <link rel=\"search\" title=\"{{ _('Search') }}\" href=\"{{ pathto('search') }}\" />\n    {%- endif %}\n    {%- if hasdoc('copyright') %}\n    <link rel=\"copyright\" title=\"{{ _('Copyright') }}\" href=\"{{ pathto('copyright') }}\" />\n    {%- endif %}\n    <link rel=\"top\" title=\"{{ docstitle|e }}\" href=\"{{ pathto('index') }}\" />\n    {%- if parents %}\n    <link rel=\"up\" title=\"{{ parents[-1].title|striptags|e }}\" href=\"{{ parents[-1].link|e }}\" />\n    {%- endif %}\n    {%- if next %}\n    <link rel=\"next\" title=\"{{ next.title|striptags|e }}\" href=\"{{ next.link|e }}\" />\n    {%- endif %}\n    {%- if prev %}\n    <link rel=\"prev\" title=\"{{ prev.title|striptags|e }}\" href=\"{{ prev.link|e }}\" />\n    {%- endif %}\n{%- endblock %}\n{%- block extrahead %} {% endblock %}\n  </head>\n  <body>\n{%- block header %}{% endblock %}\n\n{%- block relbar1 %}{{ relbar() }}{% endblock %}\n\n{%- block content %}\n  {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}\n\n    <div class=\"document\">\n  {%- block document %}\n      <div class=\"documentwrapper\">\n      {%- if render_sidebar %}\n        <div class=\"bodywrapper\">\n      {%- endif %}\n          <div class=\"body\">\n            {% block body %} {% endblock %}\n          </div>\n      {%- if render_sidebar %}\n        </div>\n      {%- endif %}\n      </div>\n  {%- endblock %}\n\n  {%- block sidebar2 %}{{ sidebar() }}{% endblock %}\n      <div class=\"clearer\"></div>\n    </div>\n{%- endblock %}\n\n{%- block relbar2 %}{{ relbar() }}{% endblock %}\n\n{%- block footer %}\n    <div class=\"footer\">\n    {%- if show_copyright %}\n      {%- if hasdoc('copyright') %}\n        {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href=\"{{ path }}\">Copyright</a> {{ copyright }}.{% endtrans %}\n      {%- else %}\n        {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}\n      {%- endif %}\n    {%- endif %}\n    {%- if last_updated %}\n      {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}\n    {%- endif %}\n    {%- if show_sphinx %}\n      {% trans sphinx_version=sphinx_version|e %}Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> {{ sphinx_version }}.{% endtrans %}\n    {%- endif %}\n    </div>\n    <p>asdf asdf asdf asdf 22</p>\n{%- endblock %}\n  </body>\n</html>\n\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/search.html",
    "content": "{#\n    basic/search.html\n    ~~~~~~~~~~~~~~~~~\n\n    Template for the search page.\n\n    :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.\n    :license: BSD, see LICENSE for details.\n#}\n{%- extends \"layout.html\" %}\n{% set title = _('Search') %}\n{% set script_files = script_files + ['_static/searchtools.js'] %}\n{% block footer %}\n  <script type=\"text/javascript\">\n    jQuery(function() { Search.loadIndex(\"{{ pathto('searchindex.js', 1) }}\"); });\n  </script>\n  {# this is used when loading the search index using $.ajax fails,\n     such as on Chrome for documents on localhost #}\n  <script type=\"text/javascript\" id=\"searchindexloader\"></script>\n  {{ super() }}\n{% endblock %}\n{% block body %}\n  <noscript>\n  <div id=\"fallback\" class=\"admonition warning\">\n    <p class=\"last\">\n      {% trans %}Please activate JavaScript to enable the search\n      functionality.{% endtrans %}\n    </p>\n  </div>\n  </noscript>\n\n  {% if search_performed %}\n    <h2>{{ _('Search Results') }}</h2>\n    {% if not search_results %}\n      <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\\'ve selected enough categories.') }}</p>\n    {% endif %}\n  {% endif %}\n  <div id=\"search-results\">\n  {% if search_results %}\n    <ul>\n    {% for href, caption, context in search_results %}\n      <li>\n        <a href=\"{{ pathto(item.href) }}\">{{ caption }}</a>\n        <p class=\"context\">{{ context|e }}</p>\n      </li>\n    {% endfor %}\n    </ul>\n  {% endif %}\n  </div>\n{% endblock %}\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/searchbox.html",
    "content": "<div role=\"search\">\n  <form id =\"rtd-search-form\" class=\"wy-form\" action=\"{{ pathto('search') }}\" method=\"get\">\n    <input type=\"text\" name=\"q\" placeholder=\"Search docs\" />\n    <input type=\"hidden\" name=\"check_keywords\" value=\"yes\" />\n    <input type=\"hidden\" name=\"area\" value=\"default\" />\n  </form>\n</div>\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/static/css/badge_only.css",
    "content": ".fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:\"\"}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url(\"../font/fontawesome_webfont.eot\");src:url(\"../font/fontawesome_webfont.eot?#iefix\") format(\"embedded-opentype\"),url(\"../font/fontawesome_webfont.woff\") format(\"woff\"),url(\"../font/fontawesome_webfont.ttf\") format(\"truetype\"),url(\"../font/fontawesome_webfont.svg#FontAwesome\") format(\"svg\")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:\"\\f02d\"}.icon-book:before{content:\"\\f02d\"}.fa-caret-down:before{content:\"\\f0d7\"}.icon-caret-down:before{content:\"\\f0d7\"}.fa-caret-up:before{content:\"\\f0d8\"}.icon-caret-up:before{content:\"\\f0d8\"}.fa-caret-left:before{content:\"\\f0d9\"}.icon-caret-left:before{content:\"\\f0d9\"}.fa-caret-right:before{content:\"\\f0da\"}.icon-caret-right:before{content:\"\\f0da\"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:\"\"}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/static/css/theme.css",
    "content": "*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:\"courier new\",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:\"\";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type=\"search\"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=\"search\"]::-webkit-search-decoration,input[type=\"search\"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^=\"javascript:\"]:after,a[href^=\"#\"]:after{content:\"\"}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type=\"text\"],input[type=\"password\"],input[type=\"email\"],input[type=\"url\"],input[type=\"date\"],input[type=\"month\"],input[type=\"time\"],input[type=\"datetime\"],input[type=\"datetime-local\"],input[type=\"week\"],input[type=\"number\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"color\"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:\"\"}.clearfix:after{clear:both}/*!\n *  Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome\n *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)\n */@font-face{font-family:'FontAwesome';src:url(\"../fonts/fontawesome-webfont.eot?v=4.0.3\");src:url(\"../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3\") format(\"embedded-opentype\"),url(\"../fonts/fontawesome-webfont.woff?v=4.0.3\") format(\"woff\"),url(\"../fonts/fontawesome-webfont.ttf?v=4.0.3\") format(\"truetype\"),url(\"../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular\") format(\"svg\");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:\"\\f000\"}.fa-music:before{content:\"\\f001\"}.fa-search:before,.icon-search:before{content:\"\\f002\"}.fa-envelope-o:before{content:\"\\f003\"}.fa-heart:before{content:\"\\f004\"}.fa-star:before{content:\"\\f005\"}.fa-star-o:before{content:\"\\f006\"}.fa-user:before{content:\"\\f007\"}.fa-film:before{content:\"\\f008\"}.fa-th-large:before{content:\"\\f009\"}.fa-th:before{content:\"\\f00a\"}.fa-th-list:before{content:\"\\f00b\"}.fa-check:before{content:\"\\f00c\"}.fa-times:before{content:\"\\f00d\"}.fa-search-plus:before{content:\"\\f00e\"}.fa-search-minus:before{content:\"\\f010\"}.fa-power-off:before{content:\"\\f011\"}.fa-signal:before{content:\"\\f012\"}.fa-gear:before,.fa-cog:before{content:\"\\f013\"}.fa-trash-o:before{content:\"\\f014\"}.fa-home:before,.icon-home:before{content:\"\\f015\"}.fa-file-o:before{content:\"\\f016\"}.fa-clock-o:before{content:\"\\f017\"}.fa-road:before{content:\"\\f018\"}.fa-download:before{content:\"\\f019\"}.fa-arrow-circle-o-down:before{content:\"\\f01a\"}.fa-arrow-circle-o-up:before{content:\"\\f01b\"}.fa-inbox:before{content:\"\\f01c\"}.fa-play-circle-o:before{content:\"\\f01d\"}.fa-rotate-right:before,.fa-repeat:before{content:\"\\f01e\"}.fa-refresh:before{content:\"\\f021\"}.fa-list-alt:before{content:\"\\f022\"}.fa-lock:before{content:\"\\f023\"}.fa-flag:before{content:\"\\f024\"}.fa-headphones:before{content:\"\\f025\"}.fa-volume-off:before{content:\"\\f026\"}.fa-volume-down:before{content:\"\\f027\"}.fa-volume-up:before{content:\"\\f028\"}.fa-qrcode:before{content:\"\\f029\"}.fa-barcode:before{content:\"\\f02a\"}.fa-tag:before{content:\"\\f02b\"}.fa-tags:before{content:\"\\f02c\"}.fa-book:before,.icon-book:before{content:\"\\f02d\"}.fa-bookmark:before{content:\"\\f02e\"}.fa-print:before{content:\"\\f02f\"}.fa-camera:before{content:\"\\f030\"}.fa-font:before{content:\"\\f031\"}.fa-bold:before{content:\"\\f032\"}.fa-italic:before{content:\"\\f033\"}.fa-text-height:before{content:\"\\f034\"}.fa-text-width:before{content:\"\\f035\"}.fa-align-left:before{content:\"\\f036\"}.fa-align-center:before{content:\"\\f037\"}.fa-align-right:before{content:\"\\f038\"}.fa-align-justify:before{content:\"\\f039\"}.fa-list:before{content:\"\\f03a\"}.fa-dedent:before,.fa-outdent:before{content:\"\\f03b\"}.fa-indent:before{content:\"\\f03c\"}.fa-video-camera:before{content:\"\\f03d\"}.fa-picture-o:before{content:\"\\f03e\"}.fa-pencil:before{content:\"\\f040\"}.fa-map-marker:before{content:\"\\f041\"}.fa-adjust:before{content:\"\\f042\"}.fa-tint:before{content:\"\\f043\"}.fa-edit:before,.fa-pencil-square-o:before{content:\"\\f044\"}.fa-share-square-o:before{content:\"\\f045\"}.fa-check-square-o:before{content:\"\\f046\"}.fa-arrows:before{content:\"\\f047\"}.fa-step-backward:before{content:\"\\f048\"}.fa-fast-backward:before{content:\"\\f049\"}.fa-backward:before{content:\"\\f04a\"}.fa-play:before{content:\"\\f04b\"}.fa-pause:before{content:\"\\f04c\"}.fa-stop:before{content:\"\\f04d\"}.fa-forward:before{content:\"\\f04e\"}.fa-fast-forward:before{content:\"\\f050\"}.fa-step-forward:before{content:\"\\f051\"}.fa-eject:before{content:\"\\f052\"}.fa-chevron-left:before{content:\"\\f053\"}.fa-chevron-right:before{content:\"\\f054\"}.fa-plus-circle:before{content:\"\\f055\"}.fa-minus-circle:before{content:\"\\f056\"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:\"\\f057\"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:\"\\f058\"}.fa-question-circle:before{content:\"\\f059\"}.fa-info-circle:before{content:\"\\f05a\"}.fa-crosshairs:before{content:\"\\f05b\"}.fa-times-circle-o:before{content:\"\\f05c\"}.fa-check-circle-o:before{content:\"\\f05d\"}.fa-ban:before{content:\"\\f05e\"}.fa-arrow-left:before{content:\"\\f060\"}.fa-arrow-right:before{content:\"\\f061\"}.fa-arrow-up:before{content:\"\\f062\"}.fa-arrow-down:before{content:\"\\f063\"}.fa-mail-forward:before,.fa-share:before{content:\"\\f064\"}.fa-expand:before{content:\"\\f065\"}.fa-compress:before{content:\"\\f066\"}.fa-plus:before{content:\"\\f067\"}.fa-minus:before{content:\"\\f068\"}.fa-asterisk:before{content:\"\\f069\"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:\"\\f06a\"}.fa-gift:before{content:\"\\f06b\"}.fa-leaf:before{content:\"\\f06c\"}.fa-fire:before,.icon-fire:before{content:\"\\f06d\"}.fa-eye:before{content:\"\\f06e\"}.fa-eye-slash:before{content:\"\\f070\"}.fa-warning:before,.fa-exclamation-triangle:before{content:\"\\f071\"}.fa-plane:before{content:\"\\f072\"}.fa-calendar:before{content:\"\\f073\"}.fa-random:before{content:\"\\f074\"}.fa-comment:before{content:\"\\f075\"}.fa-magnet:before{content:\"\\f076\"}.fa-chevron-up:before{content:\"\\f077\"}.fa-chevron-down:before{content:\"\\f078\"}.fa-retweet:before{content:\"\\f079\"}.fa-shopping-cart:before{content:\"\\f07a\"}.fa-folder:before{content:\"\\f07b\"}.fa-folder-open:before{content:\"\\f07c\"}.fa-arrows-v:before{content:\"\\f07d\"}.fa-arrows-h:before{content:\"\\f07e\"}.fa-bar-chart-o:before{content:\"\\f080\"}.fa-twitter-square:before{content:\"\\f081\"}.fa-facebook-square:before{content:\"\\f082\"}.fa-camera-retro:before{content:\"\\f083\"}.fa-key:before{content:\"\\f084\"}.fa-gears:before,.fa-cogs:before{content:\"\\f085\"}.fa-comments:before{content:\"\\f086\"}.fa-thumbs-o-up:before{content:\"\\f087\"}.fa-thumbs-o-down:before{content:\"\\f088\"}.fa-star-half:before{content:\"\\f089\"}.fa-heart-o:before{content:\"\\f08a\"}.fa-sign-out:before{content:\"\\f08b\"}.fa-linkedin-square:before{content:\"\\f08c\"}.fa-thumb-tack:before{content:\"\\f08d\"}.fa-external-link:before{content:\"\\f08e\"}.fa-sign-in:before{content:\"\\f090\"}.fa-trophy:before{content:\"\\f091\"}.fa-github-square:before{content:\"\\f092\"}.fa-upload:before{content:\"\\f093\"}.fa-lemon-o:before{content:\"\\f094\"}.fa-phone:before{content:\"\\f095\"}.fa-square-o:before{content:\"\\f096\"}.fa-bookmark-o:before{content:\"\\f097\"}.fa-phone-square:before{content:\"\\f098\"}.fa-twitter:before{content:\"\\f099\"}.fa-facebook:before{content:\"\\f09a\"}.fa-github:before,.icon-github:before{content:\"\\f09b\"}.fa-unlock:before{content:\"\\f09c\"}.fa-credit-card:before{content:\"\\f09d\"}.fa-rss:before{content:\"\\f09e\"}.fa-hdd-o:before{content:\"\\f0a0\"}.fa-bullhorn:before{content:\"\\f0a1\"}.fa-bell:before{content:\"\\f0f3\"}.fa-certificate:before{content:\"\\f0a3\"}.fa-hand-o-right:before{content:\"\\f0a4\"}.fa-hand-o-left:before{content:\"\\f0a5\"}.fa-hand-o-up:before{content:\"\\f0a6\"}.fa-hand-o-down:before{content:\"\\f0a7\"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:\"\\f0a8\"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:\"\\f0a9\"}.fa-arrow-circle-up:before{content:\"\\f0aa\"}.fa-arrow-circle-down:before{content:\"\\f0ab\"}.fa-globe:before{content:\"\\f0ac\"}.fa-wrench:before{content:\"\\f0ad\"}.fa-tasks:before{content:\"\\f0ae\"}.fa-filter:before{content:\"\\f0b0\"}.fa-briefcase:before{content:\"\\f0b1\"}.fa-arrows-alt:before{content:\"\\f0b2\"}.fa-group:before,.fa-users:before{content:\"\\f0c0\"}.fa-chain:before,.fa-link:before,.icon-link:before{content:\"\\f0c1\"}.fa-cloud:before{content:\"\\f0c2\"}.fa-flask:before{content:\"\\f0c3\"}.fa-cut:before,.fa-scissors:before{content:\"\\f0c4\"}.fa-copy:before,.fa-files-o:before{content:\"\\f0c5\"}.fa-paperclip:before{content:\"\\f0c6\"}.fa-save:before,.fa-floppy-o:before{content:\"\\f0c7\"}.fa-square:before{content:\"\\f0c8\"}.fa-bars:before{content:\"\\f0c9\"}.fa-list-ul:before{content:\"\\f0ca\"}.fa-list-ol:before{content:\"\\f0cb\"}.fa-strikethrough:before{content:\"\\f0cc\"}.fa-underline:before{content:\"\\f0cd\"}.fa-table:before{content:\"\\f0ce\"}.fa-magic:before{content:\"\\f0d0\"}.fa-truck:before{content:\"\\f0d1\"}.fa-pinterest:before{content:\"\\f0d2\"}.fa-pinterest-square:before{content:\"\\f0d3\"}.fa-google-plus-square:before{content:\"\\f0d4\"}.fa-google-plus:before{content:\"\\f0d5\"}.fa-money:before{content:\"\\f0d6\"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:\"\\f0d7\"}.fa-caret-up:before{content:\"\\f0d8\"}.fa-caret-left:before{content:\"\\f0d9\"}.fa-caret-right:before{content:\"\\f0da\"}.fa-columns:before{content:\"\\f0db\"}.fa-unsorted:before,.fa-sort:before{content:\"\\f0dc\"}.fa-sort-down:before,.fa-sort-asc:before{content:\"\\f0dd\"}.fa-sort-up:before,.fa-sort-desc:before{content:\"\\f0de\"}.fa-envelope:before{content:\"\\f0e0\"}.fa-linkedin:before{content:\"\\f0e1\"}.fa-rotate-left:before,.fa-undo:before{content:\"\\f0e2\"}.fa-legal:before,.fa-gavel:before{content:\"\\f0e3\"}.fa-dashboard:before,.fa-tachometer:before{content:\"\\f0e4\"}.fa-comment-o:before{content:\"\\f0e5\"}.fa-comments-o:before{content:\"\\f0e6\"}.fa-flash:before,.fa-bolt:before{content:\"\\f0e7\"}.fa-sitemap:before{content:\"\\f0e8\"}.fa-umbrella:before{content:\"\\f0e9\"}.fa-paste:before,.fa-clipboard:before{content:\"\\f0ea\"}.fa-lightbulb-o:before{content:\"\\f0eb\"}.fa-exchange:before{content:\"\\f0ec\"}.fa-cloud-download:before{content:\"\\f0ed\"}.fa-cloud-upload:before{content:\"\\f0ee\"}.fa-user-md:before{content:\"\\f0f0\"}.fa-stethoscope:before{content:\"\\f0f1\"}.fa-suitcase:before{content:\"\\f0f2\"}.fa-bell-o:before{content:\"\\f0a2\"}.fa-coffee:before{content:\"\\f0f4\"}.fa-cutlery:before{content:\"\\f0f5\"}.fa-file-text-o:before{content:\"\\f0f6\"}.fa-building-o:before{content:\"\\f0f7\"}.fa-hospital-o:before{content:\"\\f0f8\"}.fa-ambulance:before{content:\"\\f0f9\"}.fa-medkit:before{content:\"\\f0fa\"}.fa-fighter-jet:before{content:\"\\f0fb\"}.fa-beer:before{content:\"\\f0fc\"}.fa-h-square:before{content:\"\\f0fd\"}.fa-plus-square:before{content:\"\\f0fe\"}.fa-angle-double-left:before{content:\"\\f100\"}.fa-angle-double-right:before{content:\"\\f101\"}.fa-angle-double-up:before{content:\"\\f102\"}.fa-angle-double-down:before{content:\"\\f103\"}.fa-angle-left:before{content:\"\\f104\"}.fa-angle-right:before{content:\"\\f105\"}.fa-angle-up:before{content:\"\\f106\"}.fa-angle-down:before{content:\"\\f107\"}.fa-desktop:before{content:\"\\f108\"}.fa-laptop:before{content:\"\\f109\"}.fa-tablet:before{content:\"\\f10a\"}.fa-mobile-phone:before,.fa-mobile:before{content:\"\\f10b\"}.fa-circle-o:before{content:\"\\f10c\"}.fa-quote-left:before{content:\"\\f10d\"}.fa-quote-right:before{content:\"\\f10e\"}.fa-spinner:before{content:\"\\f110\"}.fa-circle:before{content:\"\\f111\"}.fa-mail-reply:before,.fa-reply:before{content:\"\\f112\"}.fa-github-alt:before{content:\"\\f113\"}.fa-folder-o:before{content:\"\\f114\"}.fa-folder-open-o:before{content:\"\\f115\"}.fa-smile-o:before{content:\"\\f118\"}.fa-frown-o:before{content:\"\\f119\"}.fa-meh-o:before{content:\"\\f11a\"}.fa-gamepad:before{content:\"\\f11b\"}.fa-keyboard-o:before{content:\"\\f11c\"}.fa-flag-o:before{content:\"\\f11d\"}.fa-flag-checkered:before{content:\"\\f11e\"}.fa-terminal:before{content:\"\\f120\"}.fa-code:before{content:\"\\f121\"}.fa-reply-all:before{content:\"\\f122\"}.fa-mail-reply-all:before{content:\"\\f122\"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:\"\\f123\"}.fa-location-arrow:before{content:\"\\f124\"}.fa-crop:before{content:\"\\f125\"}.fa-code-fork:before{content:\"\\f126\"}.fa-unlink:before,.fa-chain-broken:before{content:\"\\f127\"}.fa-question:before{content:\"\\f128\"}.fa-info:before{content:\"\\f129\"}.fa-exclamation:before{content:\"\\f12a\"}.fa-superscript:before{content:\"\\f12b\"}.fa-subscript:before{content:\"\\f12c\"}.fa-eraser:before{content:\"\\f12d\"}.fa-puzzle-piece:before{content:\"\\f12e\"}.fa-microphone:before{content:\"\\f130\"}.fa-microphone-slash:before{content:\"\\f131\"}.fa-shield:before{content:\"\\f132\"}.fa-calendar-o:before{content:\"\\f133\"}.fa-fire-extinguisher:before{content:\"\\f134\"}.fa-rocket:before{content:\"\\f135\"}.fa-maxcdn:before{content:\"\\f136\"}.fa-chevron-circle-left:before{content:\"\\f137\"}.fa-chevron-circle-right:before{content:\"\\f138\"}.fa-chevron-circle-up:before{content:\"\\f139\"}.fa-chevron-circle-down:before{content:\"\\f13a\"}.fa-html5:before{content:\"\\f13b\"}.fa-css3:before{content:\"\\f13c\"}.fa-anchor:before{content:\"\\f13d\"}.fa-unlock-alt:before{content:\"\\f13e\"}.fa-bullseye:before{content:\"\\f140\"}.fa-ellipsis-h:before{content:\"\\f141\"}.fa-ellipsis-v:before{content:\"\\f142\"}.fa-rss-square:before{content:\"\\f143\"}.fa-play-circle:before{content:\"\\f144\"}.fa-ticket:before{content:\"\\f145\"}.fa-minus-square:before{content:\"\\f146\"}.fa-minus-square-o:before{content:\"\\f147\"}.fa-level-up:before{content:\"\\f148\"}.fa-level-down:before{content:\"\\f149\"}.fa-check-square:before{content:\"\\f14a\"}.fa-pencil-square:before{content:\"\\f14b\"}.fa-external-link-square:before{content:\"\\f14c\"}.fa-share-square:before{content:\"\\f14d\"}.fa-compass:before{content:\"\\f14e\"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:\"\\f150\"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:\"\\f151\"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:\"\\f152\"}.fa-euro:before,.fa-eur:before{content:\"\\f153\"}.fa-gbp:before{content:\"\\f154\"}.fa-dollar:before,.fa-usd:before{content:\"\\f155\"}.fa-rupee:before,.fa-inr:before{content:\"\\f156\"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:\"\\f157\"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:\"\\f158\"}.fa-won:before,.fa-krw:before{content:\"\\f159\"}.fa-bitcoin:before,.fa-btc:before{content:\"\\f15a\"}.fa-file:before{content:\"\\f15b\"}.fa-file-text:before{content:\"\\f15c\"}.fa-sort-alpha-asc:before{content:\"\\f15d\"}.fa-sort-alpha-desc:before{content:\"\\f15e\"}.fa-sort-amount-asc:before{content:\"\\f160\"}.fa-sort-amount-desc:before{content:\"\\f161\"}.fa-sort-numeric-asc:before{content:\"\\f162\"}.fa-sort-numeric-desc:before{content:\"\\f163\"}.fa-thumbs-up:before{content:\"\\f164\"}.fa-thumbs-down:before{content:\"\\f165\"}.fa-youtube-square:before{content:\"\\f166\"}.fa-youtube:before{content:\"\\f167\"}.fa-xing:before{content:\"\\f168\"}.fa-xing-square:before{content:\"\\f169\"}.fa-youtube-play:before{content:\"\\f16a\"}.fa-dropbox:before{content:\"\\f16b\"}.fa-stack-overflow:before{content:\"\\f16c\"}.fa-instagram:before{content:\"\\f16d\"}.fa-flickr:before{content:\"\\f16e\"}.fa-adn:before{content:\"\\f170\"}.fa-bitbucket:before,.icon-bitbucket:before{content:\"\\f171\"}.fa-bitbucket-square:before{content:\"\\f172\"}.fa-tumblr:before{content:\"\\f173\"}.fa-tumblr-square:before{content:\"\\f174\"}.fa-long-arrow-down:before{content:\"\\f175\"}.fa-long-arrow-up:before{content:\"\\f176\"}.fa-long-arrow-left:before{content:\"\\f177\"}.fa-long-arrow-right:before{content:\"\\f178\"}.fa-apple:before{content:\"\\f179\"}.fa-windows:before{content:\"\\f17a\"}.fa-android:before{content:\"\\f17b\"}.fa-linux:before{content:\"\\f17c\"}.fa-dribbble:before{content:\"\\f17d\"}.fa-skype:before{content:\"\\f17e\"}.fa-foursquare:before{content:\"\\f180\"}.fa-trello:before{content:\"\\f181\"}.fa-female:before{content:\"\\f182\"}.fa-male:before{content:\"\\f183\"}.fa-gittip:before{content:\"\\f184\"}.fa-sun-o:before{content:\"\\f185\"}.fa-moon-o:before{content:\"\\f186\"}.fa-archive:before{content:\"\\f187\"}.fa-bug:before{content:\"\\f188\"}.fa-vk:before{content:\"\\f189\"}.fa-weibo:before{content:\"\\f18a\"}.fa-renren:before{content:\"\\f18b\"}.fa-pagelines:before{content:\"\\f18c\"}.fa-stack-exchange:before{content:\"\\f18d\"}.fa-arrow-circle-o-right:before{content:\"\\f18e\"}.fa-arrow-circle-o-left:before{content:\"\\f190\"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:\"\\f191\"}.fa-dot-circle-o:before{content:\"\\f192\"}.fa-wheelchair:before{content:\"\\f193\"}.fa-vimeo-square:before{content:\"\\f194\"}.fa-turkish-lira:before,.fa-try:before{content:\"\\f195\"}.fa-plus-square-o:before{content:\"\\f196\"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:\"FontAwesome\";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:\"\"}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=\"search\"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:\" \";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:\"\"}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:\"\"}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:\" *\";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type=\"text\"],.wy-control-group .wy-form-full input[type=\"password\"],.wy-control-group .wy-form-full input[type=\"email\"],.wy-control-group .wy-form-full input[type=\"url\"],.wy-control-group .wy-form-full input[type=\"date\"],.wy-control-group .wy-form-full input[type=\"month\"],.wy-control-group .wy-form-full input[type=\"time\"],.wy-control-group .wy-form-full input[type=\"datetime\"],.wy-control-group .wy-form-full input[type=\"datetime-local\"],.wy-control-group .wy-form-full input[type=\"week\"],.wy-control-group .wy-form-full input[type=\"number\"],.wy-control-group .wy-form-full input[type=\"search\"],.wy-control-group .wy-form-full input[type=\"tel\"],.wy-control-group .wy-form-full input[type=\"color\"],.wy-control-group .wy-form-halves input[type=\"text\"],.wy-control-group .wy-form-halves input[type=\"password\"],.wy-control-group .wy-form-halves input[type=\"email\"],.wy-control-group .wy-form-halves input[type=\"url\"],.wy-control-group .wy-form-halves input[type=\"date\"],.wy-control-group .wy-form-halves input[type=\"month\"],.wy-control-group .wy-form-halves input[type=\"time\"],.wy-control-group .wy-form-halves input[type=\"datetime\"],.wy-control-group .wy-form-halves input[type=\"datetime-local\"],.wy-control-group .wy-form-halves input[type=\"week\"],.wy-control-group .wy-form-halves input[type=\"number\"],.wy-control-group .wy-form-halves input[type=\"search\"],.wy-control-group .wy-form-halves input[type=\"tel\"],.wy-control-group .wy-form-halves input[type=\"color\"],.wy-control-group .wy-form-thirds input[type=\"text\"],.wy-control-group .wy-form-thirds input[type=\"password\"],.wy-control-group .wy-form-thirds input[type=\"email\"],.wy-control-group .wy-form-thirds input[type=\"url\"],.wy-control-group .wy-form-thirds input[type=\"date\"],.wy-control-group .wy-form-thirds input[type=\"month\"],.wy-control-group .wy-form-thirds input[type=\"time\"],.wy-control-group .wy-form-thirds input[type=\"datetime\"],.wy-control-group .wy-form-thirds input[type=\"datetime-local\"],.wy-control-group .wy-form-thirds input[type=\"week\"],.wy-control-group .wy-form-thirds input[type=\"number\"],.wy-control-group .wy-form-thirds input[type=\"search\"],.wy-control-group .wy-form-thirds input[type=\"tel\"],.wy-control-group .wy-form-thirds input[type=\"color\"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type=\"text\"],.wy-control-group.fluid-input input[type=\"password\"],.wy-control-group.fluid-input input[type=\"email\"],.wy-control-group.fluid-input input[type=\"url\"],.wy-control-group.fluid-input input[type=\"date\"],.wy-control-group.fluid-input input[type=\"month\"],.wy-control-group.fluid-input input[type=\"time\"],.wy-control-group.fluid-input input[type=\"datetime\"],.wy-control-group.fluid-input input[type=\"datetime-local\"],.wy-control-group.fluid-input input[type=\"week\"],.wy-control-group.fluid-input input[type=\"number\"],.wy-control-group.fluid-input input[type=\"search\"],.wy-control-group.fluid-input input[type=\"tel\"],.wy-control-group.fluid-input input[type=\"color\"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{-webkit-appearance:button;cursor:pointer;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;*overflow:visible}input[type=\"text\"],input[type=\"password\"],input[type=\"email\"],input[type=\"url\"],input[type=\"date\"],input[type=\"month\"],input[type=\"time\"],input[type=\"datetime\"],input[type=\"datetime-local\"],input[type=\"week\"],input[type=\"number\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"color\"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type=\"datetime-local\"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type=\"checkbox\"],input[type=\"radio\"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type=\"search\"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=\"search\"]::-webkit-search-cancel-button,input[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none}input[type=\"text\"]:focus,input[type=\"password\"]:focus,input[type=\"email\"]:focus,input[type=\"url\"]:focus,input[type=\"date\"]:focus,input[type=\"month\"]:focus,input[type=\"time\"]:focus,input[type=\"datetime\"]:focus,input[type=\"datetime-local\"]:focus,input[type=\"week\"]:focus,input[type=\"number\"]:focus,input[type=\"search\"]:focus,input[type=\"tel\"]:focus,input[type=\"color\"]:focus{outline:0;outline:thin dotted \\9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type=\"file\"]:focus,input[type=\"radio\"]:focus,input[type=\"checkbox\"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=\"text\"][disabled],input[type=\"password\"][disabled],input[type=\"email\"][disabled],input[type=\"url\"][disabled],input[type=\"date\"][disabled],input[type=\"month\"][disabled],input[type=\"time\"][disabled],input[type=\"datetime\"][disabled],input[type=\"datetime-local\"][disabled],input[type=\"week\"][disabled],input[type=\"number\"][disabled],input[type=\"search\"][disabled],input[type=\"tel\"][disabled],input[type=\"color\"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type=\"file\"]:focus:invalid:focus,input[type=\"radio\"]:focus:invalid:focus,input[type=\"checkbox\"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=\"text\"],.wy-control-group.wy-control-group-error input[type=\"password\"],.wy-control-group.wy-control-group-error input[type=\"email\"],.wy-control-group.wy-control-group-error input[type=\"url\"],.wy-control-group.wy-control-group-error input[type=\"date\"],.wy-control-group.wy-control-group-error input[type=\"month\"],.wy-control-group.wy-control-group-error input[type=\"time\"],.wy-control-group.wy-control-group-error input[type=\"datetime\"],.wy-control-group.wy-control-group-error input[type=\"datetime-local\"],.wy-control-group.wy-control-group-error input[type=\"week\"],.wy-control-group.wy-control-group-error input[type=\"number\"],.wy-control-group.wy-control-group-error input[type=\"search\"],.wy-control-group.wy-control-group-error input[type=\"tel\"],.wy-control-group.wy-control-group-error input[type=\"color\"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type=\"submit\"]{margin:0.7em 0 0}.wy-form input[type=\"text\"],.wy-form input[type=\"password\"],.wy-form input[type=\"email\"],.wy-form input[type=\"url\"],.wy-form input[type=\"date\"],.wy-form input[type=\"month\"],.wy-form input[type=\"time\"],.wy-form input[type=\"datetime\"],.wy-form input[type=\"datetime-local\"],.wy-form input[type=\"week\"],.wy-form input[type=\"number\"],.wy-form input[type=\"search\"],.wy-form input[type=\"tel\"],.wy-form input[type=\"color\"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type=\"password\"],.wy-form input[type=\"email\"],.wy-form input[type=\"url\"],.wy-form input[type=\"date\"],.wy-form input[type=\"month\"],.wy-form input[type=\"time\"],.wy-form input[type=\"datetime\"],.wy-form input[type=\"datetime-local\"],.wy-form input[type=\"week\"],.wy-form input[type=\"number\"],.wy-form input[type=\"search\"],.wy-form input[type=\"tel\"],.wy-form input[type=\"color\"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden}body{font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:\"Roboto Slab\",\"ff-tisa-web-pro\",\"Georgia\",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:\"Consolas\",\"Monaco\",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:\"Example\";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:\"Consolas\",\"Monaco\",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:\"Consolas\",\"Monaco\",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:\"\"}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980b9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:\"\"}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:\"\"}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:fixed;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:\"Lato\",\"proxima-nova\",\"Helvetica Neue\",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:\"\"}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:\"\\f0c1\";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:\"Roboto Slab\",\"ff-tisa-web-pro\",\"Georgia\",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/static/js/theme.js",
    "content": "$( document ).ready(function() {\n    // Shift nav in mobile when clicking the menu.\n    $(document).on('click', \"[data-toggle='wy-nav-top']\", function() {\n      $(\"[data-toggle='wy-nav-shift']\").toggleClass(\"shift\");\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    // Close menu when you click a link.\n    $(document).on('click', \".wy-menu-vertical .current ul li a\", function() {\n      $(\"[data-toggle='wy-nav-shift']\").removeClass(\"shift\");\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift\");\n    });\n    $(document).on('click', \"[data-toggle='rst-current-version']\", function() {\n      $(\"[data-toggle='rst-versions']\").toggleClass(\"shift-up\");\n    });  \n    // Make tables responsive\n    $(\"table.docutils:not(.field-list)\").wrap(\"<div class='wy-table-responsive'></div>\");\n});\n\nwindow.SphinxRtdTheme = (function (jquery) {\n    var stickyNav = (function () {\n        var navBar,\n            win,\n            stickyNavCssClass = 'stickynav',\n            applyStickNav = function () {\n                if (navBar.height() <= win.height()) {\n                    navBar.addClass(stickyNavCssClass);\n                } else {\n                    navBar.removeClass(stickyNavCssClass);\n                }\n            },\n            enable = function () {\n                applyStickNav();\n                win.on('resize', applyStickNav);\n            },\n            init = function () {\n                navBar = jquery('nav.wy-nav-side:first');\n                win    = jquery(window);\n            };\n        jquery(init);\n        return {\n            enable : enable\n        };\n    }());\n    return {\n        StickyNav : stickyNav\n    };\n}($));\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = css/theme.css\n\n[options]\ntypekit_id = hiw1hhg\nanalytics_id = \nsticky_navigation = False\n"
  },
  {
    "path": "doc/_themes/sphinx_rtd_theme/versions.html",
    "content": "{% if READTHEDOCS %}\n{# Add rst-badge after rst-versions for small badge style. #}\n  <div class=\"rst-versions\" data-toggle=\"rst-versions\" role=\"note\" aria-label=\"versions\">\n    <span class=\"rst-current-version\" data-toggle=\"rst-current-version\">\n      <span class=\"fa fa-book\"> Read the Docs</span>\n      v: {{ current_version }}\n      <span class=\"fa fa-caret-down\"></span>\n    </span>\n    <div class=\"rst-other-versions\">\n      <dl>\n        <dt>Versions</dt>\n        {% for slug, url in versions %}\n          <dd><a href=\"{{ url }}\">{{ slug }}</a></dd>\n        {% endfor %}\n      </dl>\n      <dl>\n        <dt>Downloads</dt>\n        {% for type, url in downloads %}\n          <dd><a href=\"{{ url }}\">{{ type }}</a></dd>\n        {% endfor %}\n      </dl>\n      <dl>\n        <dt>On Read the Docs</dt>\n          <dd>\n            <a href=\"//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}\">Project Home</a>\n          </dd>\n          <dd>\n            <a href=\"//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}\">Builds</a>\n          </dd>\n      </dl>\n      <hr/>\n      Free document hosting provided by <a href=\"http://www.readthedocs.org\">Read the Docs</a>.\n\n    </div>\n  </div>\n{% endif %}\n\n"
  },
  {
    "path": "doc/android-spdy-proxy.rst",
    "content": "SPDY Proxy with Firefox for Android\n===================================\n\nThis document describes how to use SPDY proxy from Android device\nusing Firefox for Android. No root privilege is required. It may be\npossible to use other Web browser/software if they provide the ability\nto specify HTTP proxy. Because we don't use the features only\navailable in latest Android devices, this method works on relatively\nold but still used versions, e.g., Andriod 2.3 series.\n\nSetting up SPDY Proxy\n---------------------\n\nIf you have VPS, then you can setup SPDY proxy there.  You can use\n``shrpx`` with ``-s`` option + Squid as SPDY proxy.  Alternatively,\n`node-spdyproxy <https://github.com/igrigorik/node-spdyproxy/>`_ may\nalso work. If you don't have VPS, but your home internet connection\nhas global IP address which can be accessible from Android device, you\ncan use your home PC as SPDY proxy temporarily for the experiment.\nThe self-signed certificate is OK because we will run ``shrpx`` with\n``-k`` option on Android in this example. Alternatively, you can store\nyour certificate in Android device and specify it using ``--cacert``\noption. If you think these are insecure, obtain valid certificate.\n\nBuilding spdylay library and shrpx\n----------------------------------\n\nFirst Android NDK must be installed on your system.  Refer\n:doc:`package_README` to see how to install NDK. In the following document, We\nuse ``ANDROID_HOME`` environment variable.\n\nTo make it easier to run Android cross-compiler tools (and for the\nsake of this document), include the path to those commands to\n``PATH``::\n\n    $ export PATH=$ANDROID_HOME/toolchain/bin:$PATH\n\nWe need to build dependent libraries: OpenSSL and libevent.\n\nTo configure OpenSSL, use the following script::\n\n    #!/bin/sh\n\n    if [ -z \"$ANDROID_HOME\" ]; then\n        echo 'No $ANDROID_HOME specified.'\n        exit 1\n    fi\n    PREFIX=$ANDROID_HOME/usr/local\n    TOOLCHAIN=$ANDROID_HOME/toolchain\n    PATH=$TOOLCHAIN/bin:$PATH\n\n    export CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-\n    ./Configure --prefix=$PREFIX android\n\nThen run ``make install`` to build and install library.\n\nFor libevent, use the following script to configure::\n\n    #!/bin/sh\n\n    if [ -z \"$ANDROID_HOME\" ]; then\n        echo 'No $ANDROID_HOME specified.'\n        exit 1\n    fi\n    PREFIX=$ANDROID_HOME/usr/local\n    TOOLCHAIN=$ANDROID_HOME/toolchain\n    PATH=$TOOLCHAIN/bin:$PATH\n\n    ./configure \\\n        --host=arm-linux-androideabi \\\n        --build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE` \\\n        --prefix=$PREFIX \\\n        --disable-shared \\\n        --enable-static \\\n        CPPFLAGS=-I$PREFIX/include \\\n        LDFLAGS=-L$PREFIX/lib\n\nThen run ``make install`` to\nbuild and install library.\n\nTo build spdylay, use ``android-config`` to configure and\n``android-make`` to build as described in :doc:`package_README`.\n\nIf all went well, ``shrpx`` binary is created in src directory.  Strip\ndebugging information from the binary using the following command::\n\n    $ arm-linux-androideabi-strip src/shrpx\n\nSetup shrpx on Android device\n-----------------------------\n\nThere may be several ways to run ``shrpx`` on Android. I describe the\nway to use `Android Terminal Emulator\n<https://github.com/jackpal/Android-Terminal-Emulator>`_.  It can be\ninstalled from Google Play. Copy ``shrpx`` binary to the location\nwhere the Android-Terminal-Emulator is installed (In case of my phone,\nit is ``/data/data/jackpal.androidterm``) and give the executable\npermission to ``shrpx`` using ``chmod``::\n\n    $ chmod 755 shrpx\n\nThen run ``shrpx`` in client-mode like this::\n\n    $ ./shrpx -k -p -f localhost,8000 -b SPDY-PROXY-ADDR,SPDY-PROXY-PORT\n\nSubstitute ``SPDY-PROXY-ADDR`` and ``SPDY-PROXY-PORT`` with the SPDY\nproxy address and port you have setup respectively. The ``-k`` option\ntells ``shrpx`` not to complain the self-signed certificate for SPDY\nproxy. The ``-p`` option makes ``shrpx`` run so called client mode.\nIn that mode, ``shrpx`` acts like ordinary HTTP forward proxy in\nfrontend connection, it forwards the requests from the client to\nbackend in encrypted SPDY connection. The ``-f`` option specify the\naddress and port ``shrpx`` listens to. In this setup, the web browser\nshould be setup to use HTTP proxy localhost:8000. The ``-b`` option\nspecify the SPDY proxy address and port ``shrpx`` forwards the\nrequests from the client. The configuration looks like this::\n\n\n    +----Android------------------------+          +---SPDY-Proxy------+\n    | [Firefox] <-- HTTP --> [shrpx] <--=-- SPDY --=-->[shrpx,squid]<--=-- SPDY --> ...\n    +-----------------------------------+          +-------------------+   HTTP\n\nWith the above command-line option, ``shrpx`` only opens 1 connection\nto SPDY proxy. Of course, Firefox will use multiple connections to\nneighboring ``shrpx``. ``shrpx`` coalesces all the requests in 1\nbackend connection, that is the benefit SPDY proxy brings in.\n\nSetup Firefox to use SPDY proxy\n-------------------------------\n\nIf you have not installed, Firefox for Android, install it.  Enter\n``about:config`` in URL bar in Firefox and locate proxy\nsettings. Setup those values like this::\n\n    network.proxy.http = localhost\n    network.proxy.http_port = 8000\n    network.proxy.ssl = localhost\n    network.proxy.ssl_port = 8000\n    network.proxy.type = 1\n\nYou also need to tweak the following settings to increase in-flight\nrequests to circumvent latency::\n\n    network.http.max-persistent-connections-per-proxy\n    network.http.max-connections\n    network.http.max-connections-per-server\n\nSince ``shrpx`` handles maximum 100 concurrent streams, it is\nreasonable to set\n``network.http.max-persistent-connections-per-proxy`` to ``100``.\n\nNow borwse the sites with Firefox. The all HTTP requests are now sent\nvia internal ``shrpx`` to SPDY proxy in 1 connection. SPDY proxy will\nget resources on behalf of the client and sent back the response.\n"
  },
  {
    "path": "doc/apiref-header.rst",
    "content": "API Reference\n=============\n\nIncludes\n--------\n\nTo use the public APIs, include ``spdylay/spdylay.h``::\n\n    #include <spdylay/spdylay.h>\n\nRemarks\n-------\n\nDo not call `spdylay_session_send`, `spdylay_session_recv` or\n`spdylay_session_mem_recv` from the spdylay callback functions\ndirectly or indirectly. It will lead to the crash. You can submit\nrequests or frames in the callbacks then call `spdylay_session_send`,\n`spdylay_session_recv` or `spdylay_session_mem_recv` outside of the\ncallbacks.\n"
  },
  {
    "path": "doc/conf.py.in",
    "content": "# -*- coding: utf-8 -*-\n# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n#\n# Spdylay documentation build configuration file, created by\n# sphinx-quickstart on Sun Mar 11 22:57:49 2012.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\n\n# -- General configuration -----------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = []\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'Spdylay'\ncopyright = u'2012, 2014, Tatsuhiro Tsujikawa'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '@PACKAGE_VERSION@'\n# The full version, including alpha/beta/rc tags.\nrelease = '@PACKAGE_VERSION@'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['manual', 'README.rst', '*-header.rst']\n\n# The reST default role (used for this markup: `text`) to use for all documents.\ndefault_role = 'c:func'\nprimary_domain = 'c'\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The default language to highlight source code in. The default is 'python'.\nhighlight_language = 'c'\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\nhtml_theme_path = ['_themes']\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n#html_static_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\nhtml_sidebars = {\n    '**': ['menu.html', 'localtoc.html', 'relations.html', 'sourcelink.html',\n           'searchbox.html']\n    }\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'Spdylaydoc'\n\n\n# -- Options for LaTeX output --------------------------------------------------\n\n# The paper size ('letter' or 'a4').\n#latex_paper_size = 'letter'\n\n# The font size ('10pt', '11pt' or '12pt').\n#latex_font_size = '10pt'\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'Spdylay.tex', u'Spdylay Documentation',\n   u'Tatsuhiro Tsujikawa', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Additional stuff for the LaTeX preamble.\n#latex_preamble = ''\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output --------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'spdylay', u'Spdylay Documentation',\n     [u'Tatsuhiro Tsujikawa'], 1)\n]\n"
  },
  {
    "path": "doc/index.rst",
    "content": ".. Spdylay documentation master file, created by\n   sphinx-quickstart on Sun Mar 11 22:57:49 2012.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nSpdylay - SPDY C Library\n========================\n\nThis is an experimental implementation of Google's SPDY protocol in C.\nThis library provides SPDY version 2, 3 and 3.1 framing layer\nimplementation.  It does not perform any I/O operations.  When the\nlibrary needs them, it calls the callback functions provided by the\napplication. It also does not include any event polling mechanism, so\nthe application can freely choose the way of handling events. This\nlibrary code does not depend on any particular SSL library (except for\nexample programs which depend on OpenSSL 1.0.1 or later).\n\nThis project also develops SPDY client, server and proxy on top of\nSpdylay library.\n\nContents:\n\n.. toctree::\n   :maxdepth: 2\n\n   package_README\n   apiref\n   python\n   android-spdy-proxy\n   Download <https://github.com/tatsuhiro-t/spdylay/releases>\n   Old download <http://sourceforge.net/projects/spdylay/files/>\n   Source <https://github.com/tatsuhiro-t/spdylay>\n   Issues <https://github.com/tatsuhiro-t/spdylay/issues>\n\nResources\n---------\n\n* http://www.chromium.org/spdy\n"
  },
  {
    "path": "doc/make.bat",
    "content": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUILDDIR=_build\nset ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .\nif NOT \"%PAPER%\" == \"\" (\n\tset ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%\n)\n\nif \"%1\" == \"\" goto help\n\nif \"%1\" == \"help\" (\n\t:help\n\techo.Please use `make ^<target^>` where ^<target^> is one of\n\techo.  html       to make standalone HTML files\n\techo.  dirhtml    to make HTML files named index.html in directories\n\techo.  singlehtml to make a single large HTML file\n\techo.  pickle     to make pickle files\n\techo.  json       to make JSON files\n\techo.  htmlhelp   to make HTML files and a HTML help project\n\techo.  qthelp     to make HTML files and a qthelp project\n\techo.  devhelp    to make HTML files and a Devhelp project\n\techo.  epub       to make an epub\n\techo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\n\techo.  text       to make text files\n\techo.  man        to make manual pages\n\techo.  changes    to make an overview over all changed/added/deprecated items\n\techo.  linkcheck  to check all external links for integrity\n\techo.  doctest    to run all doctests embedded in the documentation if enabled\n\tgoto end\n)\n\nif \"%1\" == \"clean\" (\n\tfor /d %%i in (%BUILDDIR%\\*) do rmdir /q /s %%i\n\tdel /q /s %BUILDDIR%\\*\n\tgoto end\n)\n\nif \"%1\" == \"html\" (\n\t%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/html.\n\tgoto end\n)\n\nif \"%1\" == \"dirhtml\" (\n\t%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.\n\tgoto end\n)\n\nif \"%1\" == \"singlehtml\" (\n\t%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.\n\tgoto end\n)\n\nif \"%1\" == \"pickle\" (\n\t%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the pickle files.\n\tgoto end\n)\n\nif \"%1\" == \"json\" (\n\t%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can process the JSON files.\n\tgoto end\n)\n\nif \"%1\" == \"htmlhelp\" (\n\t%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run HTML Help Workshop with the ^\n.hhp project file in %BUILDDIR%/htmlhelp.\n\tgoto end\n)\n\nif \"%1\" == \"qthelp\" (\n\t%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; now you can run \"qcollectiongenerator\" with the ^\n.qhcp project file in %BUILDDIR%/qthelp, like this:\n\techo.^> qcollectiongenerator %BUILDDIR%\\qthelp\\Spdylay.qhcp\n\techo.To view the help file:\n\techo.^> assistant -collectionFile %BUILDDIR%\\qthelp\\Spdylay.ghc\n\tgoto end\n)\n\nif \"%1\" == \"devhelp\" (\n\t%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished.\n\tgoto end\n)\n\nif \"%1\" == \"epub\" (\n\t%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The epub file is in %BUILDDIR%/epub.\n\tgoto end\n)\n\nif \"%1\" == \"latex\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished; the LaTeX files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"text\" (\n\t%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The text files are in %BUILDDIR%/text.\n\tgoto end\n)\n\nif \"%1\" == \"man\" (\n\t%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Build finished. The manual pages are in %BUILDDIR%/man.\n\tgoto end\n)\n\nif \"%1\" == \"changes\" (\n\t%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.The overview file is in %BUILDDIR%/changes.\n\tgoto end\n)\n\nif \"%1\" == \"linkcheck\" (\n\t%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Link check complete; look for any errors in the above output ^\nor in %BUILDDIR%/linkcheck/output.txt.\n\tgoto end\n)\n\nif \"%1\" == \"doctest\" (\n\t%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest\n\tif errorlevel 1 exit /b 1\n\techo.\n\techo.Testing of doctests in the sources finished, look at the ^\nresults in %BUILDDIR%/doctest/output.txt.\n\tgoto end\n)\n\n:end\n"
  },
  {
    "path": "doc/mkapiref.py",
    "content": "#!/usr/bin/env python\n# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n# Generates API reference from C source code.\nimport re, sys, argparse\n\nclass FunctionDoc:\n    def __init__(self, name, content, domain):\n        self.name = name\n        self.content = content\n        self.domain = domain\n\n    def write(self, out):\n        print '''.. {}:: {}'''.format(self.domain, self.name)\n        print ''\n        for line in self.content:\n            print '    {}'.format(line)\n\nclass StructDoc:\n    def __init__(self, name, content, members, member_domain):\n        self.name = name\n        self.content = content\n        self.members = members\n        self.member_domain = member_domain\n\n    def write(self, out):\n        if self.name:\n            print '''.. type:: {}'''.format(self.name)\n            print ''\n            for line in self.content:\n                print '    {}'.format(line)\n            print ''\n            for name, content in self.members:\n                print '''    .. {}:: {}'''.format(self.member_domain, name)\n                print ''\n                for line in content:\n                    print '''        {}'''.format(line)\n            print ''\n\nclass MacroDoc:\n    def __init__(self, name, content):\n        self.name = name\n        self.content = content\n\n    def write(self, out):\n        print '''.. macro:: {}'''.format(self.name)\n        print ''\n        for line in self.content:\n            print '    {}'.format(line)\n\ndef make_api_ref(infiles):\n    macros = []\n    enums = []\n    types = []\n    functions = []\n    for infile in infiles:\n        while True:\n            line = infile.readline()\n            if not line:\n                break\n            elif line == '/**\\n':\n                line = infile.readline()\n                doctype = line.split()[1]\n                if doctype == '@function':\n                    functions.append(process_function('function', infile))\n                elif doctype == '@functypedef':\n                    types.append(process_function('type', infile))\n                elif doctype == '@struct' or doctype == '@union':\n                    types.append(process_struct(infile))\n                elif doctype == '@enum':\n                    enums.append(process_enum(infile))\n                elif doctype == '@macro':\n                    macros.append(process_macro(infile))\n    alldocs = [('Macros', macros),\n               ('Enums', enums),\n               ('Types (structs, unions and typedefs)', types),\n               ('Functions', functions)]\n    for title, docs in alldocs:\n        if not docs:\n            continue\n        print title\n        print '-'*len(title)\n        for doc in docs:\n            doc.write(sys.stdout)\n            print ''\n        print ''\n\ndef process_macro(infile):\n    content = read_content(infile)\n    line = infile.readline()\n    macro_name = line.split()[1]\n    return MacroDoc(macro_name, content)\n\ndef process_enum(infile):\n    members = []\n    enum_name = None\n    content = read_content(infile)\n    while True:\n        line = infile.readline()\n        if not line:\n            break\n        elif re.match(r'\\s*/\\*\\*\\n', line):\n            member_content = read_content(infile)\n            line = infile.readline()\n            items = line.split()\n            member_name = items[0]\n            if len(items) >= 3:\n                member_content.insert(0, '(``{}``) '\\\n                                          .format(items[2].rstrip(',')))\n            members.append((member_name, member_content))\n        elif line.startswith('}'):\n            enum_name = line.rstrip().split()[1]\n            enum_name = re.sub(r';$', '', enum_name)\n            break\n    return StructDoc(enum_name, content, members, 'macro')\n\ndef process_struct(infile):\n    members = []\n    struct_name = None\n    content = read_content(infile)\n    while True:\n        line = infile.readline()\n        if not line:\n            break\n        elif re.match(r'\\s*/\\*\\*\\n', line):\n            member_content = read_content(infile)\n            line = infile.readline()\n            member_name = line.rstrip().rstrip(';')\n            members.append((member_name, member_content))\n        elif line.startswith('}') or\\\n                (line.startswith('typedef ') and line.endswith(';\\n')):\n            if line.startswith('}'):\n                index = 1\n            else:\n                index = 3\n            struct_name = line.rstrip().split()[index]\n            struct_name = re.sub(r';$', '', struct_name)\n            break\n    return StructDoc(struct_name, content, members, 'member')\n\ndef process_function(domain, infile):\n    content = read_content(infile)\n    func_proto = []\n    while True:\n        line = infile.readline()\n        if not line:\n            break\n        elif line == '\\n':\n            break\n        else:\n            func_proto.append(line)\n    func_proto = ''.join(func_proto)\n    func_proto = re.sub(r';\\n$', '', func_proto)\n    func_proto = re.sub(r'\\s+', ' ', func_proto)\n    return FunctionDoc(func_proto, content, domain)\n\ndef read_content(infile):\n    content = []\n    while True:\n        line = infile.readline()\n        if not line:\n            break\n        if re.match(r'\\s*\\*/\\n', line):\n            break\n        else:\n            content.append(transform_content(line.rstrip()))\n    return content\n\ndef arg_repl(matchobj):\n    return '*{}*'.format(matchobj.group(1).replace('*', '\\\\*'))\n\ndef transform_content(content):\n    content = re.sub(r'^\\s+\\* ?', '', content)\n    content = re.sub(r'\\|([^\\s|]+)\\|', arg_repl, content)\n    content = re.sub(r':enum:', ':macro:', content)\n    return content\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser(description=\"Generate API reference\")\n    parser.add_argument('--header', type=argparse.FileType('rb', 0),\n                        help='header inserted at the top of the page')\n    parser.add_argument('files', nargs='+', type=argparse.FileType('rb', 0),\n                        help='source file')\n    args = parser.parse_args()\n    if args.header:\n        print args.header.read()\n    for infile in args.files:\n        make_api_ref(args.files)\n"
  },
  {
    "path": "doc/package_README.rst",
    "content": ".. include:: ../README.rst\n"
  },
  {
    "path": "doc/python.rst",
    "content": "Python-spdylay - Spdylay Python Extension Module\n================================================\n\n.. py:module:: spdylay\n\nPython-spdylay is the Python extension module of Spdylay SPDY C\nlibrary.\n\nBuild\n-----\n\nTo generate C source code from ``spdylay.pyx``, run ``cython``::\n\n    $ cython spdylay.pyx\n\nTo build extension, run ``setup.py``::\n\n    $ python setup.py build_ext\n\nSession Objects\n---------------\n\n.. py:class:: Session(side, version, config=None, send_cb=None, recv_cb=None, on_ctrl_recv_cb=None, on_invalid_ctrl_recv_cb=None, on_data_chunk_recv_cb=None, on_data_recv_cb=None, before_ctrl_send_cb=None, on_ctrl_send_cb=None, on_ctrl_not_send_cb=None, on_data_send_cb=None, on_stream_close_cb=None, on_request_recv_cb=None, on_ctrl_recv_parse_error_cb=None, on_unknown_ctrl_recv_cb=None, user_data=None)\n\n    This is the class to hold the resources needed for a SPDY session.\n    Sending and receiving SPDY frames are done using the methods of\n    this class.\n\n    The *side* specifies server or client. Use one of the following:\n\n    .. py:data:: CLIENT\n\n        Indicates client.\n\n    .. py:data:: SERVER\n\n        Indicates server.\n\n    The *version* specifies SPDY protocol version. Use of the following:\n\n    .. py:data:: PROTO_SPDY2\n\n        Indicates SPDY/2.\n\n    .. py:data:: PROTO_SPDY3\n\n        Indicates SPDY/3.\n\n    The *user_data* specifies opaque object tied to this object. It\n    can be accessed through :py:attr:`user_data` attribute.\n\n    The *recv_cb* specifies callback function (callable) invoked when\n    the object wants to receive data from the remote peer. The\n    signature of this callback is:\n\n    .. py:function:: recv_cb(session, length)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback.  The implementation of this function must read at\n        most *length* bytes of bytestring and return it. If it cannot\n        read any single byte without blocking, it must return empty\n        bytestring or ``None``. If it gets EOF before it reads any\n        single byte, it must raise :py:class:`EOFError`. For other\n        errors, it must raise :py:class:`CallbackFailureError`.\n\n    The *send_cb* specifies callback function (callable) invoked when\n    session wants to send data to the remote peer. The signature of\n    this callback is:\n\n    .. py:function:: send_cb(session, data)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *data* is the bytestring to send. The\n        implementation of this function will send all or part of\n        *data*. It must return the number of bytes sent if it\n        succeeds. If it cannot send any single byte without blocking,\n        it must return 0 or ``None``. For other errors, it must return\n        :py:class:`CallbackFailureError`.\n\n    The *on_ctrl_recv_cb* specifies callback function (callable)\n    invoked when a control frame is received.\n\n    .. py:function:: on_ctrl_recv_cb(session, frame)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *frame* is the received control\n        frame. ``frame.frame_type`` tells the type of frame. See\n        `Frame Types`_ for the details. Once the frame type is\n        identified, access attribute of the *frame* to get\n        information.\n\n    The *on_invalid_ctrl_recv_cb* specifies callback function\n    (callable) invoked when an invalid control frame is received.\n\n    .. py:function:: on_invalid_ctrl_recv_cb(session, frame, status_code)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *frame* is the received control\n        frame. ``frame.frame_type`` tells the type of frame. See\n        `Frame Types`_ for the details. Once the frame type is\n        identified, access attribute of the *frame* to get\n        information.  The *status_code* is one of the `Stream Status\n        Codes`_ and indicates the error. When this callback function\n        is invoked, either RST_STREAM or GOAWAY will be sent.\n\n    The *on_data_chunk_recv_cb* specifies callback function (callable)\n    invoked when a chunk of data in DATA frame is received.\n\n    .. py:function:: on_data_chunk_recv_cb(session, flags, stream_id, data)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *stream_id* is the stream ID this DATA frame\n        belongs to. The *flags* is the flags of DATA frame which this\n        data chunk is contained. ``(flags & DATA_FLAG_FIN) != 0`` does\n        not necessarily mean this chunk of data is the last one in the\n        stream. You should use :py:func:`on_data_recv_cb` to know all\n        data frames are received. The *data* is the bytestring of\n        received data.\n\n    The *on_data_recv_cb* specifies callback function (callable)\n    invoked when DATA frame is received.\n\n    .. py:function:: on_data_recv_cb(session, flags, stream_id, length)\n\n        The actual data it contains are received by\n        :py:func:`on_data_chunk_recv_cb()`.\n\n    The *before_ctrl_send_cb* specifies callback function (callable)\n    invoked before the control frame is sent.\n\n    .. py:function:: before_ctrl_send_cb(session, frame)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *frame* is the control frame to be\n        sent. ``frame.frame_type`` tells the type of frame. See `Frame\n        Types`_ for the details. Once the frame type is identified,\n        access attribute of the *frame* to get information.\n\n    The *on_ctrl_send_cb* specifies callback function (callable)\n    invoked after the control frame is sent.\n\n    .. py:function:: on_ctrl_send_cb(session, frame)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *frame* is the control frame to be\n        sent. ``frame.frame_type`` tells the type of frame. See `Frame\n        Types`_ for the details. Once the frame type is identified,\n        access attribute of the *frame* to get information.\n\n    The *on_ctrl_not_send_cb* specifies callback function (callable)\n    after the control frame is not sent because of the error.\n\n    .. py:function:: on_ctrl_not_send_cb(session, frame, error_code)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *frame* is the received control\n        frame. ``frame.frame_type`` tells the type of frame. See\n        `Frame Types`_ for the details. Once the frame type is\n        identified, access attribute of the *frame* to get\n        information.  The *error_code* is one of the `Error Codes`_\n        and indicates the error.\n\n    The *on_data_send_cb* specifies callback function (callable)\n    invoked after DATA frame is sent.\n\n    .. py:function:: on_data_send_cb(session, flags, stream_id, length)\n\n    The *on_stream_close_cb* specifies callback function (callable)\n    invoked when the stream is closed.\n\n    .. py:function:: on_stream_close_cb(session, stream_id, status_code)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *stream_id* indicates the stream ID.  The reason\n        of closure is indicated by the *status_code*. See `Stream\n        Status Codes`_ for the details. The stream_user_data, which\n        was specified in :py:meth:`submit_request()` or\n        :py:meth:`submit_syn_stream()`, is still available in this\n        function.\n\n    The *on_request_recv_cb* specifies callback function (callable)\n    invoked when the request from the remote peer is received. In\n    other words, the frame with FIN flag set is received. In HTTP,\n    this means HTTP request, including request body, is fully\n    received.\n\n    .. py:function:: on_request_recv_cb(session, stream_id)\n\n        The *session* is the :py:class:`Session` object invoking the\n        callback. The *stream_id* indicates the stream ID.\n\n    The *on_ctrl_recv_parse_error_cb* specifies callback function\n    (callable) invoked when the received control frame octets could\n    not be parsed correctly.\n\n    .. py:function:: on_ctrl_recv_parse_error_cb(session, type, head, payload, error_code)\n\n        The *type* indicates the type of received control frame. The\n        *head* is the bytestring of control frame header. The\n        *payload* is the bytestring of data portion of the received\n        frame. The *error_code* is one of the error code defined in\n        `Error Codes`_ and indicates the error.\n\n    The *on_unknown_ctrl_recv_cb* specifies callback function\n    (callable) invoked when the received control frame type is\n    unknown.\n\n    .. py:function:: on_unknown_ctrl_recv_cb(session, head, payload)\n\n        The *head* is the bytestring of control frame header. The\n        *payload* is the bytestring of data portion of the received\n        frame.\n\n    The :py:class:`InvalidArgumentError` will be raised if the given\n    argument is invalid.  The :py:class:`UnsupportedVersionError` will\n    be raised if the *version* is not supported. The\n    :py:class:`ZlibError` will be raised if initialization of zlib\n    failed.\n\n.. py:attribute:: Session.user_data\n\n    The object passed in the constructor as *user_data* argument.\n    This attribute is read-only.\n\n.. py:method:: Session.send()\n\n    Sends pending frames to the remote peer.  This method retrieves\n    the highest prioritized frame from the outbound queue and sends it\n    to the remote peer. It does this as many as possible until the\n    user callback :py:func:`send_cb` returns 0 or ``None`` or the\n    outbound queue becomes empty. This method calls several callback\n    functions which are passed when initializing the session.  See\n    :func:`spdylay_session_send` about the callback functions invoked\n    from this method.\n\n    The :py:class:`CallbackFailureError` will be raised if the\n    callback function failed.\n\n.. py:method:: Session.recv(data=None)\n\n    Receives frames from the remote peer.  This method receives as\n    many frames as possible until the user callback :py:func:`recv_cb`\n    returns empty bytestring or ``None``. This function calls several\n    callback functions which are passed when initializing the session.\n    See :func:`spdylay_session_recv` about the callback functions\n    invoked from this method. If data is ``None``, this method will\n    invoke :py:func:`recv_cb` callback function to receive incoming\n    data.  If data is not ``None``, it must be a bytestring and this\n    method uses it as the incoming data and does not call\n    :py:func:`recv_cb` callback function.\n\n    The :py:class:`EOFError` will be raised if the remote peer did\n    shutdown on the connection. The :py:class:`CallbackFailureError`\n    will be raised if the callback function failed.\n\n.. py:method:: Session.resume_data(stream_id)\n\n    Puts back previously deferred DATA frame in the stream *stream_id*\n    to the outbound queue.\n\n    This method returns ``True`` if it succeeds, or ``False``.  This\n    method will fail if the stream does not exist or no deferred data\n    exist.\n\n.. py:method:: Session.want_read()\n\n    Returns ``True`` if session wants to receive data from the\n    remote peer.\n\n    If both :py:meth:`want_read()` and :py:meth:`want_write()` return\n    ``False``, the application should drop the connection.\n\n.. py:method:: Session.want_write()\n\n    Returns ``True`` if session wants to send data to the remote peer.\n\n    If both :py:meth:`want_read()` and :py:meth:`want_write()` return\n    ``False``, the application should drop the connection.\n\n.. py:method:: Session.get_stream_user_data(stream_id)\n\n    Returns stream_user_data for the stream *stream_id*. The\n    stream_user_data is provided by :py:meth:`submit_request()` or\n    :py:meth:`submit_syn_stream()`. If the stream is initiated by the\n    remote endpoint, stream_user_data is always ``None``. If the\n    stream is initiated by the local endpoint and ``None`` is given in\n    :py:meth:`submit_request()` or :py:meth:`submit_syn_stream()`,\n    then this function returns ``None``. If the stream does not exist,\n    this function returns ``None``.\n\n.. py:method:: Session.get_outbound_queue_size()\n\n    Returns the number of frames in the outbound queue. This does not\n    include the deferred DATA frames.\n\n.. py:method:: Session.get_pri_lowest()\n\n    Returns lowest priority value for the session.\n\n.. py:method:: Session.fail_session(status_code)\n\n    Submits GOAWAY frame. The status code *status_code* is ignored if\n    the protocol version is :py:const:`PROTO_SPDY2`.\n\n    This method should be called when the connection should be\n    terminated after sending GOAWAY. If the remaining streams should\n    be processed after GOAWAY, use :py:meth:`submit_goaway()` instead.\n\n.. py:method:: Session.submit_request(pri, nv, data_prd=None, stream_user_data=None)\n\n    Submits SYN_STREAM frame and optionally one or more DATA frames.\n\n    The *pri* is priority of this request. ``0`` is the highest\n    priority value. Use :py:meth:`get_pri_lowest()` to know the lowest\n    priority value for this session.\n\n    The *nv* is a list containing the name/value pairs.  The each\n    element is a pair of unicode strings: name and value (e.g.,\n    ``(u'host', u'localhost')``).\n\n    The *nv* must include following name/value pairs:\n\n    ``:method``\n        HTTP method (e.g., ``GET``, ``POST``, ``HEAD``, etc)\n    ``:scheme``\n        URI scheme (e.g., ``https``)\n    ``:path``\n        Absolute path and parameters of this request (e.g., ``/foo``,\n        ``/foo;bar;haz?h=j&y=123``)\n    ``:version``\n        HTTP version (e.g., ``HTTP/1.1``)\n    ``:host``\n        The hostport portion of the URI for this request (e.g.,\n        ``example.org:443``). This is the same as the HTTP “Host”\n        header field.\n\n    If the session is initialized with the version\n    :py:const:`PROTO_SPDY2`, the above names are translated to\n    ``method``, ``scheme``, ``url``, ``version`` and ``host``\n    respectively.\n\n    The names in *nv* will be lower-cased when they are sent.\n\n    If *data_prd* is not ``None``, it provides data which will be sent\n    in subsequent DATA frames. In this case, a method that allows\n    request message bodies\n    (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must\n    be specified with ``:method`` key in nv (e.g. ``POST``).  The type\n    of *data_prd* is expected to be :py:class:`DataProvider`. If\n    *data_prd* is ``None``, SYN_STREAM have FLAG_FIN set.\n\n    .. note::\n\n         This method does not increase reference count of *data_prd*,\n         so the application must hold the reference to it until the\n         stream is closed.\n\n    The *stream_user_data* is data associated to the stream opened by\n    this request and can be an arbitrary object, which can be\n    retrieved later by :py:meth:`get_stream_user_data()`.\n\n    Since the library reorders the frames and tries to send the\n    highest prioritized one first and the SPDY specification requires\n    the stream ID must be strictly increasing, the stream ID of this\n    request cannot be known until it is about to sent. To know the\n    stream ID of the request, the application can use\n    :py:func:`before_ctrl_send_cb`. This callback is called just\n    before the frame is sent. For SYN_STREAM frame, the argument frame\n    has the stream ID assigned. Also since the stream is already\n    opened, :py:meth:`get_stream_user_data()` can be used to get\n    stream_user_data to identify which SYN_STREAM we are processing.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *pri*\n    is invalid; or the *nv* includes empty name or ``None`` value.\n\n.. py:method:: Session.submit_response(stream_id, nv, data_prd=None)\n\n    Submits SYN_REPLY frame and optionally one or more DATA frames\n    against the stream *stream_id*.\n\n    The *nv* is a list containing the name/value pairs.  The each\n    element is a pair of unicode strings: name and value (e.g.,\n    ``(u'host', u'localhost')``).\n\n    The *nv* must include following name/value pairs:\n\n    ``:status``\n        HTTP status code (e.g., ``200`` or ``200 OK``)\n    ``:version``\n        HTTP response version (e.g., ``HTTP/1.1``)\n\n    If the session is initialized with the version\n    :py:const:`PROTO_SPDY2`, the above names are translated to\n    ``status`` and ``version`` respectively.\n\n    The names in *nv* will be lower-cased when they are sent.\n\n    If *data_prd* is not ``None``, it provides data which will be sent\n    in subsequent DATA frames. The type of *data_prd* is expected to\n    be :py:class:`DataProvider`.  If *data_prd* is ``None``, SYN_REPLY\n    have FLAG_FIN set.\n\n    .. note::\n\n         This method does not increase reference count of *data_prd*,\n         so the application must hold the reference to it until the\n         stream is closed.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *nv*\n    includes empty name or ``None`` value.\n\n.. py:method:: Session.submit_syn_stream(flags, pri, nv, assoc_stream_id=0, stream_user_data=None)\n\n    Submits SYN_STREAM frame. The *flags* is bitwise OR of the\n    following values:\n\n    * :py:const:`CTRL_FLAG_FIN`\n    * :py:const:`CTRL_FLAG_UNIDIRECTIONAL`\n\n    If *flags* includes :py:const:`CTRL_FLAG_FIN`, this frame has\n    FLAG_FIN flag set.\n\n    The *assoc_stream_id* is used for server-push. Specify 0 if this\n    stream is not server-push. If session is initialized for client\n    use, *assoc_stream_id* is ignored.\n\n    The *pri* is priority of this request. ``0`` is the highest\n    priority value. Use :py:meth:`get_pri_lowest()` to know the lowest\n    priority value for this session.\n\n    The *nv* is a list containing the name/value pairs.  The each\n    element is a pair of unicode strings: name and value (e.g.,\n    ``(u'host', u'localhost')``).\n\n    The names in *nv* will be lower-cased when they are sent.\n\n    The *stream_user_data* is data associated to the stream opened by\n    this request and can be an arbitrary object, which can be\n    retrieved later by :py:meth:`get_stream_user_data()`.\n\n    This function is low-level in a sense that the application code\n    can specify flags and the Associated-To-Stream-ID directly. For\n    usual HTTP request, :py:meth:`submit_request()` is useful.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *pri*\n    is invalid; or the *assoc_stream_id* is invalid; or the *nv*\n    includes empty name or ``None`` value.\n\n.. py:method:: Session.submit_syn_reply(flags, stream_id, nv)\n\n    Submits SYN_REPLY frame. The *flags* is bitwise OR of the\n    following values:\n\n    * :py:const:`CTRL_FLAG_FIN`\n\n    If *flags* includes :py:const:`CTRL_FLAG_FIN`, this frame has\n    FLAG_FIN flag set.\n\n    The stream which this frame belongs to is given in the\n    *stream_id*. The *nv* is the name/value pairs in this frame.\n\n    The *nv* is a list containing the name/value pairs.  The each\n    element is a pair of unicode strings: name and value (e.g.,\n    ``(u'host', u'localhost')``).\n\n    The names in *nv* will be lower-cased when they are sent.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *nv*\n    includes empty name or ``None`` value.\n\n.. py:method:: Session.submit_headers(flags, stream_id, nv)\n\n    Submits HEADERS frame. The *flags* is bitwise OR of the following\n    values:\n\n    * :py:const:`CTRL_FLAG_FIN`\n\n    If *flags* includes :py:const:`CTRL_FLAG_FIN`, this frame has\n    FLAG_FIN flag set.\n\n    The stream which this frame belongs to is given in the\n    *stream_id*. The *nv* is the name/value pairs in this frame.\n\n    The *nv* is a list containing the name/value pairs.  The each\n    element is a pair of unicode strings: name and value (e.g.,\n    ``(u'host', u'localhost')``).\n\n    The names in *nv* will be lower-cased when they are sent.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *nv*\n    includes empty name or ``None`` value.\n\n.. py:method:: Session.submit_data(stream_id, flags, data_prd)\n\n    Submits one or more DATA frames to the stream *stream_id*. The\n    data to be sent are provided by *data_prd*.  The type of\n    *data_prd* is expected to be :py:class:`DataProvider`. If *flags*\n    contains :py:const:`DATA_FLAG_FIN`, the last DATA frame has\n    FLAG_FIN set.\n\n    .. note::\n\n         This method does not increase reference count of *data_prd*,\n         so the application must hold the reference to it until the\n         stream is closed.\n\n.. py:method:: Session.submit_rst_stream(stream_id, status_code)\n\n    Submits RST_STREAM frame to cancel/reject the stream *stream_id*\n    with the status code *status_code*. See `Stream Status Codes`_ for\n    available status codes.\n\n.. py:method:: Session.submit_ping()\n\n    Submits PING frame.\n\n.. py:method:: Session.submit_goaway(status_code)\n\n    Submits GOAWAY frame. The status code *status_code* is ignored if\n    the protocol version is :py:const:`PROTO_SPDY2`. See `GOAWAY\n    Status Codes`_ for available status codes.\n\n.. py:method:: Session.submit_settings(flags, iv)\n\n    Stores local settings and submits SETTINGS frame. The *flags* is\n    bitwise OR of the values described in `SETTINGS Frame Flags`_.\n\n    The *iv* is a list of tuple ``(settings_id, flag, value)``.  For\n    settings_id, see `SETTINGS IDs`_. For flag, see `SETTINGS ID\n    Flags`_.\n\n    The :py:class:`InvalidArgumentError` will be raised if the *iv*\n    contains duplicate settings ID or invalid value.\n\n.. py:method:: Session.submit_window_update(stream_id, delta_window_size)\n\n    Submits WINDOW_UPDATE frame. The effective range of the\n    *delta_window_size* is ``[1, (1 << 31)-1]``, inclusive. But the\n    application must be responsible to keep the resulting window\n    ``size <= (1 << 31)-1``.\n\n    The :py:class:`InvalidArgumentError` will be raised if the\n    *delta_window_size* is 0 or negative. The\n    :py:class:`StreamClosedError` will be raised if the stream is\n    already closed or does not exist.\n\nHelper Functions\n----------------\n\n.. py:function:: get_npn_protocols()\n\n    Returns SPDY version strings which can be directly passed to\n    ``ssl.SSLContext.set_npn_protocols()``. Please note that the\n    returned list only includes SPDY version strings this library\n    supports. If the application intends to support other fallback\n    protocols (e.g., ``http/1.1``), the application should add them to\n    the returned list.\n\n.. py:function:: npn_get_version(proto)\n\n    Returns SPDY version which spdylay library supports from the given\n    protocol name. The *proto* is the unicode string to the protocol\n    name. Currently, ``spdy/2`` and ``spdy/3`` are supported.  The\n    returned nonzero SPDY version can be passed as the version\n    argument in :py:class:`Session` constructor.\n\n    This function returns nonzero SPDY version if it succeeds, or 0.\n\n\nData Provider Objects\n---------------------\n\n.. py:class:: DataProvider(source, read_cb)\n\n    This class represents the data source and the way to read a chunk\n    of data from it. The *source* is expected to be the data source to\n    read, but the application can freely pass any object including\n    ``None``. The *read_cb* is the callback function invoked when the\n    library needs to read data. The data read will be sent as DATA\n    frame.\n\n    .. py:function:: read_cb(session, stream_id, length, read_ctrl, source)\n\n        The *session* is the :py:class:`Session` object. The\n        *stream_id* is the stream to send data. The *source* is the\n        object passed as a *source* in DataProvider constructor. The\n        implementation of this callback must read at most *length*\n        bytes of data and return it as bytestring. When all data is\n        read, assign :py:const:`READ_EOF` to ``read_ctrl.flags``.  If\n        the application wants to postpone DATA frames, (e.g.,\n        asynchronous I/O, or reading data blocks for long time), it is\n        achieved by returning :py:const:`ERR_DEFERRED` without reading\n        any data in this invocation. The library removes DATA frame\n        from the outgoing queue temporarily. To move back deferred\n        DATA frame to outgoing queue, call\n        :py:meth:`Session.resume_data()`. In case of error, there are\n        2 choices. Raising :py:class:`TemporalCallbackFailureError`\n        will close the stream by issuing RST_STREAM with\n        :py:const:`INTERNAL_ERROR`. Raising\n        :py:class:`CallbackFailureError` will signal the entire\n        session failure.\n\n.. py:attribute:: DataProvider.source\n\n.. py:attribute:: DataProvider.read_cb\n\nControl Frame Objects\n---------------------\n\n.. py:class:: CtrlFrame\n\n    The base class of SPDY control frames.\n\n    .. py:attribute:: version\n\n        Version\n\n    .. py:attribute:: frame_type\n\n        Frame type. See `Frame Types`_.\n\n    .. py:attribute:: flags\n\n        Flags. See `Control Frame Flags`_.\n\n    .. py:attribute:: length\n\n        Frame payload length\n\nThe following frame classes inherit :py:class:`CtrlFrame` class.\n\n.. py:class:: SynStreamFrame\n\n    .. py:attribute:: stream_id\n\n        Stream ID\n\n    .. py:attribute:: assoc_stream_id\n\n        Associated-To-Stream-ID\n\n    .. py:attribute:: pri\n\n        Priority\n\n    .. py:attribute:: slot\n\n        Credential slot\n\n    .. py:attribute:: nv\n\n        List of name/value pair.\n\n.. py:class:: SynReplyFrame\n\n    .. py:attribute:: stream_id\n\n        Stream ID\n\n    .. py:attribute:: nv\n\n        List of name/value pair.\n\n.. py:class:: HeadersFrame\n\n    .. py:attribute:: stream_id\n\n        Stream ID\n\n    .. py:attribute:: nv\n\n        List of name/value pair.\n\n.. py:class:: RstStreamFrame\n\n\n    .. py:attribute:: stream_id\n\n        Stream ID\n\n    .. py:attribute:: status_code\n\n        Status code\n\n.. py:class:: SettingsFrame\n\n    .. py:attribute:: iv\n\n        List of tuple ``(settings_id, flags, value)``\n\n.. py:class:: PingFrame\n\n    .. py:attribute:: unique_id\n\n        Unique ID\n\n.. py:class:: GoawayFrame\n\n    .. py:attribute:: last_good_stream_id\n\n        Last good stream ID\n\n    .. py:attribute:: status_code\n\n        Status code\n\n.. py:class:: WindowUpdateFrame\n\n    .. py:attribute:: stream_id\n\n        Stream ID\n\n    .. py:attribute:: delta_window_size\n\n        Delta window size\n\nExceptions\n----------\n\n.. py:class:: EOFError\n\n.. py:class:: CallbackFailureError\n\n.. py:class:: TemporalCallbackFailureError\n\n.. py:class:: InvalidArgumentError\n\n.. py:class:: ZlibError\n\n.. py:class:: UnsupportedVersionError\n\n.. py:class:: StreamClosedError\n\nRead Callback Flags\n-------------------\n\n.. py:data:: READ_EOF\n\nError Codes\n-----------\n\n.. py:data:: ERR_INVALID_ARGUMENT\n.. py:data:: ERR_ZLIB\n.. py:data:: ERR_UNSUPPORTED_VERSION\n.. py:data:: ERR_WOULDBLOCK\n.. py:data:: ERR_PROTO\n.. py:data:: ERR_INVALID_FRAME\n.. py:data:: ERR_EOF\n.. py:data:: ERR_DEFERRED\n.. py:data:: ERR_STREAM_ID_NOT_AVAILABLE\n.. py:data:: ERR_STREAM_CLOSED\n.. py:data:: ERR_STREAM_CLOSING\n.. py:data:: ERR_STREAM_SHUT_WR\n.. py:data:: ERR_INVALID_STREAM_ID\n.. py:data:: ERR_INVALID_STREAM_STATE\n.. py:data:: ERR_DEFERRED_DATA_EXIST\n.. py:data:: ERR_SYN_STREAM_NOT_ALLOWED\n.. py:data:: ERR_GOAWAY_ALREADY_SENT\n.. py:data:: ERR_INVALID_HEADER_BLOCK\n.. py:data:: ERR_INVALID_STATE\n.. py:data:: ERR_GZIP\n.. py:data:: ERR_TEMPORAL_CALLBACK_FAILURE\n\nFollowing error codes indicate fatal error.\n\n.. py:data:: ERR_FATAL\n.. py:data:: ERR_NOMEM\n.. py:data:: ERR_CALLBACK_FAILURE\n\nFrame Types\n-----------\n\n.. py:data:: SYN_STREAM\n\n.. py:data:: SYN_REPLY\n\n.. py:data:: RST_STREAM\n\n.. py:data:: SETTINGS\n\n.. py:data:: NOOP\n\n   Note that this was deprecated in SPDY/3.\n\n.. py:data:: PING\n\n.. py:data:: GOAWAY\n\n.. py:data:: HEADERS\n\n.. py:data:: WINDOW_UPDATE\n\n   This first appeared in SPDY/3.\n\n.. py:data:: CREDENTIAL\n\n   This first appeared in SPDY/3.\n\nControl Frame Flags\n-------------------\n\n.. py:data:: CTRL_FLAG_NONE\n\n   Indicates no flags set.\n\n.. py:data:: CTRL_FLAG_FIN\n\n.. py:data:: CTRL_FLAG_UNIDIRECTIONAL\n\nStream Status Codes\n-------------------\n\n.. py:data:: OK\n\n   This is not a valid status code for RST_STREAM. Don't use this in\n   :py:meth:`Session.submit_rst_stream()`.\n\n.. py:data:: PROTOCOL_ERROR\n\n.. py:data:: INVALID_STREAM\n\n.. py:data:: REFUSED_STREAM\n\n.. py:data:: UNSUPPORTED_VERSION\n\n.. py:data:: CANCEL\n\n.. py:data:: INTERNAL_ERROR\n\n.. py:data:: FLOW_CONTROL_ERROR\n\nFollowing status codes were introduced in SPDY/3.\n\n.. py:data:: STREAM_IN_USE\n\n.. py:data:: STREAM_ALREADY_CLOSED\n\n.. py:data:: INVALID_CREDENTIALS\n\n.. py:data:: FRAME_TOO_LARGE\n\nGOAWAY Status Codes\n-------------------\n\n.. py:data:: GOAWAY_OK\n\n.. py:data:: GOAWAY_PROTOCOL_ERROR\n\n.. py:data:: GOAWAY_INTERNAL_ERROR\n\nSETTINGS Frame Flags\n--------------------\n\n.. py:data:: FLAG_SETTINGS_NONE\n\n.. py:data:: FLAG_SETTINGS_CLEAR_SETTINGS\n\nSETTINGS IDs\n------------\n\n.. py:data:: SETTINGS_UPLOAD_BANDWIDTH\n\n.. py:data:: SETTINGS_DOWNLOAD_BANDWIDTH\n\n.. py:data:: SETTINGS_ROUND_TRIP_TIME\n\n.. py:data:: SETTINGS_MAX_CONCURRENT_STREAMS\n\n.. py:data:: SETTINGS_CURRENT_CWND\n\n.. py:data:: SETTINGS_DOWNLOAD_RETRANS_RATE\n\n.. py:data:: SETTINGS_INITIAL_WINDOW_SIZE\n\n.. py:data:: SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE\n\n.. py:data::  SETTINGS_MAX\n\nSETTINGS ID Flags\n-----------------\n\n.. py:data:: ID_FLAG_SETTINGS_NONE\n\n.. py:data:: ID_FLAG_SETTINGS_PERSIST_VALUE\n\n.. py:data:: ID_FLAG_SETTINGS_PERSISTED\n\nSimple SPDY Client\n------------------\n\nThis module offers a simple SPDY client implementation.  The function\n:py:func:`urlfetch()` fetches given URLs. For each URL,\n*StreamHandlerClass* is instantiated and its methods are called when\ncertain event occurs. The *StreamHandlerClass* must be a subclass of\n:py:class:`BaseSPDYStreamHandler`.\n\n.. py:function:: urlfetch(url_or_urls, StreamHandlerClass)\n\n    Opens URL and handles the response from the servers.\n\n    The *url_or_urls* is either one URL string or list of URL string.\n    For each URL, *StreamHandlerClass* is instantiated and it handles\n    the request to and response from the server. If successive URLs in\n    *url_or_urls* list have same origin, they are processed in one\n    SPDY session.\n\n.. py:class:: BaseSPDYStreamHandler(url, fetcher)\n\n    This class handles one URL retrieval, which corresponds one SPDY\n    stream. The *url* is the URL to fetch. The *fetcher* is a driver\n    object to call methods of this object. For now it is opaque\n    object. This class is intended to be subclassed by the application\n    to add specific behavior.\n\n    ``BaseSPDYStreamHandler`` has the following instance variables:\n\n    .. py:attribute:: url\n\n        The URL for this stream.\n\n    .. py:attribute:: stream_id\n\n        The stream ID for this stream.\n\n    ``BaseSPDYStreamHandler`` has the following methods:\n\n    .. py:method:: on_header(nv)\n\n        Called when name/value pairs (headers) *nv* is received.  This\n        method may be overridden by subclasses. The default\n        implementation does nothing.\n\n    .. py:method:: on_data(data)\n\n        Called when *data* is received. This method may be overridden\n        by subclass. The default implementation does nothing.\n\n    .. py:method:: on_close(status_code)\n\n        Called when this stream is closed. The *status_code* indicates\n        the reason of the closure. See `Stream Status Codes`_.  This\n        method may be overridden by subclass. The default\n        implementation does nothing.\n\nThe example follows:\n\n.. literalinclude:: ../python/spdyclient.py\n    :language: python\n\nSimple SPDY Server\n------------------\n\nThis module offers a simple SPDY server implementation to ready for\nuse with little additional code.\n\nThe :py:class:`ThreadedSPDYServer` is a ``socketserver.TCPServer``\nsubclass.  As the name of the class suggests, it is multi threaded.\nIt only supports SPDY connection and does not fallback to HTTP/1.1.\nSince it uses TLS NPN extension, Python 3.3.0 or later is required.\n\n.. py:class:: ThreadedSPDYServer(server_address, RequestHandlerCalss, cert_file, key_file)\n\n    This class builds on ``TCPServer`` class by passing\n    *server_address* and *RequestHandlerCalss*. The request is handled\n    by the instance of *RequestHandlerCalss*.\n\nThe :py:class:`ThreadedSPDYServer` requires a *RequestHandlerCalss* on\ninstantiation, which must be a subclass of\n:py:class:`BaseSPDYRequestHandler`.\n\nMost texts are copied (and modified) from ``http.server``\ndocumentation.\n\n.. py:class:: BaseSPDYRequestHandler(request, client_address, server)\n\n    This class is used to handle the SPDY requests (streams) that\n    arrive at the server. By itself, it cannot respond to any actual\n    SPDY requests; it must be subclassed to handle each request method\n    (e.g. ``GET`` or ``POST``). ``BaseSPDYRequestHandler`` provides a\n    number of class and instance variables, and methods for use by\n    subclasses.\n\n    The handler will gather headers (name/value pairs in SPDY terms)\n    and read POST data (if any), then call a method specific to the\n    request type. The method name is constructed from the request. For\n    example, for the request method ``SPAM``, the ``do_SPAM()`` method\n    will be called with no arguments. All of the relevant information\n    is stored in instance variables of the handler. Subclasses should\n    not need to override or extend the ``__init__()`` method.\n\n    .. note::\n\n        Currently, this implementation accepts request body only if\n        method is POST and the request body will be stored in memory.\n\n    ``BaseSPDYRequestHandler`` has the following instance variables:\n\n    .. py:attribute:: client_address\n\n        Contains a tuple of the form ``(host, port)`` referring to the\n        client's address.\n\n    .. py:attribute:: server\n\n        Contains the server instance.\n\n    .. py:attribute:: command\n\n        Contains the command (request type, method). For example,\n        ``GET``.\n\n    .. py:attribute:: path\n\n        Contains the request path.\n\n    .. py:attribute:: request_version\n\n        Contains the version string from the request. For example,\n        ``HTTP/1.1``.\n\n    .. py:attribute:: headers\n\n        Contains the request headers. Each name/value pair is a tuple\n        of the form ``(name, value)``.\n\n    .. py:attribute:: rfile\n\n        Contains an input stream, positioned at the start of the\n        optional input data. If there is no optional input data, it\n        may be ``None``.\n\n    .. py:attribute:: wfile\n\n        Contains the output stream for writing a response back to the\n        client.\n\n    ``BaseSPDYRequestHandler`` has the following class variables:\n\n    .. py:attribute:: server_version\n\n        Specifies the server software version.\n\n    .. py:attribute:: sys_version\n\n        Contains the Python system version.\n\n    A ``BaseSPDYRequestHandler`` instance has the following methods:\n\n    .. py:method:: handle()\n\n        Interacts client exchanging SPDY frames. When a request is\n        completely received, it calls appropriate ``do_*()`` method.\n        This method will handle multiple requests (streams) until SPDY\n        session is over.\n\n    .. py:method:: send_error(code, message=None)\n\n        Send a complete error reply to the client The numeric *code*\n        specifies the HTTP error code, with *message* as optional,\n        more specific text. A complete set of headers is sent,\n        followed by HTML text.\n\n    .. py:method:: send_response(code, message=None)\n\n        Adds a response code and, optionally, short message.\n        This will be formatted as ':status' response header field.\n\n    .. py:method:: send_header(keyword, value)\n\n        Adds the HTTP header. The *keyword* and *value* must be\n        unicode strings and not ``None``.\n\nThe example of ``BaseSPDYRequestHandler`` and ``ThreadedSPDYServer``\nfollows:\n\n.. literalinclude:: ../python/spdyserv.py\n    :language: python\n"
  },
  {
    "path": "examples/.gitignore",
    "content": "spdynative\nspdycli\n"
  },
  {
    "path": "examples/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nif ENABLE_EXAMPLES\n\nAM_CFLAGS = -Wall\nAM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \\\n\t@OPENSSL_CFLAGS@ @DEFS@\nAM_LDFLAGS = @OPENSSL_LIBS@\nLDADD = $(top_builddir)/lib/libspdylay.la\n\nnoinst_PROGRAMS = spdycli\nspdycli_SOURCES = spdycli.c\n\nendif # ENABLE_EXAMPLES\n\nEXTRA_DIST = README.rst\n"
  },
  {
    "path": "examples/README.rst",
    "content": "spdycat, spdyd and shrpx were moved to ``../src``.\n"
  },
  {
    "path": "examples/spdy.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDY_H\n#define SPDY_H\n\n#include \"spdylay_config.h\"\n\n#include <signal.h>\n\n#include <vector>\n#include <string>\n#include <functional>\n\n#include \"spdylay_ssl.h\"\n#include \"uri.h\"\n#include \"util.h\"\n#include \"SpdyServer.h\"\n\nusing namespace spdylay;\n\nnamespace spdylay {\n\nclass request {\npublic:\n  request(const std::vector<std::pair<std::string, std::string>>& headers)\n    : headers_(headers)\n  {}\n\n  const std::vector<std::pair<std::string, std::string>>& headers()\n  {\n    return headers_;\n  }\nprivate:\n  std::vector<std::pair<std::string, std::string>> headers_;\n};\n\nclass response {\npublic:\n  response()\n    : status_code_(200)\n  {}\n\n  void set_status(int status_code)\n  {\n    status_code_ = status_code;\n  }\n\n  const char* get_status_string() const\n  {\n    switch(status_code_) {\n    case 100: return \"100 Continue\";\n    case 101: return \"101 Switching Protocols\";\n    case 200: return \"200 OK\";\n    case 201: return \"201 Created\";\n    case 202: return \"202 Accepted\";\n    case 203: return \"203 Non-Authoritative Information\";\n    case 204: return \"204 No Content\";\n    case 205: return \"205 Reset Content\";\n    case 206: return \"206 Partial Content\";\n    case 300: return \"300 Multiple Choices\";\n    case 301: return \"301 Moved Permanently\";\n    case 302: return \"302 Found\";\n    case 303: return \"303 See Other\";\n    case 304: return \"304 Not Modified\";\n    case 305: return \"305 Use Proxy\";\n      // case 306: return \"306 (Unused)\";\n    case 307: return \"307 Temporary Redirect\";\n    case 400: return \"400 Bad Request\";\n    case 401: return \"401 Unauthorized\";\n    case 402: return \"402 Payment Required\";\n    case 403: return \"403 Forbidden\";\n    case 404: return \"404 Not Found\";\n    case 405: return \"405 Method Not Allowed\";\n    case 406: return \"406 Not Acceptable\";\n    case 407: return \"407 Proxy Authentication Required\";\n    case 408: return \"408 Request Timeout\";\n    case 409: return \"409 Conflict\";\n    case 410: return \"410 Gone\";\n    case 411: return \"411 Length Required\";\n    case 412: return \"412 Precondition Failed\";\n    case 413: return \"413 Request Entity Too Large\";\n    case 414: return \"414 Request-URI Too Long\";\n    case 415: return \"415 Unsupported Media Type\";\n    case 416: return \"416 Requested Range Not Satisfiable\";\n    case 417: return \"417 Expectation Failed\";\n    case 500: return \"500 Internal Server Error\";\n    case 501: return \"501 Not Implemented\";\n    case 502: return \"502 Bad Gateway\";\n    case 503: return \"503 Service Unavailable\";\n    case 504: return \"504 Gateway Timeout\";\n    case 505: return \"505 HTTP Version Not Supported\";\n    default: return \"\";\n    }\n  }\n\n  void set_header(const std::string& key, const std::string& value)\n  {\n    headers_.push_back(std::make_pair(key, value));\n  }\n\n  const std::vector<std::pair<std::string, std::string>>& get_headers()\n  {\n    return headers_;\n  }\n\n  void end(const std::string& body)\n  {\n    body_ = body;\n  }\n\n  const std::string& get_body() const\n  {\n    return body_;\n  }\nprivate:\n  int status_code_;\n  std::string body_;\n  std::vector<std::pair<std::string, std::string>> headers_;\n};\n\nssize_t string_read_callback\n(spdylay_session *session, int32_t stream_id,\n uint8_t *buf, size_t length, int *eof,\n spdylay_data_source *source, void *user_data)\n{\n  std::pair<std::string, size_t>& body_pair =\n    *reinterpret_cast<std::pair<std::string, size_t>*>(source->ptr);\n  const std::string& body = body_pair.first;\n  size_t off = body_pair.second;\n  ssize_t readlen = std::min(body.size()-off, length);\n  memcpy(buf, body.c_str()+off, readlen);\n  off += readlen;\n  if(off == body.size()) {\n    *eof = 1;\n  }\n  return readlen;\n}\n\nvoid on_request_recv_callback\n(spdylay_session *session, int32_t stream_id, void *user_data)\n{\n  SpdyEventHandler *hd = reinterpret_cast<SpdyEventHandler*>(user_data);\n  Request *req = hd->get_stream(stream_id);\n  request request_obj(req->headers);\n  response response_obj;\n  (*reinterpret_cast<std::function<void (request&, response&)>*>\n   (hd->config()->data_ptr))(request_obj, response_obj);\n  size_t body_length = response_obj.get_body().size();\n  response_obj.set_header(\"content-length\", util::to_str(body_length));\n  req->response_body = std::make_pair(response_obj.get_body(), 0);\n\n  spdylay_data_provider data_prd;\n  data_prd.source.ptr = &req->response_body;\n  data_prd.read_callback = string_read_callback;\n  hd->submit_response(response_obj.get_status_string(), stream_id,\n                      response_obj.get_headers(), &data_prd);\n}\n\nclass spdy {\npublic:\n  spdy() : server_(0) {}\n  ~spdy()\n  {\n    delete server_;\n  }\n  bool listen(const std::string& host, uint16_t port,\n              const std::string& private_key_file, const std::string& cert_file,\n              std::function<void (request&, response&)> callback,\n              bool verbose = false)\n  {\n    delete server_;\n    callback_ = callback;\n    config_.verbose = verbose;\n    config_.host = host;\n    config_.port = port;\n    config_.private_key_file = private_key_file;\n    config_.cert_file = cert_file;\n    config_.on_request_recv_callback = on_request_recv_callback;\n    config_.data_ptr = &callback_;\n    server_ = new SpdyServer(&config_);\n    return server_->listen() == 0;\n  }\n\n  int run()\n  {\n    return server_->run();\n  }\nprivate:\n  Config config_;\n  std::function<void (request&, response&)> callback_;\n  SpdyServer *server_;\n};\n\nnamespace reactor {\n\ntemplate<typename Server>\nint run(Server& server)\n{\n  struct sigaction act;\n  memset(&act, 0, sizeof(struct sigaction));\n  act.sa_handler = SIG_IGN;\n  sigaction(SIGPIPE, &act, 0);\n  OpenSSL_add_all_algorithms();\n  SSL_load_error_strings();\n  SSL_library_init();\n  reset_timer();\n  int r = server.run();\n  if(r == 0) {\n    return EXIT_SUCCESS;\n  } else {\n    return EXIT_FAILURE;\n  }\n}\n\n} // namespace reactor\n\n} // namespace spdylay\n\n#endif // SPDY_H\n"
  },
  {
    "path": "examples/spdycli.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n/*\n * This program is written to show how to use Spdylay API in C and\n * intentionally made simple.\n */\n#include <stdint.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <poll.h>\n#include <signal.h>\n#include <stdio.h>\n#include <assert.h>\n#include <string.h>\n\n#include <spdylay/spdylay.h>\n\n#include <config.h>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\nenum {\n  IO_NONE,\n  WANT_READ,\n  WANT_WRITE\n};\n\nstruct Connection {\n  SSL *ssl;\n  spdylay_session *session;\n  /* WANT_READ if SSL connection needs more input; or WANT_WRITE if it\n     needs more output; or IO_NONE. This is necessary because SSL/TLS\n     re-negotiation is possible at any time. Spdylay API offers\n     similar functions like spdylay_session_want_read() and\n     spdylay_session_want_write() but they do not take into account\n     SSL connection. */\n  int want_io;\n};\n\nstruct Request {\n  char *host;\n  /* In this program, path contains query component as well. */\n  char *path;\n  /* This is the concatenation of host and port with \":\" in\n     between. */\n  char *hostport;\n  /* The gzip stream inflater for the compressed response. */\n  spdylay_gzip *inflater;\n  /* Stream ID for this request. */\n  int32_t stream_id;\n  uint16_t port;\n};\n\nstruct URI {\n  const char *host;\n  /* In this program, path contains query component as well. */\n  const char *path;\n  const char *hostport;\n  size_t hostlen;\n  size_t pathlen;\n  size_t hostportlen;\n  uint16_t port;\n};\n\n/*\n * Returns copy of string |s| with the length |len|. The returned\n * string is NULL-terminated.\n */\nstatic char* strcopy(const char *s, size_t len)\n{\n  char *dst;\n  dst = malloc(len+1);\n  memcpy(dst, s, len);\n  dst[len] = '\\0';\n  return dst;\n}\n\n/*\n * Prints error message |msg| and exit.\n */\nstatic void die(const char *msg)\n{\n  fprintf(stderr, \"FATAL: %s\\n\", msg);\n  exit(EXIT_FAILURE);\n}\n\n/*\n * Prints error containing the function name |func| and message |msg|\n * and exit.\n */\nstatic void dief(const char *func, const char *msg)\n{\n  fprintf(stderr, \"FATAL: %s: %s\\n\", func, msg);\n  exit(EXIT_FAILURE);\n}\n\n/*\n * Prints error containing the function name |func| and error code\n * |error_code| and exit.\n */\nstatic void diec(const char *func, int error_code)\n{\n  fprintf(stderr, \"FATAL: %s: error_code=%d, msg=%s\\n\", func, error_code,\n          spdylay_strerror(error_code));\n  exit(EXIT_FAILURE);\n}\n\n/*\n * Check response is content-encoding: gzip. We need this because SPDY\n * client is required to support gzip.\n */\nstatic void check_gzip(struct Request *req, char **nv)\n{\n  int gzip = 0;\n  size_t i;\n  for(i = 0; nv[i]; i += 2) {\n    if(strcmp(\"content-encoding\", nv[i]) == 0) {\n      gzip = strcmp(\"gzip\", nv[i+1]) == 0;\n      break;\n    }\n  }\n  if(gzip) {\n    int rv;\n    if(req->inflater) {\n      return;\n    }\n    rv = spdylay_gzip_inflate_new(&req->inflater);\n    if(rv != 0) {\n      die(\"Can't allocate inflate stream.\");\n    }\n  }\n}\n\n/*\n * The implementation of spdylay_send_callback type. Here we write\n * |data| with size |length| to the network and return the number of\n * bytes actually written. See the documentation of\n * spdylay_send_callback for the details.\n */\nstatic ssize_t send_callback(spdylay_session *session _U_,\n                             const uint8_t *data, size_t length, int flags _U_,\n                             void *user_data)\n{\n  struct Connection *connection;\n  ssize_t rv;\n  connection = (struct Connection*)user_data;\n  connection->want_io = IO_NONE;\n  ERR_clear_error();\n  rv = SSL_write(connection->ssl, data, (int)length);\n  if(rv < 0) {\n    int err = SSL_get_error(connection->ssl, (int)rv);\n    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {\n      connection->want_io = (err == SSL_ERROR_WANT_READ ?\n                             WANT_READ : WANT_WRITE);\n      rv = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      rv = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  }\n  return rv;\n}\n\n/*\n * The implementation of spdylay_recv_callback type. Here we read data\n * from the network and write them in |buf|. The capacity of |buf| is\n * |length| bytes. Returns the number of bytes stored in |buf|. See\n * the documentation of spdylay_recv_callback for the details.\n */\nstatic ssize_t recv_callback(spdylay_session *session _U_,\n                             uint8_t *buf, size_t length, int flags _U_,\n                             void *user_data)\n{\n  struct Connection *connection;\n  ssize_t rv;\n  connection = (struct Connection*)user_data;\n  connection->want_io = IO_NONE;\n  ERR_clear_error();\n  rv = SSL_read(connection->ssl, buf, (int)length);\n  if(rv < 0) {\n    int err = SSL_get_error(connection->ssl, (int)rv);\n    if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {\n      connection->want_io = (err == SSL_ERROR_WANT_READ ?\n                             WANT_READ : WANT_WRITE);\n      rv = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      rv = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(rv == 0) {\n    rv = SPDYLAY_ERR_EOF;\n  }\n  return rv;\n}\n\n/*\n * The implementation of spdylay_before_ctrl_send_callback type.  We\n * use this function to get stream ID of the request. This is because\n * stream ID is not known when we submit the request\n * (spdylay_submit_request).\n */\nstatic void before_ctrl_send_callback(spdylay_session *session,\n                                      spdylay_frame_type type,\n                                      spdylay_frame *frame,\n                                      void *user_data _U_)\n{\n  if(type == SPDYLAY_SYN_STREAM) {\n    struct Request *req;\n    int stream_id = frame->syn_stream.stream_id;\n    req = spdylay_session_get_stream_user_data(session, stream_id);\n    if(req && req->stream_id == -1) {\n      req->stream_id = stream_id;\n      printf(\"[INFO] Stream ID = %d\\n\", stream_id);\n    }\n  }\n}\n\nstatic void on_ctrl_send_callback(spdylay_session *session,\n                                  spdylay_frame_type type,\n                                  spdylay_frame *frame, void *user_data _U_)\n{\n  char **nv;\n  const char *name = NULL;\n  int32_t stream_id;\n  size_t i;\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    nv = frame->syn_stream.nv;\n    name = \"SYN_STREAM\";\n    stream_id = frame->syn_stream.stream_id;\n    break;\n  default:\n    break;\n  }\n  if(name && spdylay_session_get_stream_user_data(session, stream_id)) {\n    printf(\"[INFO] C ----------------------------> S (%s)\\n\", name);\n    for(i = 0; nv[i]; i += 2) {\n      printf(\"       %s: %s\\n\", nv[i], nv[i+1]);\n    }\n  }\n}\n\nstatic void on_ctrl_recv_callback(spdylay_session *session,\n                                  spdylay_frame_type type,\n                                  spdylay_frame *frame, void *user_data _U_)\n{\n  struct Request *req;\n  char **nv;\n  const char *name = NULL;\n  int32_t stream_id;\n  size_t i;\n  switch(type) {\n  case SPDYLAY_SYN_REPLY:\n    nv = frame->syn_reply.nv;\n    name = \"SYN_REPLY\";\n    stream_id = frame->syn_reply.stream_id;\n    break;\n  case SPDYLAY_HEADERS:\n    nv = frame->headers.nv;\n    name = \"HEADERS\";\n    stream_id = frame->headers.stream_id;\n    break;\n  default:\n    break;\n  }\n  if(!name) {\n    return;\n  }\n  req = spdylay_session_get_stream_user_data(session, stream_id);\n  if(req) {\n    check_gzip(req, nv);\n    printf(\"[INFO] C <---------------------------- S (%s)\\n\", name);\n    for(i = 0; nv[i]; i += 2) {\n      printf(\"       %s: %s\\n\", nv[i], nv[i+1]);\n    }\n  }\n}\n\n/*\n * The implementation of spdylay_on_stream_close_callback type. We use\n * this function to know the response is fully received. Since we just\n * fetch 1 resource in this program, after reception of the response,\n * we submit GOAWAY and close the session.\n */\nstatic void on_stream_close_callback(spdylay_session *session,\n                                     int32_t stream_id,\n                                     spdylay_status_code status_code _U_,\n                                     void *user_data _U_)\n{\n  struct Request *req;\n  req = spdylay_session_get_stream_user_data(session, stream_id);\n  if(req) {\n    int rv;\n    rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);\n    if(rv != 0) {\n      diec(\"spdylay_submit_goaway\", rv);\n    }\n  }\n}\n\n#define MAX_OUTLEN 4096\n\n/*\n * The implementation of spdylay_on_data_chunk_recv_callback type. We\n * use this function to print the received response body.\n */\nstatic void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags _U_,\n                                        int32_t stream_id,\n                                        const uint8_t *data, size_t len,\n                                        void *user_data _U_)\n{\n  struct Request *req;\n  req = spdylay_session_get_stream_user_data(session, stream_id);\n  if(req) {\n    printf(\"[INFO] C <---------------------------- S (DATA)\\n\");\n    printf(\"       %lu bytes\\n\", (unsigned long int)len);\n    if(req->inflater) {\n      while(len > 0) {\n        uint8_t out[MAX_OUTLEN];\n        size_t outlen = MAX_OUTLEN;\n        size_t tlen = len;\n        int rv;\n        rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);\n        if(rv == -1) {\n          spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);\n          break;\n        }\n        fwrite(out, 1, outlen, stdout);\n        data += tlen;\n        len -= tlen;\n      }\n    } else {\n      /* TODO add support gzip */\n      fwrite(data, 1, len, stdout);\n    }\n    printf(\"\\n\");\n  }\n}\n\n/*\n * Setup callback functions. Spdylay API offers many callback\n * functions, but most of them are optional. The send_callback is\n * always required. Since we use spdylay_session_recv(), the\n * recv_callback is also required.\n */\nstatic void setup_spdylay_callbacks(spdylay_session_callbacks *callbacks)\n{\n  memset(callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks->send_callback = send_callback;\n  callbacks->recv_callback = recv_callback;\n  callbacks->before_ctrl_send_callback = before_ctrl_send_callback;\n  callbacks->on_ctrl_send_callback = on_ctrl_send_callback;\n  callbacks->on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks->on_stream_close_callback = on_stream_close_callback;\n  callbacks->on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n}\n\n/*\n * Callback function for SSL/TLS NPN. Since this program only supports\n * SPDY protocol, if server does not offer SPDY protocol the Spdylay\n * library supports, we terminate program.\n */\nstatic int select_next_proto_cb(SSL* ssl _U_,\n                                unsigned char **out, unsigned char *outlen,\n                                const unsigned char *in, unsigned int inlen,\n                                void *arg)\n{\n  int rv;\n  uint16_t *spdy_proto_version;\n  /* spdylay_select_next_protocol() selects SPDY protocol version the\n     Spdylay library supports. */\n  rv = spdylay_select_next_protocol(out, outlen, in, inlen);\n  if(rv <= 0) {\n    die(\"Server did not advertise spdy/2 or spdy/3 protocol.\");\n  }\n  spdy_proto_version = (uint16_t*)arg;\n  *spdy_proto_version = rv;\n  return SSL_TLSEXT_ERR_OK;\n}\n\n/*\n * Setup SSL context. We pass |spdy_proto_version| to get negotiated\n * SPDY protocol version in NPN callback.\n */\nstatic void init_ssl_ctx(SSL_CTX *ssl_ctx, uint16_t *spdy_proto_version)\n{\n  /* Disable SSLv2 and enable all workarounds for buggy servers */\n  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n  /* Set NPN callback */\n  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,\n                                   spdy_proto_version);\n}\n\nstatic void ssl_handshake(SSL *ssl, int fd)\n{\n  int rv;\n  if(SSL_set_fd(ssl, fd) == 0) {\n    dief(\"SSL_set_fd\", ERR_error_string(ERR_get_error(), NULL));\n  }\n  ERR_clear_error();\n  rv = SSL_connect(ssl);\n  if(rv <= 0) {\n    dief(\"SSL_connect\", ERR_error_string(ERR_get_error(), NULL));\n  }\n}\n\n/*\n * Connects to the host |host| and port |port|.  This function returns\n * the file descriptor of the client socket.\n */\nstatic int connect_to(const char *host, uint16_t port)\n{\n  struct addrinfo hints;\n  int fd = -1;\n  int rv;\n  char service[NI_MAXSERV];\n  struct addrinfo *res, *rp;\n  snprintf(service, sizeof(service), \"%u\", port);\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  rv = getaddrinfo(host, service, &hints, &res);\n  if(rv != 0) {\n    dief(\"getaddrinfo\", gai_strerror(rv));\n  }\n  for(rp = res; rp; rp = rp->ai_next) {\n    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    if(fd == -1) {\n      continue;\n    }\n    while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&\n          errno == EINTR);\n    if(rv == 0) {\n      break;\n    }\n    close(fd);\n    fd = -1;\n  }\n  freeaddrinfo(res);\n  return fd;\n}\n\nstatic void make_non_block(int fd)\n{\n  int flags, rv;\n  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);\n  if(flags == -1) {\n    dief(\"fcntl\", strerror(errno));\n  }\n  while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);\n  if(rv == -1) {\n    dief(\"fcntl\", strerror(errno));\n  }\n}\n\n/*\n * Setting TCP_NODELAY is not mandatory for the SPDY protocol.\n */\nstatic void set_tcp_nodelay(int fd)\n{\n  int val = 1;\n  int rv;\n  rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));\n  if(rv == -1) {\n    dief(\"setsockopt\", strerror(errno));\n  }\n}\n\n/*\n * Update |pollfd| based on the state of |connection|.\n */\nstatic void ctl_poll(struct pollfd *pollfd, struct Connection *connection)\n{\n  pollfd->events = 0;\n  if(spdylay_session_want_read(connection->session) ||\n     connection->want_io == WANT_READ) {\n    pollfd->events |= POLLIN;\n  }\n  if(spdylay_session_want_write(connection->session) ||\n     connection->want_io == WANT_WRITE) {\n    pollfd->events |= POLLOUT;\n  }\n}\n\n/*\n * Submits the request |req| to the connection |connection|.  This\n * function does not send packets; just append the request to the\n * internal queue in |connection->session|.\n */\nstatic void submit_request(struct Connection *connection, struct Request *req)\n{\n  int pri = 0;\n  int rv;\n  const char *nv[15];\n  /* We always use SPDY/3 style header even if the negotiated protocol\n     version is SPDY/2. The library translates the header name as\n     necessary. Make sure that the last item is NULL! */\n  nv[0] = \":method\";     nv[1] = \"GET\";\n  nv[2] = \":path\";       nv[3] = req->path;\n  nv[4] = \":version\";    nv[5] = \"HTTP/1.1\";\n  nv[6] = \":scheme\";     nv[7] = \"https\";\n  nv[8] = \":host\";       nv[9] = req->hostport;\n  nv[10] = \"accept\";     nv[11] = \"*/*\";\n  nv[12] = \"user-agent\"; nv[13] = \"spdylay/\"SPDYLAY_VERSION;\n  nv[14] = NULL;\n  rv = spdylay_submit_request(connection->session, pri, nv, NULL, req);\n  if(rv != 0) {\n    diec(\"spdylay_submit_request\", rv);\n  }\n}\n\n/*\n * Performs the network I/O.\n */\nstatic void exec_io(struct Connection *connection)\n{\n  int rv;\n  rv = spdylay_session_recv(connection->session);\n  if(rv != 0) {\n    diec(\"spdylay_session_recv\", rv);\n  }\n  rv = spdylay_session_send(connection->session);\n  if(rv != 0) {\n    diec(\"spdylay_session_send\", rv);\n  }\n}\n\nstatic void request_init(struct Request *req, const struct URI *uri)\n{\n  req->host = strcopy(uri->host, uri->hostlen);\n  req->port = uri->port;\n  req->path = strcopy(uri->path, uri->pathlen);\n  req->hostport = strcopy(uri->hostport, uri->hostportlen);\n  req->stream_id = -1;\n  req->inflater = NULL;\n}\n\nstatic void request_free(struct Request *req)\n{\n  free(req->host);\n  free(req->path);\n  free(req->hostport);\n  spdylay_gzip_inflate_del(req->inflater);\n}\n\n/*\n * Fetches the resource denoted by |uri|.\n */\nstatic void fetch_uri(const struct URI *uri)\n{\n  spdylay_session_callbacks callbacks;\n  int fd;\n  SSL_CTX *ssl_ctx;\n  SSL *ssl;\n  struct Request req;\n  struct Connection connection;\n  int rv;\n  nfds_t npollfds = 1;\n  struct pollfd pollfds[1];\n  uint16_t spdy_proto_version;\n\n  request_init(&req, uri);\n\n  setup_spdylay_callbacks(&callbacks);\n\n  /* Establish connection and setup SSL */\n  fd = connect_to(req.host, req.port);\n  if(fd == -1) {\n    die(\"Could not open file descriptor\");\n  }\n  ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n  if(ssl_ctx == NULL) {\n    dief(\"SSL_CTX_new\", ERR_error_string(ERR_get_error(), NULL));\n  }\n  init_ssl_ctx(ssl_ctx, &spdy_proto_version);\n  ssl = SSL_new(ssl_ctx);\n  if(ssl == NULL) {\n    dief(\"SSL_new\", ERR_error_string(ERR_get_error(), NULL));\n  }\n  /* To simplify the program, we perform SSL/TLS handshake in blocking\n     I/O. */\n  ssl_handshake(ssl, fd);\n\n  connection.ssl = ssl;\n  connection.want_io = IO_NONE;\n\n  /* Here make file descriptor non-block */\n  make_non_block(fd);\n  set_tcp_nodelay(fd);\n\n  printf(\"[INFO] SPDY protocol version = %d\\n\", spdy_proto_version);\n  rv = spdylay_session_client_new(&connection.session, spdy_proto_version,\n                                  &callbacks, &connection);\n  if(rv != 0) {\n    diec(\"spdylay_session_client_new\", rv);\n  }\n\n  /* Submit the HTTP request to the outbound queue. */\n  submit_request(&connection, &req);\n\n  pollfds[0].fd = fd;\n  ctl_poll(pollfds, &connection);\n\n  /* Event loop */\n  while(spdylay_session_want_read(connection.session) ||\n        spdylay_session_want_write(connection.session)) {\n    int nfds = poll(pollfds, npollfds, -1);\n    if(nfds == -1) {\n      dief(\"poll\", strerror(errno));\n    }\n    if(pollfds[0].revents & (POLLIN | POLLOUT)) {\n      exec_io(&connection);\n    }\n    if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {\n      die(\"Connection error\");\n    }\n    ctl_poll(pollfds, &connection);\n  }\n\n  /* Resource cleanup */\n  spdylay_session_del(connection.session);\n  SSL_shutdown(ssl);\n  SSL_free(ssl);\n  SSL_CTX_free(ssl_ctx);\n  shutdown(fd, SHUT_WR);\n  close(fd);\n  request_free(&req);\n}\n\nstatic int parse_uri(struct URI *res, const char *uri)\n{\n  /* We only interested in https */\n  size_t len, i, offset;\n  int ipv6addr = 0;\n  memset(res, 0, sizeof(struct URI));\n  len = strlen(uri);\n  if(len < 9 || memcmp(\"https://\", uri, 8) != 0) {\n    return -1;\n  }\n  offset = 8;\n  res->host = res->hostport = &uri[offset];\n  res->hostlen = 0;\n  if(uri[offset] == '[') {\n    /* IPv6 literal address */\n    ++offset;\n    ++res->host;\n    ipv6addr = 1;\n    for(i = offset; i < len; ++i) {\n      if(uri[i] == ']') {\n        res->hostlen = i-offset;\n        offset = i+1;\n        break;\n      }\n    }\n  } else {\n    const char delims[] = \":/?#\";\n    for(i = offset; i < len; ++i) {\n      if(strchr(delims, uri[i]) != NULL) {\n        break;\n      }\n    }\n    res->hostlen = i-offset;\n    offset = i;\n  }\n  if(res->hostlen == 0) {\n    return -1;\n  }\n  /* Assuming https */\n  res->port = 443;\n  if(offset < len) {\n    if(uri[offset] == ':') {\n      /* port */\n      const char delims[] = \"/?#\";\n      int port = 0;\n      ++offset;\n      for(i = offset; i < len; ++i) {\n        if(strchr(delims, uri[i]) != NULL) {\n          break;\n        }\n        if('0' <= uri[i] && uri[i] <= '9') {\n          port *= 10;\n          port += uri[i]-'0';\n          if(port > 65535) {\n            return -1;\n          }\n        } else {\n          return -1;\n        }\n      }\n      if(port == 0) {\n        return -1;\n      }\n      offset = i;\n      res->port = port;\n    }\n  }\n  res->hostportlen = uri+offset+ipv6addr-res->host;\n  for(i = offset; i < len; ++i) {\n    if(uri[i] == '#') {\n      break;\n    }\n  }\n  if(i-offset == 0) {\n    res->path = \"/\";\n    res->pathlen = 1;\n  } else {\n    res->path = &uri[offset];\n    res->pathlen = i-offset;\n  }\n  return 0;\n}\n\nint main(int argc, char **argv)\n{\n  struct URI uri;\n  struct sigaction act;\n  int rv;\n\n  if(argc < 2) {\n    die(\"Specify URI\");\n  }\n\n  memset(&act, 0, sizeof(struct sigaction));\n  act.sa_handler = SIG_IGN;\n  sigaction(SIGPIPE, &act, 0);\n\n  SSL_load_error_strings();\n  SSL_library_init();\n\n  rv = parse_uri(&uri, argv[1]);\n  if(rv != 0) {\n    die(\"parse_uri failed\");\n  }\n  fetch_uri(&uri);\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "examples/spdynative.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include <iostream>\n\n#include \"spdy.h\"\n\nint main()\n{\n  spdy server;\n  if(!server.listen(\"localhost\", 8080, \"server.key\", \"server.crt\",\n                    [](request& req, response& res) {\n                      res.set_status(200);\n                      res.set_header(\"content-type\", \"text/plain\");\n                      res.end(\"C++ FTW\\n\");\n                    }))\n    return EXIT_FAILURE;\n\n  std::cout << \"Server running at http://localhost:8080/\" << std::endl;\n  return reactor::run(server);\n}\n"
  },
  {
    "path": "fedora/spdylay.spec",
    "content": "Prefix: %{_usr}\nName: spdylay\nVersion: 0.3.7\nRelease: 1%{?dist}\nSummary: The experimental SPDY protocol version 2 and 3 implementation in C\n\nGroup: System Environment/Libraries\nLicense: MIT\nURL: http://sourceforge.net/projects/spdylay/\nSource0: %{name}-%{version}.tar.gz\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)\n\nBuildRequires: pkgconfig >= 0.20, zlib >= 1.2.3, gcc, gcc-c++, make\nBuildRequires: openssl-devel, CUnit-devel\n\n#Requires:\n\n%description\nThis is an experimental implementation of Google's SPDY protocol in C.\nThis library provides SPDY version 2 and 3 framing layer implementation. It does not\nperform any I/O operations. When the library needs them, it calls the callback functions\nprovided by the application. It also does not include any event polling mechanism,\nso the application can freely choose the way of handling events. This library code does\nnot depend on any particular SSL library (except for example programs which depend on\nOpenSSL 1.0.1 or later).\n\n%package devel\nSummary: Development files for %{name}\nGroup: Development/Libraries\nRequires: %{name} = %{version}-%{release}\n\n%description devel\nThe %{name}-devel package contains libraries and header files for\ndeveloping applications that use %{name}.\n\n%prep\n%setup -q\n\n%build\nautoreconf -i\n%{__automake}\n%{__autoconf}\n%configure --disable-static --enable-examples --disable-xmltest\n%{__make} %{?_smp_mflags}\n\n%install\nrm -rf $RPM_BUILD_ROOT\n%{__make} install DESTDIR=$RPM_BUILD_ROOT\n\n%clean\nrm -rf $RPM_BUILD_ROOT\n\n%post -p /sbin/ldconfig\n\n%postun -p /sbin/ldconfig\n\n%files\n%defattr(-,root,root,-)\n%doc\n%{_libdir}/*.so.*\n%exclude %{_libdir}/*.la\n%{_bindir}/shrpx\n%{_bindir}/spdycat\n%{_bindir}/spdyd\n\n%files devel\n%defattr(-,root,root,-)\n%doc %{_docdir}/%{name}\n%{_includedir}/*\n%{_libdir}/*.so\n%{_libdir}/pkgconfig/*.pc\n\n%changelog\n* Sat Oct 27 2012 Raul Gutierrez Segales <rgs@itevenworks.net> 0.3.7-DEV\n- Initial RPM release.\n"
  },
  {
    "path": "lib/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nSUBDIRS = includes\n\nAM_CFLAGS = -Wall\nAM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes @DEFS@\n\npkgconfigdir = $(libdir)/pkgconfig\npkgconfig_DATA = libspdylay.pc\nDISTCLEANFILES = $(pkgconfig_DATA)\n\nlib_LTLIBRARIES = libspdylay.la\n\nOBJECTS = spdylay_pq.c spdylay_map.c spdylay_queue.c \\\n\tspdylay_buffer.c spdylay_frame.c spdylay_zlib.c \\\n\tspdylay_session.c spdylay_helper.c spdylay_stream.c spdylay_npn.c \\\n\tspdylay_submit.c spdylay_outbound_item.c \\\n\tspdylay_gzip.c\n\nHFILES = spdylay_pq.h spdylay_int.h spdylay_map.h spdylay_queue.h \\\n\tspdylay_buffer.h spdylay_frame.h spdylay_zlib.h \\\n\tspdylay_session.h spdylay_helper.h spdylay_stream.h spdylay_int.h \\\n\tspdylay_npn.h spdylay_gzip.h \\\n\tspdylay_submit.h spdylay_outbound_item.h \\\n\tspdylay_net.h\n\nlibspdylay_la_SOURCES = $(HFILES) $(OBJECTS)\nlibspdylay_la_LDFLAGS = -no-undefined \\\n\t-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)\n"
  },
  {
    "path": "lib/includes/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nnobase_include_HEADERS = spdylay/spdylay.h spdylay/spdylayver.h\n"
  },
  {
    "path": "lib/includes/spdylay/spdylay.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_H\n#define SPDYLAY_H\n\n#ifdef  __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdlib.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <spdylay/spdylayver.h>\n\nstruct spdylay_session;\n/**\n * @struct\n *\n * The primary structure to hold the resources needed for a SPDY\n * session. The details of this structure are intentionally hidden\n * from the public API.\n */\ntypedef struct spdylay_session spdylay_session;\n\n/**\n * @enum\n *\n * The SPDY protocol version.\n */\ntypedef enum {\n  /**\n   * SPDY protocol version 2\n   */\n  SPDYLAY_PROTO_SPDY2 = 2,\n  /**\n   * SPDY protocol version 3\n   */\n  SPDYLAY_PROTO_SPDY3 = 3,\n  /**\n   * SPDY protocol version 3.1\n   */\n  SPDYLAY_PROTO_SPDY3_1 = 4\n} spdylay_proto_version;\n\n/**\n * @enum\n *\n * Error codes used in the Spdylay library. The code range is [-999,\n * -500], inclusive. The following values are defined:\n */\ntypedef enum {\n  /**\n   * Invalid argument passed.\n   */\n  SPDYLAY_ERR_INVALID_ARGUMENT = -501,\n  /**\n   * Zlib error.\n   */\n  SPDYLAY_ERR_ZLIB = -502,\n  /**\n   * The specified protocol version is not supported.\n   */\n  SPDYLAY_ERR_UNSUPPORTED_VERSION = -503,\n  /**\n   * Used as a return value from :type:`spdylay_send_callback` and\n   * :type:`spdylay_recv_callback` to indicate that the operation\n   * would block.\n   */\n  SPDYLAY_ERR_WOULDBLOCK = -504,\n  /**\n   * General protocol error\n   */\n  SPDYLAY_ERR_PROTO = -505,\n  /**\n   * The frame is invalid.\n   */\n  SPDYLAY_ERR_INVALID_FRAME = -506,\n  /**\n   * The peer performed a shutdown on the connection.\n   */\n  SPDYLAY_ERR_EOF = -507,\n  /**\n   * Used as a return value from\n   * :func:`spdylay_data_source_read_callback` to indicate that data\n   * transfer is postponed. See\n   * :func:`spdylay_data_source_read_callback` for details.\n   */\n  SPDYLAY_ERR_DEFERRED = -508,\n  /**\n   * Stream ID has reached the maximum value. Therefore no stream ID\n   * is available.\n   */\n  SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE = -509,\n  /**\n   * The stream is already closed; or the stream ID is invalid.\n   */\n  SPDYLAY_ERR_STREAM_CLOSED = -510,\n  /**\n   * RST_STREAM has been added to the outbound queue. The stream is in\n   * closing state.\n   */\n  SPDYLAY_ERR_STREAM_CLOSING = -511,\n  /**\n   * The transmission is not allowed for this stream (e.g., a frame\n   * with FLAG_FIN flag set has already sent).\n   */\n  SPDYLAY_ERR_STREAM_SHUT_WR = -512,\n  /**\n   * The stream ID is invalid.\n   */\n  SPDYLAY_ERR_INVALID_STREAM_ID = -513,\n  /**\n   * The state of the stream is not valid (e.g., SYN_REPLY cannot be\n   * sent to the stream if SYN_REPLY has already been sent).\n   */\n  SPDYLAY_ERR_INVALID_STREAM_STATE = -514,\n  /**\n   * Another DATA frame has already been deferred.\n   */\n  SPDYLAY_ERR_DEFERRED_DATA_EXIST = -515,\n  /**\n   * SYN_STREAM is not allowed. (e.g., GOAWAY has been sent and/or\n   * received.\n   */\n  SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED = -516,\n  /**\n   * GOAWAY has already been sent.\n   */\n  SPDYLAY_ERR_GOAWAY_ALREADY_SENT = -517,\n  /**\n   * The received frame contains the invalid header block. (e.g.,\n   * There are duplicate header names; or the header names are not\n   * encoded in US-ASCII character set and not lower cased; or the\n   * header name is zero-length string; or the header value contains\n   * multiple in-sequence NUL bytes).\n   */\n  SPDYLAY_ERR_INVALID_HEADER_BLOCK = -518,\n  /**\n   * Indicates that the context is not suitable to perform the\n   * requested operation.\n   */\n  SPDYLAY_ERR_INVALID_STATE = -519,\n  /**\n   * The gzip error.\n   */\n  SPDYLAY_ERR_GZIP = -520,\n  /**\n   * The user callback function failed due to the temporal error.\n   */\n  SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE = -521,\n  /**\n   * The length of the frame is too large.\n   */\n  SPDYLAY_ERR_FRAME_TOO_LARGE = -522,\n  /**\n   * The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is\n   * under unexpected condition and cannot process any further data\n   * reliably (e.g., out of memory).\n   */\n  SPDYLAY_ERR_FATAL = -900,\n  /**\n   * Out of memory. This is a fatal error.\n   */\n  SPDYLAY_ERR_NOMEM = -901,\n  /**\n   * The user callback function failed. This is a fatal error.\n   */\n  SPDYLAY_ERR_CALLBACK_FAILURE = -902\n} spdylay_error;\n\ntypedef enum {\n  SPDYLAY_MSG_MORE\n} spdylay_io_flag;\n\n/**\n * @enum\n * The control frame types in SPDY protocol.\n */\ntypedef enum {\n  /**\n   * The SYN_STREAM control frame.\n   */\n  SPDYLAY_SYN_STREAM = 1,\n  /**\n   * The SYN_REPLY control frame.\n   */\n  SPDYLAY_SYN_REPLY = 2,\n  /**\n   * The RST_STREAM control frame.\n   */\n  SPDYLAY_RST_STREAM = 3,\n  /**\n   * The SETTINGS control frame.\n   */\n  SPDYLAY_SETTINGS = 4,\n  /**\n   * The NOOP control frame. This was deprecated in SPDY/3.\n   */\n  SPDYLAY_NOOP = 5,\n  /**\n   * The PING control frame.\n   */\n  SPDYLAY_PING = 6,\n  /**\n   * The GOAWAY control frame.\n   */\n  SPDYLAY_GOAWAY = 7,\n  /**\n   * The HEADERS control frame.\n   */\n  SPDYLAY_HEADERS = 8,\n  /**\n   * The WINDOW_UPDATE control frame. This first appeared in SPDY/3.\n   */\n  SPDYLAY_WINDOW_UPDATE = 9,\n  /**\n   * The CREDENTIAL control frame. This first appeared in SPDY/3.\n   */\n  SPDYLAY_CREDENTIAL = 10\n} spdylay_frame_type;\n\n/**\n * @enum\n *\n * The flags for a control frame.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  SPDYLAY_CTRL_FLAG_NONE = 0,\n  /**\n   * FLAG_FIN flag.\n   */\n  SPDYLAY_CTRL_FLAG_FIN = 0x1,\n  /**\n   * FLAG_UNIDIRECTIONAL flag.\n   */\n  SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL = 0x2\n} spdylay_ctrl_flag;\n\n/**\n * @enum\n * The flags for a DATA frame.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  SPDYLAY_DATA_FLAG_NONE = 0,\n  /**\n   * FLAG_FIN flag.\n   */\n  SPDYLAY_DATA_FLAG_FIN = 0x1\n} spdylay_data_flag;\n\n/**\n * @enum\n * The flags for the SETTINGS control frame.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  SPDYLAY_FLAG_SETTINGS_NONE = 0,\n  /**\n   * SETTINGS_CLEAR_SETTINGS flag.\n   */\n  SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS = 1\n} spdylay_settings_flag;\n\n/**\n * @enum\n * The flags for SETTINGS ID/value pair.\n */\ntypedef enum {\n  /**\n   * No flag set.\n   */\n  SPDYLAY_ID_FLAG_SETTINGS_NONE = 0,\n  /**\n   * FLAG_SETTINGS_PERSIST_VALUE flag.\n   */\n  SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE = 1,\n  /**\n   * FLAG_SETTINGS_PERSISTED flag.\n   */\n  SPDYLAY_ID_FLAG_SETTINGS_PERSISTED = 2\n} spdylay_settings_id_flag;\n\n/**\n * @enum\n * The SETTINGS ID.\n */\ntypedef enum {\n  /**\n   * SETTINGS_UPLOAD_BANDWIDTH\n   */\n  SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH = 1,\n  /**\n   * SETTINGS_DOWNLOAD_BANDWIDTH\n   */\n  SPDYLAY_SETTINGS_DOWNLOAD_BANDWIDTH = 2,\n  /**\n   * SETTINGS_ROUND_TRIP_TIME\n   */\n  SPDYLAY_SETTINGS_ROUND_TRIP_TIME = 3,\n  /**\n   * SETTINGS_MAX_CONCURRENT_STREAMS\n   */\n  SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS = 4,\n  /**\n   * SETTINGS_CURRENT_CWND\n   */\n  SPDYLAY_SETTINGS_CURRENT_CWND = 5,\n  /**\n   * SETTINGS_DOWNLOAD_RETRANS_RATE\n   */\n  SPDYLAY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6,\n  /**\n   * SETTINGS_INITIAL_WINDOW_SIZE\n   */\n  SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE = 7,\n  /**\n   * SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE. This first appeared in\n   * SPDY/3.\n   */\n  SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8,\n  /**\n   * Maximum ID of :type:`spdylay_settings_id`.\n   */\n  SPDYLAY_SETTINGS_MAX = 8\n} spdylay_settings_id;\n\n/**\n * @macro\n * Default maximum concurrent streams.\n */\n#define SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)\n\n/**\n * @macro\n *\n * Initial window size for both connection-level and stream-level flow\n * control.\n */\n#define SPDYLAY_INITIAL_WINDOW_SIZE 65536\n\n/**\n * @enum\n * The status codes for the RST_STREAM control frame.\n */\ntypedef enum {\n  /**\n   * SPDYLAY_OK is not valid status code for RST_STREAM. It is defined\n   * just for spdylay library use.\n   */\n  SPDYLAY_OK = 0,\n  /**\n   * PROTOCOL_ERROR\n   */\n  SPDYLAY_PROTOCOL_ERROR = 1,\n  /**\n   * INVALID_STREAM\n   */\n  SPDYLAY_INVALID_STREAM = 2,\n  /**\n   * REFUSED_STREAM\n   */\n  SPDYLAY_REFUSED_STREAM = 3,\n  /**\n   * UNSUPPORTED_VERSION\n   */\n  SPDYLAY_UNSUPPORTED_VERSION = 4,\n  /**\n   * CANCEL\n   */\n  SPDYLAY_CANCEL = 5,\n  /**\n   * INTERNAL_ERROR\n   */\n  SPDYLAY_INTERNAL_ERROR = 6,\n  /**\n   * FLOW_CONTROL_ERROR\n   */\n  SPDYLAY_FLOW_CONTROL_ERROR = 7,\n  /* Following status codes were introduced in SPDY/3 */\n  /**\n   * STREAM_IN_USE\n   */\n  SPDYLAY_STREAM_IN_USE = 8,\n  /**\n   * STREAM_ALREADY_CLOSED\n   */\n  SPDYLAY_STREAM_ALREADY_CLOSED = 9,\n  /**\n   * INVALID_CREDENTIALS\n   */\n  SPDYLAY_INVALID_CREDENTIALS = 10,\n  /**\n   * FRAME_TOO_LARGE\n   */\n  SPDYLAY_FRAME_TOO_LARGE = 11\n} spdylay_status_code;\n\n/**\n * @enum\n * The status codes for GOAWAY, introduced in SPDY/3.\n */\ntypedef enum {\n  /**\n   * OK. This indicates a normal session teardown.\n   */\n  SPDYLAY_GOAWAY_OK = 0,\n  /**\n   * PROTOCOL_ERROR\n   */\n  SPDYLAY_GOAWAY_PROTOCOL_ERROR = 1,\n  /**\n   * INTERNAL_ERROR\n   */\n  SPDYLAY_GOAWAY_INTERNAL_ERROR = 2\n} spdylay_goaway_status_code;\n\n/**\n * @struct\n * The control frame header.\n */\ntypedef struct {\n  /**\n   * SPDY protocol version.\n   */\n  uint16_t version;\n  /**\n   * The type of this control frame.\n   */\n  uint16_t type;\n  /**\n   * The control frame flags.\n   */\n  uint8_t flags;\n  /**\n   * The length field of this control frame.\n   */\n  int32_t length;\n} spdylay_ctrl_hd;\n\n/**\n * @struct\n * The SYN_STREAM control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The associated-to-stream ID. 0 if this frame has no\n   * associated-to-stream.\n   */\n  int32_t assoc_stream_id;\n  /**\n   * The priority of this frame. 0 is the highest priority value. Use\n   * `spdylay_session_get_pri_lowest()` to know the lowest priority\n   * value.\n   */\n  uint8_t pri;\n  /**\n   * The index in server's CREDENTIAL vector of the client certificate.\n   * This was introduced in SPDY/3.\n   */\n  uint8_t slot;\n  /**\n   * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer\n   * to the name string and ``nv[2*i+1]`` contains a pointer to the\n   * value string. The one beyond last value must be ``NULL``. That\n   * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be\n   * ``NULL``. This member may be ``NULL``.\n   */\n  char **nv;\n} spdylay_syn_stream;\n\n/**\n * @struct\n * The SYN_REPLY control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer\n   * to the name string and ``nv[2*i+1]`` contains a pointer to the\n   * value string. The one beyond last value must be ``NULL``. That\n   * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be\n   * ``NULL``. This member may be ``NULL``.\n   */\n  char **nv;\n} spdylay_syn_reply;\n\n/**\n * @struct\n * The HEADERS control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The name/value pairs. For i >= 0, ``nv[2*i]`` contains a pointer\n   * to the name string and ``nv[2*i+1]`` contains a pointer to the\n   * value string. The one beyond last value must be ``NULL``. That\n   * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be\n   * ``NULL``. This member may be ``NULL``.\n   */\n  char **nv;\n} spdylay_headers;\n\n/**\n * @struct\n * The RST_STREAM control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The status code. See :type:`spdylay_status_code`.\n   */\n  uint32_t status_code;\n} spdylay_rst_stream;\n\n/**\n * @struct\n * The SETTINGS ID/Value pair. It has the following members:\n */\ntypedef struct {\n  /**\n   * The SETTINGS ID. See :type:`spdylay_settings_id`.\n   */\n  int32_t settings_id;\n  /**\n   * The flags. See :type:`spdylay_settings_id_flag`.\n   */\n  uint8_t flags;\n  /**\n   * The value of this entry.\n   */\n  uint32_t value;\n} spdylay_settings_entry;\n\n/**\n * @struct\n * The SETTINGS control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The number of SETTINGS ID/Value pairs in |iv|.\n   */\n  size_t niv;\n  /**\n   * The pointer to the array of SETTINGS ID/Value pair.\n   */\n  spdylay_settings_entry *iv;\n} spdylay_settings;\n\n/**\n * @struct\n * The PING control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The unique ID.\n   */\n  uint32_t unique_id;\n} spdylay_ping;\n\n/**\n * @struct\n * The GOAWAY control frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The last-good-stream ID.\n   */\n  int32_t last_good_stream_id;\n  /**\n   * The status code. This first appeared in SPDY/3. See\n   * :type:`spdylay_goaway_status_code`.\n   */\n  uint32_t status_code;\n} spdylay_goaway;\n\n/**\n * @struct\n *\n * The WINDOW_UPDATE control frame. This first appeared in SPDY/3.  It\n * has the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The delta-window-size.\n   */\n  int32_t delta_window_size;\n} spdylay_window_update;\n\n/**\n * @struct\n *\n * The structure to hold chunk of memory.\n */\ntypedef struct {\n  /**\n   * The pointer to the data.\n   */\n  uint8_t *data;\n  /**\n   * The length of the data.\n   */\n  size_t length;\n} spdylay_mem_chunk;\n\n/**\n * @struct\n *\n * The CREDENTIAL control frame. This first appeared in SPDY/3. It has\n * the following members:\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n  /**\n   * The index in the client certificate vector.\n   */\n  uint16_t slot;\n  /**\n   * Cryptographic proof that the client has possession of the private\n   * key associated with the certificate.\n   */\n  spdylay_mem_chunk proof;\n  /**\n   * The certificate chain. The certs[0] is the leaf certificate.\n   */\n  spdylay_mem_chunk *certs;\n  /**\n   * The number of certificates in |certs|.\n   */\n  size_t ncerts;\n} spdylay_credential;\n\n/**\n * @struct\n *\n * Convenient structure to inspect control frame header.  It is useful\n * to get the frame type.\n */\ntypedef struct {\n  /**\n   * The control frame header.\n   */\n  spdylay_ctrl_hd hd;\n} spdylay_ctrl_frame;\n\n/**\n * @union\n *\n * This union represents the some kind of data source passed to\n * :type:`spdylay_data_source_read_callback`.\n */\ntypedef union {\n  /**\n   * The integer field, suitable for a file descriptor.\n   */\n  int fd;\n  /**\n   * The pointer to an arbitrary object.\n   */\n  void *ptr;\n} spdylay_data_source;\n\n/**\n * @functypedef\n *\n * Callback function invoked when the library wants to read data from\n * the |source|. The read data is sent in the stream |stream_id|. The\n * implementation of this function must read at most |length| bytes of\n * data from |source| (or possibly other places) and store them in\n * |buf| and return number of data stored in |buf|. If EOF is reached,\n * set |*eof| to 1.  If the application wants to postpone DATA frames,\n * (e.g., asynchronous I/O, or reading data blocks for long time), it\n * is achieved by returning :enum:`SPDYLAY_ERR_DEFERRED` without\n * reading any data in this invocation.  The library removes DATA\n * frame from the outgoing queue temporarily.  To move back deferred\n * DATA frame to outgoing queue, call `spdylay_session_resume_data()`.\n * In case of error, there are 2 choices. Returning\n * :enum:`SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream\n * by issuing RST_STREAM with :enum:`SPDYLAY_INTERNAL_ERROR`.\n * Returning :enum:`SPDYLAY_ERR_CALLBACK_FAILURE` will signal the\n * entire session failure.\n */\ntypedef ssize_t (*spdylay_data_source_read_callback)\n(spdylay_session *session, int32_t stream_id,\n uint8_t *buf, size_t length, int *eof,\n spdylay_data_source *source, void *user_data);\n\n/**\n * @struct\n *\n * This struct represents the data source and the way to read a chunk\n * of data from it.\n */\ntypedef struct {\n  /**\n   * The data source.\n   */\n  spdylay_data_source source;\n  /**\n   * The callback function to read a chunk of data from the |source|.\n   */\n  spdylay_data_source_read_callback read_callback;\n} spdylay_data_provider;\n\n/**\n * @union\n *\n * This union includes all control frames to pass them\n * to various function calls as spdylay_frame type.\n */\ntypedef union {\n  /**\n   * Convenient structure to inspect control frame header.\n   */\n  spdylay_ctrl_frame ctrl;\n  /**\n   * The SYN_STREAM control frame.\n   */\n  spdylay_syn_stream syn_stream;\n  /**\n   * The SYN_REPLY control frame.\n   */\n  spdylay_syn_reply syn_reply;\n  /**\n   * The RST_STREAM control frame.\n   */\n  spdylay_rst_stream rst_stream;\n  /**\n   * The SETTINGS control frame.\n   */\n  spdylay_settings settings;\n  /**\n   * The PING control frame.\n   */\n  spdylay_ping ping;\n  /**\n   * The GOAWAY control frame.\n   */\n  spdylay_goaway goaway;\n  /**\n   * The HEADERS control frame.\n   */\n  spdylay_headers headers;\n  /**\n   * The WINDOW_UPDATE control frame.\n   */\n  spdylay_window_update window_update;\n  /**\n   * The CREDENTIAL control frame.\n   */\n  spdylay_credential credential;\n} spdylay_frame;\n\n/**\n * @functypedef\n *\n * Callback function invoked when |session| wants to send data to the\n * remote peer. The implementation of this function must send at most\n * |length| bytes of data stored in |data|. The |flags| is currently\n * not used and always 0. It must return the number of bytes sent if\n * it succeeds.  If it cannot send any single byte without blocking,\n * it must return :enum:`SPDYLAY_ERR_WOULDBLOCK`. For other errors, it\n * must return :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`.\n */\ntypedef ssize_t (*spdylay_send_callback)\n(spdylay_session *session,\n const uint8_t *data, size_t length, int flags, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when |session| wants to receive data from\n * the remote peer. The implementation of this function must read at\n * most |length| bytes of data and store it in |buf|. The |flags| is\n * currently not used and always 0. It must return the number of bytes\n * written in |buf| if it succeeds. If it cannot read any single byte\n * without blocking, it must return :enum:`SPDYLAY_ERR_WOULDBLOCK`. If\n * it gets EOF before it reads any single byte, it must return\n * :enum:`SPDYLAY_ERR_EOF`. For other errors, it must return\n * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`.\n */\ntypedef ssize_t (*spdylay_recv_callback)\n(spdylay_session *session,\n uint8_t *buf, size_t length, int flags, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked by `spdylay_session_recv()` when a\n * control frame is received.\n */\ntypedef void (*spdylay_on_ctrl_recv_callback)\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked by `spdylay_session_recv()` when an\n * invalid control frame is received. The |status_code| is one of the\n * :enum:`spdylay_status_code` and indicates the error. When this\n * callback function is invoked, the library automatically submits\n * either RST_STREAM or GOAWAY frame.\n */\ntypedef void (*spdylay_on_invalid_ctrl_recv_callback)\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n uint32_t status_code, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when a chunk of data in DATA frame is\n * received. The |stream_id| is the stream ID this DATA frame belongs\n * to. The |flags| is the flags of DATA frame which this data chunk is\n * contained. ``(flags & SPDYLAY_DATA_FLAG_FIN) != 0`` does not\n * necessarily mean this chunk of data is the last one in the\n * stream. You should use :type:`spdylay_on_data_recv_callback` to\n * know all data frames are received.\n */\ntypedef void (*spdylay_on_data_chunk_recv_callback)\n(spdylay_session *session, uint8_t flags, int32_t stream_id,\n const uint8_t *data, size_t len, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when DATA frame is received. The actual\n * data it contains are received by\n * :type:`spdylay_on_data_chunk_recv_callback`.\n */\ntypedef void (*spdylay_on_data_recv_callback)\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked before the control frame |frame| of type\n * |type| is sent. This may be useful, for example, to know the stream\n * ID of SYN_STREAM frame (see also\n * `spdylay_session_get_stream_user_data()`), which is not assigned\n * when it was queued.\n */\ntypedef void (*spdylay_before_ctrl_send_callback)\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked after the control frame |frame| of type\n * |type| is sent.\n */\ntypedef void (*spdylay_on_ctrl_send_callback)\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked after the control frame |frame| of type\n * |type| is not sent because of the error. The error is indicated by\n * the |error_code|, which is one of the values defined in\n * :type:`spdylay_error`.\n */\ntypedef void (*spdylay_on_ctrl_not_send_callback)\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n int error_code, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked after DATA frame is sent.\n */\ntypedef void (*spdylay_on_data_send_callback)\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the stream |stream_id| is\n * closed. The reason of closure is indicated by the\n * |status_code|. The stream_user_data, which was specified in\n * `spdylay_submit_request()` or `spdylay_submit_syn_stream()`, is\n * still available in this function.\n */\ntypedef void (*spdylay_on_stream_close_callback)\n(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,\n void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the request from the remote peer is\n * received.  In other words, the frame with FIN flag set is received.\n * In HTTP, this means HTTP request, including request body, is fully\n * received.\n */\ntypedef void (*spdylay_on_request_recv_callback)\n(spdylay_session *session, int32_t stream_id, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the received control frame octets\n * could not be parsed correctly. The |type| indicates the type of\n * received control frame. The |head| is the pointer to the header of\n * the received frame. The |headlen| is the length of the\n * |head|. According to the SPDY spec, the |headlen| is always 8. In\n * other words, the |head| is the first 8 bytes of the received frame.\n * The |payload| is the pointer to the data portion of the received\n * frame.  The |payloadlen| is the length of the |payload|. This is\n * the data after the length field. The |error_code| is one of the\n * error code defined in :enum:`spdylay_error` and indicates the\n * error.\n */\ntypedef void (*spdylay_on_ctrl_recv_parse_error_callback)\n(spdylay_session *session, spdylay_frame_type type,\n const uint8_t *head, size_t headlen,\n const uint8_t *payload, size_t payloadlen,\n int error_code, void *user_data);\n\n/**\n * @functypedef\n *\n * Callback function invoked when the received control frame type is\n * unknown. The |head| is the pointer to the header of the received\n * frame. The |headlen| is the length of the |head|. According to the\n * SPDY spec, the |headlen| is always 8. In other words, the |head| is\n * the first 8 bytes of the received frame.  The |payload| is the\n * pointer to the data portion of the received frame.  The\n * |payloadlen| is the length of the |payload|. This is the data after\n * the length field.\n */\ntypedef void (*spdylay_on_unknown_ctrl_recv_callback)\n(spdylay_session *session,\n const uint8_t *head, size_t headlen,\n const uint8_t *payload, size_t payloadlen,\n void *user_data);\n\n#define SPDYLAY_MAX_SCHEME 255\n#define SPDYLAY_MAX_HOSTNAME 255\n\nstruct spdylay_origin;\n\n/**\n * @struct\n *\n * The Web origin structure. The origin is the tuple (scheme, host,\n * port). The details of this structure is intentionally hidden. To\n * access these members, use accessor functions below.\n */\ntypedef struct spdylay_origin spdylay_origin;\n\n/**\n * @function\n *\n * Returns the scheme member of the |origin|.\n */\nconst char* spdylay_origin_get_scheme(const spdylay_origin *origin);\n\n/**\n * @function\n *\n * Returns the host member of the |origin|.\n */\nconst char* spdylay_origin_get_host(const spdylay_origin *origin);\n\n/**\n * @function\n *\n * Returns the port member of the |origin|.\n */\nuint16_t spdylay_origin_get_port(const spdylay_origin *origin);\n\n/**\n * @functypedef\n *\n * .. warning::\n *\n *    CREDENTIAL functionality was removed.  This interface is\n *    preserved just for ABI compatibility.  Don't use this function.\n *    We guess no one use CREDENTIAL since it was a catastrophic\n *    failure.\n *\n * Callback function invoked when the library needs the cryptographic\n * proof that the client has possession of the private key associated\n * with the certificate for the given |origin|.  If called with\n * |prooflen| == 0, the implementation of this function must return\n * the length of the proof in bytes. If called with |prooflen| > 0,\n * write proof into |proof| exactly |prooflen| bytes and return 0.\n *\n * Because the client certificate vector has limited number of slots,\n * the application code may be required to pass the same proof more\n * than once.\n */\ntypedef ssize_t (*spdylay_get_credential_proof)\n(spdylay_session *session, const spdylay_origin *origin,\n uint8_t *proof, size_t prooflen, void *user_data);\n\n/**\n * @functypedef\n *\n * .. warning::\n *\n *    CREDENTIAL functionality was removed.  This interface is\n *    preserved just for ABI compatibility.  Don't use this function.\n *    We guess no one use CREDENTIAL since it was a catastrophic\n *    failure.\n *\n * Callback function invoked when the library needs the length of the\n * client certificate chain for the given |origin|.  The\n * implementation of this function must return the length of the\n * client certificate chain.  If no client certificate is required for\n * the given |origin|, return 0.  If positive integer is returned,\n * :type:`spdylay_get_credential_proof` and\n * :type:`spdylay_get_credential_cert` callback functions will be used\n * to get the cryptographic proof and certificate respectively.\n */\ntypedef ssize_t (*spdylay_get_credential_ncerts)\n(spdylay_session *session, const spdylay_origin *origin, void *user_data);\n\n/**\n * @functypedef\n *\n * .. warning::\n *\n *    CREDENTIAL functionality was removed.  This interface is\n *    preserved just for ABI compatibility.  Don't use this function.\n *    We guess no one use CREDENTIAL since it was a catastrophic\n *    failure.\n *\n * Callback function invoked when the library needs the client\n * certificate for the given |origin|. The |idx| is the index of the\n * certificate chain and 0 means the leaf certificate of the chain.\n * If called with |certlen| == 0, the implementation of this function\n * must return the length of the certificate in bytes. If called with\n * |certlen| > 0, write certificate into |cert| exactly |certlen|\n * bytes and return 0.\n */\ntypedef ssize_t (*spdylay_get_credential_cert)\n(spdylay_session *session, const spdylay_origin *origin, size_t idx,\n uint8_t *cert, size_t certlen, void *user_data);\n\n/**\n * @struct\n *\n * Callback functions.\n */\ntypedef struct {\n  /**\n   * Callback function invoked when the |session| wants to send data\n   * to the remote peer.\n   */\n  spdylay_send_callback send_callback;\n  /**\n   * Callback function invoked when the |session| wants to receive\n   * data from the remote peer.\n   */\n  spdylay_recv_callback recv_callback;\n  /**\n   * Callback function invoked by `spdylay_session_recv()` when a\n   * control frame is received.\n   */\n  spdylay_on_ctrl_recv_callback on_ctrl_recv_callback;\n  /**\n   * Callback function invoked by `spdylay_session_recv()` when an\n   * invalid control frame is received.\n   */\n  spdylay_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback;\n  /**\n   * Callback function invoked when a chunk of data in DATA frame is\n   * received.\n   */\n  spdylay_on_data_chunk_recv_callback on_data_chunk_recv_callback;\n  /**\n   * Callback function invoked when DATA frame is received.\n   */\n  spdylay_on_data_recv_callback on_data_recv_callback;\n  /**\n   * Callback function invoked before the control frame is sent.\n   */\n  spdylay_before_ctrl_send_callback before_ctrl_send_callback;\n  /**\n   * Callback function invoked after the control frame is sent.\n   */\n  spdylay_on_ctrl_send_callback on_ctrl_send_callback;\n  /**\n   * The callback function invoked when a control frame is not sent\n   * because of an error.\n   */\n  spdylay_on_ctrl_not_send_callback on_ctrl_not_send_callback;\n  /**\n   * Callback function invoked after DATA frame is sent.\n   */\n  spdylay_on_data_send_callback on_data_send_callback;\n  /**\n   * Callback function invoked when the stream is closed.\n   */\n  spdylay_on_stream_close_callback on_stream_close_callback;\n  /**\n   * Callback function invoked when request from the remote peer is\n   * received.\n   */\n  spdylay_on_request_recv_callback on_request_recv_callback;\n  /**\n   * .. warning::\n   *\n   *    CREDENTIAL functionality was removed.  This interface is\n   *    preserved just for ABI compatibility.  Don't use this function.\n   *    We guess no one use CREDENTIAL since it was a catastrophic\n   *    failure.\n   *\n   * Callback function invoked when the library needs the\n   * cryptographic proof that the client has possession of the private\n   * key associated with the certificate.\n   */\n  spdylay_get_credential_proof get_credential_proof;\n  /**\n   * .. warning::\n   *\n   *    CREDENTIAL functionality was removed.  This interface is\n   *    preserved just for ABI compatibility.  Don't use this function.\n   *    We guess no one use CREDENTIAL since it was a catastrophic\n   *    failure.\n   *\n   * Callback function invoked when the library needs the length of the\n   * client certificate chain.\n   */\n  spdylay_get_credential_ncerts get_credential_ncerts;\n  /**\n   * .. warning::\n   *\n   *    CREDENTIAL functionality was removed.  This interface is\n   *    preserved just for ABI compatibility.  Don't use this function.\n   *    We guess no one use CREDENTIAL since it was a catastrophic\n   *    failure.\n   *\n   * Callback function invoked when the library needs the client\n   * certificate.\n   */\n  spdylay_get_credential_cert get_credential_cert;\n  /**\n   * Callback function invoked when the received control frame octets\n   * could not be parsed correctly.\n   */\n  spdylay_on_ctrl_recv_parse_error_callback on_ctrl_recv_parse_error_callback;\n  /**\n   * Callback function invoked when the received control frame type is\n   * unknown.\n   */\n  spdylay_on_unknown_ctrl_recv_callback on_unknown_ctrl_recv_callback;\n} spdylay_session_callbacks;\n\n/**\n * @function\n *\n * Initializes |*session_ptr| for client use, using the protocol\n * version |version|. The all members of |callbacks| are copied to\n * |*session_ptr|. Therefore |*session_ptr| does not store\n * |callbacks|. |user_data| is an arbitrary user supplied data, which\n * will be passed to the callback functions.\n *\n * The :member:`spdylay_session_callbacks.send_callback` must be\n * specified.  If the application code uses `spdylay_session_recv()`,\n * the :member:`spdylay_session_callbacks.recv_callback` must be\n * specified. The other members of |callbacks| can be ``NULL``.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n * :enum:`SPDYLAY_ERR_ZLIB`\n *     The z_stream initialization failed.\n * :enum:`SPDYLAY_ERR_UNSUPPORTED_VERSION`\n *     The version is not supported.\n */\nint spdylay_session_client_new(spdylay_session **session_ptr,\n                               uint16_t version,\n                               const spdylay_session_callbacks *callbacks,\n                               void *user_data);\n\n/**\n * @function\n *\n * Initializes |*session_ptr| for server use, using the protocol\n * version |version|. The all members of |callbacks| are copied to\n * |*session_ptr|. Therefore |*session_ptr| does not store\n * |callbacks|. |user_data| is an arbitrary user supplied data, which\n * will be passed to the callback functions.\n *\n * The :member:`spdylay_session_callbacks.send_callback` must be\n * specified.  If the application code uses `spdylay_session_recv()`,\n * the :member:`spdylay_session_callbacks.recv_callback` must be\n * specified. The other members of |callbacks| can be ``NULL``.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n * :enum:`SPDYLAY_ERR_ZLIB`\n *     The z_stream initialization failed.\n * :enum:`SPDYLAY_ERR_UNSUPPORTED_VERSION`\n *     The version is not supported.\n */\nint spdylay_session_server_new(spdylay_session **session_ptr,\n                               uint16_t version,\n                               const spdylay_session_callbacks *callbacks,\n                               void *user_data);\n\n/**\n * @function\n *\n * Frees any resources allocated for |session|. If |session| is\n * ``NULL``, this function does nothing.\n */\nvoid spdylay_session_del(spdylay_session *session);\n\n/**\n * @enum\n *\n * Configuration options for :type:`spdylay_session`.\n */\ntypedef enum {\n  /**\n   * This option prevents the library from sending WINDOW_UPDATE\n   * automatically. If this option is set, the application is\n   * responsible for sending WINDOW_UPDATE using\n   * `spdylay_submit_window_update`.  This option was deprecated and\n   * the newly written application should use\n   * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2`.\n   */\n  SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE = 1,\n  /**\n   * This option sets maximum receive buffer size for incoming control\n   * frame.\n   */\n  SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER = 2,\n  /**\n   * This option prevents the library from sending WINDOW_UPDATE\n   * automatically.  If this option is set, the application is\n   * responsible to inform the library of the consumed bytes using\n   * `spdylay_session_consume()`.\n   */\n  SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2 = 3\n} spdylay_opt;\n\n/**\n * @function\n *\n * Sets the configuration option for the |session|.  The |optname| is\n * one of :type:`spdylay_opt`. The |optval| is the pointer to the\n * option value and the |optlen| is the size of |*optval|. The\n * required type of |optval| varies depending on the |optname|. See\n * below.\n *\n * The following |optname| are supported:\n *\n * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE`\n *     The |optval| must be a pointer to ``int``. If the |*optval| is\n *     nonzero, the library will not send WINDOW_UPDATE automatically.\n *     Therefore, the application is responsible for sending\n *     WINDOW_UPDATE using `spdylay_submit_window_update`. This option\n *     defaults to 0.  This option was deprecated and the newly\n *     written application should use\n *     :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2`.\n *\n * :enum:`SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER`\n *     The |optval| must be a pointer to ``uint32_t``. The |*optval|\n *     must be in the range [(1 << 13), (1 << 24)-1], inclusive. This\n *     option defaults to (1 << 24)-1.\n *\n * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2`\n *     The |optval| must be a pointer to ``int``. If the |*optval| is\n *     nonzero, the library will not send WINDOW_UPDATE automatically.\n *     Therefore, the application is responsible to inform the library\n *     of consumed bytes using `spdylay_session_consume()`.  This\n *     option defaults to 0.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |optname| is not supported; or the |optval| and/or the\n *     |optlen| are invalid.\n */\nint spdylay_session_set_option(spdylay_session *session,\n                               int optname, void *optval, size_t optlen);\n\n/**\n * @function\n *\n * .. warning::\n *\n *    CREDENTIAL functionality was removed.  This interface is\n *    preserved just for ABI compatibility.  Don't use this function.\n *    We guess no one use CREDENTIAL since it was a catastrophic\n *    failure.  This function does nothing, and just always returns 0.\n *\n * Sets the origin tuple (|scheme|, |host| and |port|) that the\n * connection is made to and the client certificate is sent in the\n * first TLS handshake. This function must be called before any call\n * of `spdylay_session_send()` and `spdylay_session_recv()` and be\n * called only once per session. This function must not be called if\n * the |session| is initialized for server use. If the client did not\n * provide the client certificate in the first TLS handshake, this\n * function must not be called.\n *\n * This function stores the given origin at the slot 1 in the client\n * certificate vector.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory\n * :enum:`SPDYLAY_ERR_INVALID_STATE`\n *     The |session| is initialized for server use; or the client\n *     certificate vector size is 0.\n */\nint spdylay_session_set_initial_client_cert_origin(spdylay_session *session,\n                                                   const char *scheme,\n                                                   const char *host,\n                                                   uint16_t port);\n\n/**\n * @function\n *\n * .. warning::\n *\n *    CREDENTIAL functionality was removed.  This interface is\n *    preserved just for ABI compatibility.  Don't use this function.\n *    We guess no one use CREDENTIAL since it was a catastrophic\n *    failure.  This function does nothing, and just always returns\n *    NULL.\n *\n * Returns the origin at the index |slot| in the client certificate\n * vector. If there is no origin at the given |slot|, this function\n * returns ``NULL``.\n *\n * This function must not be called if the |session| is initialized\n * for server use.\n */\nconst spdylay_origin* spdylay_session_get_client_cert_origin\n(spdylay_session *session,\n size_t slot);\n\n/**\n * @function\n *\n * Sends pending frames to the remote peer.\n *\n * This function retrieves the highest prioritized frame from the\n * outbound queue and sends it to the remote peer. It does this as\n * many as possible until the user callback\n * :member:`spdylay_session_callbacks.send_callback` returns\n * :enum:`SPDYLAY_ERR_WOULDBLOCK` or the outbound queue becomes empty.\n * This function calls several callback functions which are passed\n * when initializing the |session|. Here is the simple time chart\n * which tells when each callback is invoked:\n *\n * 1. Get the next frame to send from outbound queue.\n * 2. Prepare transmission of the frame.\n * 3. If the control frame cannot be sent because some preconditions\n *    are not met (e.g., SYN_STREAM cannot be sent after GOAWAY),\n *    :member:`spdylay_session_callbacks.on_ctrl_not_send_callback` is\n *    invoked. Abort the following steps.\n * 4. If the frame is SYN_STREAM, the stream is opened here.\n * 5. :member:`spdylay_session_callbacks.before_ctrl_send_callback` is\n *    invoked.\n * 6. :member:`spdylay_session_callbacks.send_callback` is invoked one\n *    or more times to send the frame.\n * 7. If the frame is a control frame,\n *    :member:`spdylay_session_callbacks.on_ctrl_send_callback` is\n *    invoked.\n * 8. If the frame is a DATA frame,\n *    :member:`spdylay_session_callbacks.on_data_send_callback` is\n *    invoked.\n * 9. If the transmission of the frame triggers closure of the stream,\n *    the stream is closed and\n *    :member:`spdylay_session_callbacks.on_stream_close_callback` is\n *    invoked.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n */\nint spdylay_session_send(spdylay_session *session);\n\n/**\n * @function\n *\n * Receives frames from the remote peer.\n *\n * This function receives as many frames as possible until the user\n * callback :member:`spdylay_session_callbacks.recv_callback` returns\n * :enum:`SPDYLAY_ERR_WOULDBLOCK`. This function calls several\n * callback functions which are passed when initializing the\n * |session|. Here is the simple time chart which tells when each\n * callback is invoked:\n *\n * 1. :member:`spdylay_session_callbacks.recv_callback` is invoked one\n *    or more times to receive frame header.\n * 2. If the frame is DATA frame:\n *\n *    1. :member:`spdylay_session_callbacks.recv_callback` is invoked\n *       to receive DATA payload. For each chunk of data,\n *       :member:`spdylay_session_callbacks.on_data_chunk_recv_callback`\n *       is invoked.\n *    2. If one DATA frame is completely received,\n *       :member:`spdylay_session_callbacks.on_data_recv_callback` is\n *       invoked.  If the frame is the final frame of the request,\n *       :member:`spdylay_session_callbacks.on_request_recv_callback`\n *       is invoked.  If the reception of the frame triggers the\n *       closure of the stream,\n *       :member:`spdylay_session_callbacks.on_stream_close_callback`\n *       is invoked.\n *\n * 3. If the frame is the control frame:\n *\n *    1. :member:`spdylay_session_callbacks.recv_callback` is invoked\n *       one or more times to receive whole frame.\n *    2. If the received frame is valid,\n *       :member:`spdylay_session_callbacks.on_ctrl_recv_callback` is\n *       invoked.  If the frame is the final frame of the request,\n *       :member:`spdylay_session_callbacks.on_request_recv_callback`\n *       is invoked.  If the reception of the frame triggers the\n *       closure of the stream,\n *       :member:`spdylay_session_callbacks.on_stream_close_callback`\n *       is invoked.\n *    3. If the received frame is unpacked but is interpreted as\n *       invalid,\n *       :member:`spdylay_session_callbacks.on_invalid_ctrl_recv_callback`\n *       is invoked.\n *    4. If the received frame could not be unpacked correctly,\n *       :member:`spdylay_session_callbacks.on_ctrl_recv_parse_error_callback`\n *       is invoked.\n *    5. If the received frame type is unknown,\n *       :member:`spdylay_session_callbacks.on_unknown_ctrl_recv_callback`\n *       is invoked.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_EOF`\n *     The remote peer did shutdown on the connection.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`\n *     The callback function failed.\n */\nint spdylay_session_recv(spdylay_session *session);\n\n/**\n * @function\n *\n * Processes data |in| as an input from the remote endpoint. The\n * |inlen| indicates the number of bytes in the |in|.\n *\n * This function behaves like `spdylay_session_recv()` except that it\n * does not use :member:`spdylay_session_callbacks.recv_callback` to\n * receive data; the |in| is the only data for the invocation of this\n * function. If all bytes are processed, this function returns. The\n * other callbacks are called in the same way as they are in\n * `spdylay_session_recv()`.\n *\n * In the current implementation, this function always tries to\n * processes all input data unless an error occurs.\n *\n * This function returns the number of processed bytes, or one of the\n * following negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nssize_t spdylay_session_mem_recv(spdylay_session *session,\n                                 const uint8_t *in, size_t inlen);\n\n/**\n * @function\n *\n * Puts back previously deferred DATA frame in the stream |stream_id|\n * to the outbound queue.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The stream does not exist or no deferred data exist.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_session_resume_data(spdylay_session *session, int32_t stream_id);\n\n/**\n * @function\n *\n * Returns nonzero value if |session| wants to receive data from the\n * remote peer.\n *\n * If both `spdylay_session_want_read()` and\n * `spdylay_session_want_write()` return 0, the application should\n * drop the connection.\n */\nint spdylay_session_want_read(spdylay_session *session);\n\n/**\n * @function\n *\n * Returns nonzero value if |session| wants to send data to the remote\n * peer.\n *\n * If both `spdylay_session_want_read()` and\n * `spdylay_session_want_write()` return 0, the application should\n * drop the connection.\n */\nint spdylay_session_want_write(spdylay_session *session);\n\n/**\n * @function\n *\n * Returns stream_user_data for the stream |stream_id|. The\n * stream_user_data is provided by `spdylay_submit_request()` or\n * `spdylay_submit_syn_stream()`.  If the stream is initiated by the\n * remote endpoint, stream_user_data is always ``NULL``. If the stream\n * is initiated by the local endpoint and ``NULL`` is given in\n * `spdylay_submit_request()` or `spdylay_submit_syn_stream()`, then\n * this function returns ``NULL``. If the stream does not exist, this\n * function returns ``NULL``.\n */\nvoid* spdylay_session_get_stream_user_data(spdylay_session *session,\n                                           int32_t stream_id);\n\n/**\n * @function\n *\n * Sets the |stream_user_data| to the stream denoted by the\n * |stream_id|.  If a stream user data is already set to the stream,\n * it is replaced with the |stream_user_data|.  It is valid to specify\n * ``NULL`` in the |stream_user_data|, which nullifies the associated\n * data pointer.\n *\n * This function returns 0 if it succeeds, or one of following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The stream does not exist\n */\nint spdylay_session_set_stream_user_data(spdylay_session *session,\n                                         int32_t stream_id,\n                                         void *stream_user_data);\n\n/**\n * @function\n *\n * Returns the number of frames in the outbound queue. This does not\n * include the deferred DATA frames.\n */\nsize_t spdylay_session_get_outbound_queue_size(spdylay_session *session);\n\n/**\n * @function\n *\n * Returns lowest priority value for the |session|.\n */\nuint8_t spdylay_session_get_pri_lowest(spdylay_session *session);\n\n/**\n * @function\n *\n * Returns the number of DATA payload in bytes received without\n * WINDOW_UPDATE transmission for the stream |stream_id|.\n *\n * If the flow control is disabled by the protocol, this function\n * returns 0.\n *\n * This function returns -1 if it fails.\n */\nint32_t spdylay_session_get_stream_recv_data_length(spdylay_session *session,\n                                                    int32_t stream_id);\n\n/**\n * @function\n *\n * Returns the number of DATA payload in bytes received without\n * WINDOW_UPDATE transmission for a connection.\n *\n * If flow control is disabled by the protocol, this function returns\n * 0.\n *\n * This function returns -1 if it fails.\n */\nint32_t spdylay_session_get_recv_data_length(spdylay_session *session);\n\n/**\n * @function\n *\n * Submits GOAWAY frame.  The status code |status_code| is ignored if\n * the protocol version is :macro:`SPDYLAY_PROTO_SPDY2`.\n *\n * This function should be called when the connection should be\n * terminated after sending GOAWAY. If the remaining streams should be\n * processed after GOAWAY, use `spdylay_submit_goaway()` instead.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_session_fail_session(spdylay_session *session,\n                                 uint32_t status_code);\n\n/**\n * @function\n *\n * Tells the |session| that |size| bytes for a stream denoted by\n * |stream_id| were consumed by application and are ready to\n * WINDOW_UPDATE.  This function is intended to be used without\n * automatic window update (see\n * :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2`).\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |stream_id| is 0.\n * :enum:`SPDYLAY_ERR_INVALID_STATE`\n *     :enum:`SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2` is not set.\n */\nint spdylay_session_consume(spdylay_session *session, int32_t stream_id,\n                            size_t size);\n\n/**\n * @function\n *\n * Returns string describing the |error_code|. The |error_code| must\n * be one of the :enum:`spdylay_error`.\n */\nconst char* spdylay_strerror(int error_code);\n\n/**\n * @function\n *\n * Submits SYN_STREAM frame and optionally one or more DATA\n * frames.\n *\n * The |pri| is priority of this request. 0 is the highest priority\n * value. Use `spdylay_session_get_pri_lowest()` to know the lowest\n * priority value for this |session|. If the |pri| is larger than the\n * lowest value, the lowest value is used silently.\n *\n * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]``\n * contains a pointer to the name string and ``nv[2*i+1]`` contains a\n * pointer to the value string. The one beyond last value must be\n * ``NULL``. That is, if the |nv| contains N name/value pairs,\n * ``nv[2*N]`` must be ``NULL``.\n *\n * The |nv| must include following name/value pairs:\n *\n * ``:method``\n *     HTTP method (e.g., ``GET``, ``POST``, ``HEAD``, etc)\n * ``:scheme``\n *     URI scheme (e.g., ``https``)\n * ``:path``\n *     Absolute path and parameters of this request (e.g., ``/foo``,\n *     ``/foo;bar;haz?h=j&y=123``)\n * ``:version``\n *     HTTP version (e.g., ``HTTP/1.1``)\n * ``:host``\n *     The hostport portion of the URI for this request (e.g.,\n *     ``example.org:443``). This is the same as the HTTP \"Host\" header\n *     field.\n *\n * If the |session| is initialized with the version\n * :macro:`SPDYLAY_PROTO_SPDY2`, the above names are translated to\n * ``method``, ``scheme``, ``url``, ``version`` and ``host``\n * respectively.\n *\n * This function creates copies of all name/value pairs in |nv|.  It\n * also lower-cases all names in |nv|.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames. In this case, a method that allows\n * request message bodies\n * (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must\n * be specified with ``:method`` key in |nv| (e.g. ``POST``). This\n * function does not take ownership of the |data_prd|. The function\n * copies the members of the |data_prd|. If |data_prd| is ``NULL``,\n * SYN_STREAM have FLAG_FIN set. The |stream_user_data| is data\n * associated to the stream opened by this request and can be an\n * arbitrary pointer, which can be retrieved later by\n * `spdylay_session_get_stream_user_data()`.\n *\n * Since the library reorders the frames and tries to send the highest\n * prioritized one first and the SPDY specification requires the\n * stream ID must be strictly increasing, the stream ID of this\n * request cannot be known until it is about to sent.  To know the\n * stream ID of the request, the application can use\n * :member:`spdylay_session_callbacks.before_ctrl_send_callback`. This\n * callback is called just before the frame is sent. For SYN_STREAM\n * frame, the argument frame has the stream ID assigned. Also since\n * the stream is already opened,\n * `spdylay_session_get_stream_user_data()` can be used to get\n * |stream_user_data| to identify which SYN_STREAM we are processing.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |nv| includes empty name or NULL value.\n * :enum:`SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE`\n *     Stream ID has reached the maximum value. Therefore no stream ID\n *     is available.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_request(spdylay_session *session, uint8_t pri,\n                           const char **nv,\n                           const spdylay_data_provider *data_prd,\n                           void *stream_user_data);\n\n/**\n * @function\n *\n * Submits SYN_REPLY frame and optionally one or more DATA frames\n * against the stream |stream_id|.\n *\n * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]``\n * contains a pointer to the name string and ``nv[2*i+1]`` contains a\n * pointer to the value string. The one beyond last value must be\n * ``NULL``. That is, if the |nv| contains N name/value pairs,\n * ``nv[2*N]`` must be ``NULL``.\n *\n * The |nv| must include following name/value pairs:\n *\n * ``:status``\n *     HTTP status code (e.g., ``200`` or ``200 OK``)\n * ``:version``\n *     HTTP response version (e.g., ``HTTP/1.1``)\n *\n * If the |session| is initialized with the version\n * :macro:`SPDYLAY_PROTO_SPDY2`, the above names are translated to\n * ``status`` and ``version`` respectively.\n *\n * This function creates copies of all name/value pairs in |nv|.  It\n * also lower-cases all names in |nv|.\n *\n * If |data_prd| is not ``NULL``, it provides data which will be sent\n * in subsequent DATA frames.  This function does not take ownership\n * of the |data_prd|. The function copies the members of the\n * |data_prd|.  If |data_prd| is ``NULL``, SYN_REPLY will have\n * FLAG_FIN set.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |nv| includes empty name or NULL value.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_response(spdylay_session *session,\n                            int32_t stream_id, const char **nv,\n                            const spdylay_data_provider *data_prd);\n\n/**\n * @function\n *\n * Submits SYN_STREAM frame. The |flags| is bitwise OR of the\n * following values:\n *\n * * :enum:`SPDYLAY_CTRL_FLAG_FIN`\n * * :enum:`SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL`\n *\n * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has\n * FLAG_FIN flag set.\n *\n * The |assoc_stream_id| is used for server-push. Specify 0 if this\n * stream is not server-push. If |session| is initialized for client\n * use, |assoc_stream_id| is ignored.\n *\n * The |pri| is priority of this request. 0 is the highest priority\n * value. Use `spdylay_session_get_pri_lowest()` to know the lowest\n * priority value for this |session|. If the |pri| is larger than the\n * lowest value, the lowest value is used silently.\n *\n * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]``\n * contains a pointer to the name string and ``nv[2*i+1]`` contains a\n * pointer to the value string. The one beyond last value must be\n * ``NULL``. That is, if the |nv| contains N name/value pairs,\n * ``nv[2*N]`` must be ``NULL``.\n *\n * This function creates copies of all name/value pairs in |nv|.  It\n * also lower-cases all names in |nv|.\n *\n * The |stream_user_data| is a pointer to an arbitrary\n * data which is associated to the stream this frame will open.\n *\n * This function is low-level in a sense that the application code can\n * specify flags and the Associated-To-Stream-ID directly. For usual\n * HTTP request, `spdylay_submit_request()` is useful.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |assoc_stream_id| is invalid; or\n *     the |nv| includes empty name or NULL value.\n * :enum:`SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE`\n *     Stream ID has reached the maximum value. Therefore no stream ID\n *     is available.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags,\n                              int32_t assoc_stream_id, uint8_t pri,\n                              const char **nv, void *stream_user_data);\n\n/**\n * @function\n *\n * Submits SYN_REPLY frame. The |flags| is bitwise OR of the following\n * values:\n *\n * * :enum:`SPDYLAY_CTRL_FLAG_FIN`\n *\n * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has\n * FLAG_FIN flag set.\n *\n * The stream which this frame belongs to is given in the\n * |stream_id|. The |nv| is the name/value pairs in this frame.\n *\n * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]``\n * contains a pointer to the name string and ``nv[2*i+1]`` contains a\n * pointer to the value string. The one beyond last value must be\n * ``NULL``. That is, if the |nv| contains N name/value pairs,\n * ``nv[2*N]`` must be ``NULL``.\n *\n * This function creates copies of all name/value pairs in |nv|.  It\n * also lower-cases all names in |nv|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |nv| includes empty name or NULL value.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags,\n                             int32_t stream_id, const char **nv);\n\n/**\n * @function\n *\n * Submits HEADERS frame. The |flags| is bitwise OR of the following\n * values:\n *\n * * :enum:`SPDYLAY_CTRL_FLAG_FIN`\n *\n * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has\n * FLAG_FIN flag set.\n *\n * The stream which this frame belongs to is given in the\n * |stream_id|. The |nv| is the name/value pairs in this frame.\n *\n * The |nv| contains the name/value pairs. For i >= 0, ``nv[2*i]``\n * contains a pointer to the name string and ``nv[2*i+1]`` contains a\n * pointer to the value string. The one beyond last value must be\n * ``NULL``. That is, if the |nv| contains N name/value pairs,\n * ``nv[2*N]`` must be ``NULL``.\n *\n * This function creates copies of all name/value pairs in |nv|.  It\n * also lower-cases all names in |nv|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |nv| includes empty name or NULL value.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_headers(spdylay_session *session, uint8_t flags,\n                           int32_t stream_id, const char **nv);\n\n/**\n * @function\n *\n * Submits one or more DATA frames to the stream |stream_id|.  The\n * data to be sent are provided by |data_prd|. If |flags| contains\n * :enum:`SPDYLAY_DATA_FLAG_FIN`, the last DATA frame has FLAG_FIN\n * set.\n *\n * This function does not take ownership of the |data_prd|. The\n * function copies the members of the |data_prd|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_data(spdylay_session *session, int32_t stream_id,\n                        uint8_t flags, const spdylay_data_provider *data_prd);\n\n/**\n * @function\n *\n * Submits RST_STREAM frame to cancel/reject the stream |stream_id|\n * with the status code |status_code|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id,\n                              uint32_t status_code);\n\n/**\n * @function\n *\n * Submits PING frame. You don't have to send PING back when you\n * received PING frame. The library automatically submits PING frame\n * in this case.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_ping(spdylay_session *session);\n\n/**\n * @function\n *\n * Submits GOAWAY frame. The status code |status_code| is ignored if\n * the protocol version is :macro:`SPDYLAY_PROTO_SPDY2`.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_goaway(spdylay_session *session, uint32_t status_code);\n\n/**\n * @function\n *\n * Stores local settings and submits SETTINGS frame. The |iv| is the\n * pointer to the array of :type:`spdylay_settings_entry`. The |niv|\n * indicates the number of :type:`spdylay_settings_entry`. The |flags|\n * is bitwise-OR of one or more values from\n * :type:`spdylay_settings_flag`.\n *\n * This function does not take ownership of the |iv|. This function\n * copies all the elements in the |iv|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |iv| contains duplicate settings ID or invalid value.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_settings(spdylay_session *session, uint8_t flags,\n                            const spdylay_settings_entry *iv, size_t niv);\n\n/**\n * @function\n *\n * Submits WINDOW_UPDATE frame. The effective range of the\n * |delta_window_size| is [1, (1 << 31)-1], inclusive. But the\n * application must be responsible to keep the resulting window size\n * <= (1 << 31)-1.\n *\n * To send connection-level WINDOW_UPDATE, specify 0 to the\n * |stream_id| if the negotiated protocol supports it.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT`\n *     The |delta_window_size| is 0 or negative.\n * :enum:`SPDYLAY_ERR_STREAM_CLOSED`\n *     The stream is already closed or does not exist.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_submit_window_update(spdylay_session *session, int32_t stream_id,\n                                 int32_t delta_window_size);\n\n/**\n * @function\n *\n * A helper function for dealing with NPN in client side.  The |in|\n * contains server's protocol in preferable order.  The format of |in|\n * is length-prefixed and not null-terminated.  For example,\n * ``spdy/2`` and ``http/1.1`` stored in |in| like this::\n *\n *     in[0] = 6\n *     in[1..6] = \"spdy/2\"\n *     in[7] = 8\n *     in[8..15] = \"http/1.1\"\n *     inlen = 16\n *\n * The selection algorithm is as follows:\n *\n * 1. If server's list contains SPDY versions the spdylay library\n *    supports, this function selects one of them and returns its SPDY\n *    protocol version which can be used directly with\n *    `spdylay_session_client_new()` and\n *    `spdylay_session_server_new()` . The following steps are not\n *    taken.\n *\n * 2. If server's list contains ``http/1.1``, this function selects\n *    ``http/1.1`` and returns 0. The following step is not taken.\n *\n * 3. This function selects nothing and returns -1. (So called\n *    non-overlap case). In this case, |out| and |outlen| are left\n *    untouched.\n *\n * When spdylay supports updated version of SPDY in the future, this\n * function may select updated protocol and application code which\n * relies on spdylay for SPDY stuff needs not be modified.\n *\n * Selecting ``spdy/2`` means that ``spdy/2`` is written into |*out|\n * and length of ``spdy/2`` (which is 6) is assigned to |*outlen|.\n *\n * See http://technotes.googlecode.com/git/nextprotoneg.html for more\n * details about NPN.\n *\n * To use this method you should do something like::\n *\n *     static int select_next_proto_cb(SSL* ssl,\n *                                     unsigned char **out,\n *                                     unsigned char *outlen,\n *                                     const unsigned char *in,\n *                                     unsigned int inlen,\n *                                     void *arg)\n *     {\n *         int version;\n *         version = spdylay_select_next_protocol(out, outlen, in, inlen);\n *         if(version == -1) {\n *             return SSL_TLSEXT_ERR_NOACK;\n *         }\n *         if(version > 0) {\n *             ((MyType*)arg)->spdy_version = version;\n *         }\n *         return SSL_TLSEXT_ERR_OK;\n *     }\n *     ...\n *     SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj);\n */\nint spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen,\n                                 const unsigned char *in, unsigned int inlen);\n\n/**\n * @struct\n *\n * This struct contains SPDY version information this library\n * supports.\n */\ntypedef struct {\n  /**\n   * SPDY protocol version name which can be used as TLS NPN protocol\n   * string.\n   */\n  const unsigned char *proto;\n  /**\n   * The length of proto member.\n   */\n  uint8_t len;\n  /**\n   * The corresponding SPDY version constant which can be passed to\n   * `spdylay_session_client_new()` and `spdylay_session_server_new()`\n   * as version argument.\n   */\n  uint16_t version;\n} spdylay_npn_proto;\n\n/**\n * @function\n *\n * Returns a pointer to the supported SPDY version list. The number of\n * elements in the list will be assigned to the |*len_ptr|. It\n * contains all SPDY version information this library supports. The\n * application can use this information to configure NPN protocol\n * offerings/selection.\n */\nconst spdylay_npn_proto* spdylay_npn_get_proto_list(size_t *len_ptr);\n\n/**\n * @function\n *\n * Returns spdy version which spdylay library supports from the given\n * protocol name. The |proto| is the pointer to the protocol name and\n * |protolen| is its length. Currently, ``spdy/2``, ``spdy/3`` and\n * ``spdy/3.1`` are supported.\n *\n * This function returns nonzero spdy version if it succeeds, or 0.\n */\nuint16_t spdylay_npn_get_version(const unsigned char *proto, size_t protolen);\n\nstruct spdylay_gzip;\n\n/**\n * @struct\n *\n * The gzip stream to inflate data. The details of this structure are\n * intentionally hidden from the public API.\n */\ntypedef struct spdylay_gzip spdylay_gzip;\n\n/**\n * @function\n *\n * A helper function to set up a per request gzip stream to inflate data.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_GZIP`\n *     The initialization of gzip stream failed.\n * :enum:`SPDYLAY_ERR_NOMEM`\n *     Out of memory.\n */\nint spdylay_gzip_inflate_new(spdylay_gzip **inflater_ptr);\n\n/**\n * @function\n *\n * Frees the inflate stream.  The |inflater| may be ``NULL``.\n */\nvoid spdylay_gzip_inflate_del(spdylay_gzip *inflater);\n\n/**\n * @function\n *\n * Inflates data in |in| with the length |*inlen_ptr| and stores the\n * inflated data to |out| which has allocated size at least\n * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent\n * the number of data written in |out|.  Similarly, |*inlen_ptr| is\n * updated to represent the number of input bytes processed.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * :enum:`SPDYLAY_ERR_GZIP`\n *     The inflation of gzip stream failed.\n *\n * The example follows::\n *\n *     void on_data_chunk_recv_callback(spdylay_session *session,\n *                                      uint8_t flags,\n *                                      int32_t stream_id,\n *                                      const uint8_t *data, size_t len,\n *                                      void *user_data)\n *     {\n *         ...\n *         req = spdylay_session_get_stream_user_data(session, stream_id);\n *         spdylay_gzip *inflater = req->inflater;\n *         while(len > 0) {\n *             uint8_t out[MAX_OUTLEN];\n *             size_t outlen = MAX_OUTLEN;\n *             size_t tlen = len;\n *             int rv;\n *             rv = spdylay_gzip_inflate(inflater, out, &outlen, data, &tlen);\n *             if(rv != 0) {\n *                 spdylay_submit_rst_stream(session, stream_id,\n *                                           SPDYLAY_INTERNAL_ERROR);\n *                 break;\n *             }\n *             ... Do stuff ...\n *             data += tlen;\n *             len -= tlen;\n *         }\n *         ....\n *     }\n */\nint spdylay_gzip_inflate(spdylay_gzip *inflater,\n                         uint8_t *out, size_t *outlen_ptr,\n                         const uint8_t *in, size_t *inlen_ptr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* SPDYLAY_H */\n"
  },
  {
    "path": "lib/includes/spdylay/spdylayver.h.in",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAYVER_H\n#define SPDYLAYVER_H\n\n/**\n * @macro\n * Version number of the Spdylay library release\n */\n#define SPDYLAY_VERSION \"@PACKAGE_VERSION@\"\n\n#endif /* SPDYLAYVER_H */\n"
  },
  {
    "path": "lib/libspdylay.pc.in",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nprefix=@prefix@\nexec_prefix=@exec_prefix@\nlibdir=@libdir@\nincludedir=@includedir@\n\nName: Spdylay\nDescription: SPDY C library\nURL: http://spdylay.sourceforge.net/\nVersion: @VERSION@\nLibs: -L${libdir} -lspdylay\nRequires.private: zlib\nCflags: -I${includedir}\n"
  },
  {
    "path": "lib/spdylay_buffer.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_buffer.h\"\n\n#include <assert.h>\n#include <string.h>\n\n#include \"spdylay_net.h\"\n#include \"spdylay_helper.h\"\n\nvoid spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity)\n{\n  buffer->root.data = NULL;\n  buffer->root.next = NULL;\n  buffer->current = &buffer->root;\n  buffer->capacity = chunk_capacity;\n  buffer->len = 0;\n  /*\n   * Set last_offset to maximum so that first append adds new buffer\n   * buffer.\n   */\n  buffer->last_offset = buffer->capacity;\n}\n\nvoid spdylay_buffer_free(spdylay_buffer *buffer)\n{\n  spdylay_buffer_chunk *p = buffer->root.next;\n  while(p) {\n    spdylay_buffer_chunk *next = p->next;\n    free(p->data);\n    free(p);\n    p = next;\n  }\n}\n\nint spdylay_buffer_alloc(spdylay_buffer *buffer)\n{\n  if(buffer->current->next == NULL) {\n    spdylay_buffer_chunk *chunk;\n    uint8_t *buf;\n    chunk = malloc(sizeof(spdylay_buffer_chunk));\n    if(chunk == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    }\n    buf = malloc(buffer->capacity);\n    if(buf == NULL) {\n      free(chunk);\n      return SPDYLAY_ERR_NOMEM;\n    }\n    chunk->data = buf;\n    chunk->next = NULL;\n    buffer->current->next = chunk;\n    buffer->current = chunk;\n  } else {\n    buffer->current = buffer->current->next;\n  }\n  buffer->len += buffer->capacity-buffer->last_offset;\n  buffer->last_offset = 0;\n  return 0;\n}\n\nuint8_t* spdylay_buffer_get(spdylay_buffer *buffer)\n{\n  if(buffer->current->data == NULL) {\n    return NULL;\n  } else {\n    return buffer->current->data+buffer->last_offset;\n  }\n}\n\nsize_t spdylay_buffer_avail(spdylay_buffer *buffer)\n{\n  return buffer->capacity-buffer->last_offset;\n}\n\nvoid spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount)\n{\n  buffer->last_offset += amount;\n  buffer->len += amount;\n  assert(buffer->last_offset <= buffer->capacity);\n}\n\nint spdylay_buffer_write(spdylay_buffer *buffer, const uint8_t *data,\n                          size_t len)\n{\n  int rv;\n  while(len) {\n    size_t writelen;\n    if(spdylay_buffer_avail(buffer) == 0) {\n      if((rv = spdylay_buffer_alloc(buffer)) != 0) {\n        return rv;\n      }\n    }\n    writelen = spdylay_min(spdylay_buffer_avail(buffer), len);\n    memcpy(spdylay_buffer_get(buffer), data, writelen);\n    data += writelen;\n    len -= writelen;\n    spdylay_buffer_advance(buffer, writelen);\n  }\n  return 0;\n}\n\nsize_t spdylay_buffer_length(spdylay_buffer *buffer)\n{\n  return buffer->len;\n}\n\nsize_t spdylay_buffer_capacity(spdylay_buffer *buffer)\n{\n  return buffer->capacity;\n}\n\nvoid spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf)\n{\n  spdylay_buffer_chunk *p = buffer->root.next;\n  for(; p; p = p->next) {\n    size_t len;\n    if(p == buffer->current) {\n      len = buffer->last_offset;\n    } else {\n      len = buffer->capacity;\n    }\n    memcpy(buf, p->data, len);\n    buf += len;\n  }\n}\n\nvoid spdylay_buffer_reset(spdylay_buffer *buffer)\n{\n  buffer->current = &buffer->root;\n  buffer->len = 0;\n  buffer->last_offset = buffer->capacity;\n}\n\nvoid spdylay_buffer_reader_init(spdylay_buffer_reader *reader,\n                                spdylay_buffer *buffer)\n{\n  reader->buffer = buffer;\n  reader->current = buffer->root.next;\n  reader->offset = 0;\n}\n\nuint8_t spdylay_buffer_reader_uint8(spdylay_buffer_reader *reader)\n{\n  uint8_t out;\n  spdylay_buffer_reader_data(reader, &out, sizeof(uint8_t));\n  return out;\n}\n\nuint16_t spdylay_buffer_reader_uint16(spdylay_buffer_reader *reader)\n{\n  uint16_t out;\n  spdylay_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint16_t));\n  return ntohs(out);\n}\n\nuint32_t spdylay_buffer_reader_uint32(spdylay_buffer_reader *reader)\n{\n  uint32_t out;\n  spdylay_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint32_t));\n  return ntohl(out);\n}\n\nvoid spdylay_buffer_reader_data(spdylay_buffer_reader *reader,\n                                uint8_t *out, size_t len)\n{\n  while(len) {\n    size_t remlen, readlen;\n    remlen = reader->buffer->capacity - reader->offset;\n    readlen = spdylay_min(remlen, len);\n    memcpy(out, reader->current->data + reader->offset, readlen);\n    out += readlen;\n    len -= readlen;\n    reader->offset += readlen;\n    if(reader->buffer->capacity == reader->offset) {\n      reader->current = reader->current->next;\n      reader->offset = 0;\n    }\n  }\n}\n\nint spdylay_buffer_reader_count(spdylay_buffer_reader *reader,\n                                size_t len, uint8_t c)\n{\n  int res = 0;\n  while(len) {\n    size_t remlen, readlen, i;\n    uint8_t *p;\n    remlen = reader->buffer->capacity - reader->offset;\n    readlen = spdylay_min(remlen, len);\n    p = reader->current->data + reader->offset;\n    for(i = 0; i < readlen; ++i) {\n      if(p[i] == c) {\n        ++res;\n      }\n    }\n    len -= readlen;\n    reader->offset += readlen;\n    if(reader->buffer->capacity == reader->offset) {\n      reader->current = reader->current->next;\n      reader->offset = 0;\n    }\n  }\n  return res;\n}\n\nvoid spdylay_buffer_reader_advance(spdylay_buffer_reader *reader,\n                                   size_t amount)\n{\n  while(amount) {\n    size_t remlen, skiplen;\n    remlen = reader->buffer->capacity - reader->offset;\n    skiplen = spdylay_min(remlen, amount);\n    amount -= skiplen;\n    reader->offset += skiplen;\n    if(reader->buffer->capacity == reader->offset) {\n      reader->current = reader->current->next;\n      reader->offset = 0;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/spdylay_buffer.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_BUFFER_H\n#define SPDYLAY_BUFFER_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n\ntypedef struct spdylay_buffer_chunk {\n  uint8_t *data;\n  struct spdylay_buffer_chunk *next;\n} spdylay_buffer_chunk;\n\n/*\n * List of fixed sized chunks\n */\ntypedef struct {\n  /* Capacity of each chunk buffer */\n  size_t capacity;\n  /* Root of list of chunk buffers. The root is dummy and its data\n     member is always NULL. */\n  spdylay_buffer_chunk root;\n  /* Points to the current chunk to write */\n  spdylay_buffer_chunk *current;\n  /* Total length of this buffer */\n  size_t len;\n  /* Offset of last chunk buffer */\n  size_t last_offset;\n} spdylay_buffer;\n\n/*\n * Initializes buffer with fixed chunk size chunk_capacity.\n */\nvoid spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity);\n/* Releases allocated memory for buffer */\nvoid spdylay_buffer_free(spdylay_buffer *buffer);\n/* Returns buffer pointer */\nuint8_t* spdylay_buffer_get(spdylay_buffer *buffer);\n/* Returns available buffer length */\nsize_t spdylay_buffer_avail(spdylay_buffer *buffer);\n/* Advances buffer pointer by amount. This reduces available buffer\n   length. */\nvoid spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount);\n\n/*\n * Writes the |data| with the |len| bytes starting at the current\n * position of the |buffer|. The new chunk buffer will be allocated on\n * the course of the write and the current position is updated.  If\n * this function succeeds, the total length of the |buffer| will be\n * increased by |len|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_buffer_write(spdylay_buffer *buffer, const uint8_t *data,\n                         size_t len);\n\n/*\n * Allocate new chunk buffer. This will increase total length of\n * buffer (returned by spdylay_buffer_length) by capacity-last_offset.\n * It means untouched buffer is assumued to be written.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative eror codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_buffer_alloc(spdylay_buffer *buffer);\n\n/* Returns total length of buffer */\nsize_t spdylay_buffer_length(spdylay_buffer *buffer);\n\n/* Returns capacity of each fixed chunk buffer */\nsize_t spdylay_buffer_capacity(spdylay_buffer *buffer);\n\n/* Stores the contents of buffer into |buf|. |buf| must be at least\n   spdylay_buffer_length(buffer) bytes long. */\nvoid spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf);\n\n/* Reset |buffer| for reuse.  Set the total length of buffer to 0.\n   Next spdylay_buffer_avail() returns 0. This function does not free\n   allocated memory space; they are reused. */\nvoid spdylay_buffer_reset(spdylay_buffer *buffer);\n\n/*\n * Reader interface to read data from spdylay_buffer sequentially.\n */\ntypedef struct {\n  /* The buffer to read */\n  spdylay_buffer *buffer;\n  /* Pointer to the current chunk to read. */\n  spdylay_buffer_chunk *current;\n  /* Offset to the current chunk data to read. */\n  size_t offset;\n} spdylay_buffer_reader;\n\n/*\n * Initializes the |reader| with the |buffer|.\n */\nvoid spdylay_buffer_reader_init(spdylay_buffer_reader *reader,\n                                spdylay_buffer *buffer);\n\n/*\n * Reads 1 byte and return it. This function will advance the current\n * position by 1.\n */\nuint8_t spdylay_buffer_reader_uint8(spdylay_buffer_reader *reader);\n\n/*\n * Reads 2 bytes integer in network byte order and returns it in host\n * byte order. This function will advance the current position by 2.\n */\nuint16_t spdylay_buffer_reader_uint16(spdylay_buffer_reader *reader);\n\n/*\n * Reads 4 bytes integer in network byte order and returns it in host\n * byte order. This function will advance the current position by 4.\n */\nuint32_t spdylay_buffer_reader_uint32(spdylay_buffer_reader *reader);\n\n/*\n * Reads |len| bytes and store them in the |out|. This function will\n * advance the current position by |len|.\n */\nvoid spdylay_buffer_reader_data(spdylay_buffer_reader *reader,\n                                uint8_t *out, size_t len);\n\n/**\n * Reads |len| bytes and count the occurrence of |c| there and return\n * it. This function will advance the current position by |len|.\n */\nint spdylay_buffer_reader_count(spdylay_buffer_reader *reader,\n                                size_t len, uint8_t c);\n\n/*\n * Advances the current position by |amount|.\n */\nvoid spdylay_buffer_reader_advance(spdylay_buffer_reader *reader,\n                                   size_t amount);\n\n#endif /* SPDYLAY_BUFFER_H */\n"
  },
  {
    "path": "lib/spdylay_frame.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_frame.h\"\n\n#include <string.h>\n#include <assert.h>\n#include <stdio.h>\n#include <errno.h>\n\n#include \"spdylay_helper.h\"\n#include \"spdylay_net.h\"\n\nsize_t spdylay_frame_get_len_size(uint16_t version)\n{\n  if(SPDYLAY_PROTO_SPDY2 == version) {\n    return 2;\n  } else if(SPDYLAY_PROTO_SPDY3 == version) {\n    return 4;\n  } else {\n    /* Unsupported version */\n    return 0;\n  }\n}\n\nstatic uint8_t* spdylay_pack_str(uint8_t *buf, const char *str, size_t len,\n                                 size_t len_size)\n{\n  spdylay_frame_put_nv_len(buf, (uint32_t)len, len_size);\n  buf += len_size;\n  memcpy(buf, str, len);\n  return buf+len;\n}\n\nstatic void spdylay_frame_pack_ctrl_hd(uint8_t* buf, const spdylay_ctrl_hd *hd)\n{\n  spdylay_put_uint16be(&buf[0], hd->version);\n  buf[0] |= 1 << 7;\n  spdylay_put_uint16be(&buf[2], hd->type);\n  spdylay_put_uint32be(&buf[4], hd->length);\n  buf[4] = hd->flags;\n}\n\nstatic void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd,\n                                         const uint8_t* buf)\n{\n  hd->version = spdylay_get_uint16(buf) & SPDYLAY_VERSION_MASK;\n  hd->type = spdylay_get_uint16(&buf[2]);\n  hd->flags = buf[4];\n  hd->length = spdylay_get_uint32(&buf[4]) & SPDYLAY_LENGTH_MASK;\n}\n\nssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr,\n                                    size_t *buflen_ptr,\n                                    uint8_t **nvbuf_ptr,\n                                    size_t *nvbuflen_ptr,\n                                    char **nv, size_t nv_offset,\n                                    size_t len_size,\n                                    spdylay_zlib *deflater)\n{\n  size_t nvspace;\n  size_t maxframelen;\n  ssize_t framelen;\n  int r;\n  nvspace = spdylay_frame_count_nv_space(nv, len_size);\n  r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace);\n  if(r != 0) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  maxframelen = nv_offset+spdylay_zlib_deflate_hd_bound(deflater, nvspace);\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, maxframelen);\n  if(r != 0) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_pack_nv(*nvbuf_ptr, nv, len_size);\n  framelen = spdylay_zlib_deflate_hd(deflater,\n                                     (*buf_ptr)+nv_offset,\n                                     maxframelen-nv_offset,\n                                     *nvbuf_ptr, nvspace);\n  if(framelen < 0) {\n    return framelen;\n  }\n  framelen += nv_offset;\n\n  if(framelen - SPDYLAY_FRAME_HEAD_LENGTH > SPDYLAY_LENGTH_MASK) {\n    /* In SPDY/2 and 3, Max frame size is 2**24 - 1. */\n    return SPDYLAY_ERR_FRAME_TOO_LARGE;\n  }\n  return framelen;\n}\n\nint spdylay_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr,\n                                        spdylay_buffer *in, size_t len_size)\n{\n  uint32_t n;\n  size_t buflen = 0;\n  size_t nvlen = 0;\n  size_t off = 0;\n  size_t inlen = spdylay_buffer_length(in);\n  size_t i;\n  spdylay_buffer_reader reader;\n  if(inlen < len_size) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  spdylay_buffer_reader_init(&reader, in);\n\n  /* TODO limit n in a reasonable number */\n  n = spdylay_frame_get_nv_len(&reader, len_size);\n  off += len_size;\n  for(i = 0; i < n; ++i) {\n    uint32_t len;\n    size_t j;\n    for(j = 0; j < 2; ++j) {\n      if(inlen-off < len_size) {\n        return SPDYLAY_ERR_INVALID_FRAME;\n      }\n      len = spdylay_frame_get_nv_len(&reader, len_size);\n      off += len_size;\n      if(inlen-off < len) {\n        return SPDYLAY_ERR_INVALID_FRAME;\n      }\n      buflen += len+1;\n      off += len;\n      if(j == 0) {\n        spdylay_buffer_reader_advance(&reader, len);\n      }\n    }\n    nvlen += spdylay_buffer_reader_count(&reader, len, '\\0');\n    ++nvlen;\n  }\n  if(inlen == off) {\n    *nvlen_ptr = nvlen;\n    *buflen_ptr = buflen+(nvlen*2+1)*sizeof(char*);\n    return 0;\n  } else {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n}\n\nstatic int VALID_HD_VALUE_CHARS[] = {\n  1 /* NUL  */, 0 /* SOH  */, 0 /* STX  */, 0 /* ETX  */,\n  0 /* EOT  */, 0 /* ENQ  */, 0 /* ACK  */, 0 /* BEL  */,\n  0 /* BS   */, 1 /* HT   */, 0 /* LF   */, 0 /* VT   */,\n  0 /* FF   */, 0 /* CR   */, 0 /* SO   */, 0 /* SI   */,\n  0 /* DLE  */, 0 /* DC1  */, 0 /* DC2  */, 0 /* DC3  */,\n  0 /* DC4  */, 0 /* NAK  */, 0 /* SYN  */, 0 /* ETB  */,\n  0 /* CAN  */, 0 /* EM   */, 0 /* SUB  */, 0 /* ESC  */,\n  0 /* FS   */, 0 /* GS   */, 0 /* RS   */, 0 /* US   */,\n  1 /* SPC  */, 1 /* !    */, 1 /* \"    */, 1 /* #    */,\n  1 /* $    */, 1 /* %    */, 1 /* &    */, 1 /* '    */,\n  1 /* (    */, 1 /* )    */, 1 /* *    */, 1 /* +    */,\n  1 /* ,    */, 1 /* -    */, 1 /* .    */, 1 /* /    */,\n  1 /* 0    */, 1 /* 1    */, 1 /* 2    */, 1 /* 3    */,\n  1 /* 4    */, 1 /* 5    */, 1 /* 6    */, 1 /* 7    */,\n  1 /* 8    */, 1 /* 9    */, 1 /* :    */, 1 /* ;    */,\n  1 /* <    */, 1 /* =    */, 1 /* >    */, 1 /* ?    */,\n  1 /* @    */, 1 /* A    */, 1 /* B    */, 1 /* C    */,\n  1 /* D    */, 1 /* E    */, 1 /* F    */, 1 /* G    */,\n  1 /* H    */, 1 /* I    */, 1 /* J    */, 1 /* K    */,\n  1 /* L    */, 1 /* M    */, 1 /* N    */, 1 /* O    */,\n  1 /* P    */, 1 /* Q    */, 1 /* R    */, 1 /* S    */,\n  1 /* T    */, 1 /* U    */, 1 /* V    */, 1 /* W    */,\n  1 /* X    */, 1 /* Y    */, 1 /* Z    */, 1 /* [    */,\n  1 /* \\    */, 1 /* ]    */, 1 /* ^    */, 1 /* _    */,\n  1 /* `    */, 1 /* a    */, 1 /* b    */, 1 /* c    */,\n  1 /* d    */, 1 /* e    */, 1 /* f    */, 1 /* g    */,\n  1 /* h    */, 1 /* i    */, 1 /* j    */, 1 /* k    */,\n  1 /* l    */, 1 /* m    */, 1 /* n    */, 1 /* o    */,\n  1 /* p    */, 1 /* q    */, 1 /* r    */, 1 /* s    */,\n  1 /* t    */, 1 /* u    */, 1 /* v    */, 1 /* w    */,\n  1 /* x    */, 1 /* y    */, 1 /* z    */, 1 /* {    */,\n  1 /* |    */, 1 /* }    */, 1 /* ~    */, 0 /* DEL  */,\n  1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */,\n  1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */,\n  1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */,\n  1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */,\n  1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */,\n  1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */,\n  1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */,\n  1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */,\n  1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */,\n  1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */,\n  1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */,\n  1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */,\n  1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */,\n  1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */,\n  1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */,\n  1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */,\n  1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */,\n  1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */,\n  1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */,\n  1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */,\n  1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */,\n  1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */,\n  1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */,\n  1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */,\n  1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */,\n  1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */,\n  1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */,\n  1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */,\n  1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */,\n  1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */,\n  1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */,\n  1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */\n};\n\nint spdylay_frame_unpack_nv(char ***nv_ptr, spdylay_buffer *in,\n                            size_t len_size)\n{\n  size_t nvlen, buflen;\n  int r;\n  size_t i;\n  char *buf, **idx, *data;\n  uint32_t n;\n  int invalid_header_block = 0;\n  spdylay_buffer_reader reader;\n  r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, len_size);\n  if(r != 0) {\n    return r;\n  }\n\n  buf = malloc(buflen);\n  if(buf == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_buffer_reader_init(&reader, in);\n  idx = (char**)(void *)buf;\n  data = buf+(nvlen*2+1)*sizeof(char*);\n  n = spdylay_frame_get_nv_len(&reader, len_size);\n  for(i = 0; i < n; ++i) {\n    uint32_t len;\n    char *name, *val;\n    char *stop;\n    int multival;\n    len = spdylay_frame_get_nv_len(&reader, len_size);\n    if(len == 0) {\n      invalid_header_block = 1;\n    }\n    name = data;\n    spdylay_buffer_reader_data(&reader, (uint8_t*)data, len);\n    for(stop = data+len; data != stop; ++data) {\n      unsigned char c = *data;\n      if(c < 0x20u || c > 0x7eu || ('A' <= c && c <= 'Z')) {\n        invalid_header_block = 1;\n      }\n    }\n    *data = '\\0';\n    ++data;\n\n    len = spdylay_frame_get_nv_len(&reader, len_size);\n    val = data;\n    spdylay_buffer_reader_data(&reader, (uint8_t*)data, len);\n\n    multival = 0;\n    for(stop = data+len; data != stop; ++data) {\n      if(*data == '\\0') {\n        *idx++ = name;\n        *idx++ = val;\n        if(val == data) {\n          invalid_header_block = 1;\n        }\n        val = data+1;\n        multival = 1;\n      } else if(!VALID_HD_VALUE_CHARS[(unsigned char)*data]) {\n        invalid_header_block = 1;\n      }\n    }\n    *data = '\\0';\n    /* Check last header value is empty if NULL separator was\n       found. */\n    if(multival && val == data) {\n      invalid_header_block = 1;\n    }\n    ++data;\n\n    *idx++ = name;\n    *idx++ = val;\n  }\n  *idx = NULL;\n  assert((size_t)((char*)idx - buf) == (nvlen*2)*sizeof(char*));\n  *nv_ptr = (char**)(void *)buf;\n  if(!invalid_header_block) {\n    spdylay_frame_nv_sort(*nv_ptr);\n    for(i = 2; i < nvlen*2; i += 2) {\n      if(strcmp((*nv_ptr)[i-2], (*nv_ptr)[i]) == 0 &&\n         (*nv_ptr)[i-2] != (*nv_ptr)[i]) {\n        invalid_header_block = 1;\n        break;\n      }\n    }\n  }\n  return invalid_header_block ? SPDYLAY_ERR_INVALID_HEADER_BLOCK : 0;\n}\n\nsize_t spdylay_frame_count_nv_space(char **nv, size_t len_size)\n{\n  size_t sum = len_size;\n  int i;\n  const char *prev = \"\";\n  size_t prevlen = 0;\n  size_t prevvallen = 0;\n  for(i = 0; nv[i]; i += 2) {\n    const char *key = nv[i];\n    const char *val = nv[i+1];\n    size_t keylen = strlen(key);\n    size_t vallen = strlen(val);\n    if(prevlen == keylen && memcmp(prev, key, keylen) == 0) {\n      if(vallen) {\n        if(prevvallen) {\n          /* Join previous value, with NULL character */\n          sum += vallen+1;\n          prevvallen = vallen;\n        } else {\n          /* Previous value is empty. In this case, drop the\n             previous. */\n          sum += vallen;\n        }\n      }\n    } else {\n      prev = key;\n      prevlen = keylen;\n      prevvallen = vallen;\n      /* SPDY NV header does not include terminating NULL byte */\n      sum += keylen+vallen+len_size*2;\n    }\n  }\n  return sum;\n}\n\nssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size)\n{\n  int i;\n  uint8_t *bufp = buf+len_size;\n  uint32_t num_nv = 0;\n  const char *prev = \"\";\n  uint8_t *cur_vallen_buf = NULL;\n  uint32_t cur_vallen = 0;\n  size_t prevkeylen = 0;\n  size_t prevvallen = 0;\n  for(i = 0; nv[i]; i += 2) {\n    const char *key = nv[i];\n    const char *val = nv[i+1];\n    size_t keylen = strlen(key);\n    size_t vallen = strlen(val);\n    if(prevkeylen == keylen && memcmp(prev, key, keylen) == 0) {\n      if(vallen) {\n        if(prevvallen) {\n          /* Join previous value, with NULL character */\n          cur_vallen += vallen+1;\n          spdylay_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size);\n          *bufp = '\\0';\n          ++bufp;\n          memcpy(bufp, val, vallen);\n          bufp += vallen;\n        } else {\n          /* Previous value is empty. In this case, drop the\n             previous. */\n          cur_vallen += vallen;\n          spdylay_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size);\n          memcpy(bufp, val, vallen);\n          bufp += vallen;\n        }\n      }\n    } else {\n      ++num_nv;\n      bufp = spdylay_pack_str(bufp, key, keylen, len_size);\n      prev = key;\n      cur_vallen_buf = bufp;\n      cur_vallen = (uint32_t)vallen;\n      prevkeylen = keylen;\n      prevvallen = vallen;\n      bufp = spdylay_pack_str(bufp, val, vallen, len_size);\n    }\n  }\n  spdylay_frame_put_nv_len(buf, num_nv, len_size);\n  return bufp-buf;\n}\n\nint spdylay_frame_is_ctrl_frame(uint8_t first_byte)\n{\n  return first_byte & 0x80;\n}\n\nvoid spdylay_frame_nv_del(char **nv)\n{\n  free(nv);\n}\n\nchar** spdylay_frame_nv_copy(const char **nv)\n{\n  int i;\n  char *buf;\n  char **idx, *data;\n  size_t buflen = 0;\n  for(i = 0; nv[i]; ++i) {\n    buflen += strlen(nv[i])+1;\n  }\n  buflen += (i+1)*sizeof(char*);\n  buf = malloc(buflen);\n  if(buf == NULL) {\n    return NULL;\n  }\n  idx = (char**)(void *)buf;\n  data = buf+(i+1)*sizeof(char*);\n\n  for(i = 0; nv[i]; ++i) {\n    size_t len = strlen(nv[i])+1;\n    memcpy(data, nv[i], len);\n    *idx++ = data;\n    data += len;\n  }\n  *idx = NULL;\n  return (char**)(void *)buf;\n}\n\nstatic int spdylay_string_compar(const void *lhs, const void *rhs)\n{\n  return strcmp(*(char **)lhs, *(char **)rhs);\n}\n\nvoid spdylay_frame_nv_sort(char **nv)\n{\n  int n;\n  for(n = 0; nv[n]; ++n);\n  qsort(nv, n/2, 2*sizeof(char*), spdylay_string_compar);\n}\n\nvoid spdylay_frame_nv_downcase(char **nv)\n{\n  int i, j;\n  for(i = 0; nv[i]; i += 2) {\n    for(j = 0; nv[i][j] != '\\0'; ++j) {\n      if('A' <= nv[i][j] && nv[i][j] <= 'Z') {\n        nv[i][j] += 'a'-'A';\n      }\n    }\n  }\n}\n\nchar** spdylay_frame_nv_norm_copy(const char **nv)\n{\n  char **nv_copy;\n  nv_copy = spdylay_frame_nv_copy(nv);\n  if(nv_copy != NULL) {\n    spdylay_frame_nv_downcase(nv_copy);\n    spdylay_frame_nv_sort(nv_copy);\n  }\n  return nv_copy;\n}\n\n/* Table to translate SPDY/3 header names to SPDY/2. */\nstatic const char *spdylay_nv_3to2[] = {\n  \":host\", \"host\",\n  \":method\", \"method\",\n  \":path\", \"url\",\n  \":scheme\", \"scheme\",\n  \":status\", \"status\",\n  \":version\", \"version\",\n  NULL\n};\n\nvoid spdylay_frame_nv_3to2(char **nv)\n{\n  int i, j;\n  for(i = 0; nv[i]; i += 2) {\n    for(j = 0; spdylay_nv_3to2[j]; j += 2) {\n      if(strcmp(nv[i], spdylay_nv_3to2[j]) == 0) {\n        nv[i] = (char*)spdylay_nv_3to2[j+1];\n        break;\n      }\n    }\n  }\n}\n\nvoid spdylay_frame_nv_2to3(char **nv)\n{\n  int i, j;\n  for(i = 0; nv[i]; i += 2) {\n    for(j = 0; spdylay_nv_3to2[j]; j += 2) {\n      if(strcmp(nv[i], spdylay_nv_3to2[j+1]) == 0) {\n        nv[i] = (char*)spdylay_nv_3to2[j];\n        break;\n      }\n    }\n  }\n}\n\nvoid spdylay_frame_syn_stream_init(spdylay_syn_stream *frame,\n                                   uint16_t version, uint8_t flags,\n                                   int32_t stream_id, int32_t assoc_stream_id,\n                                   uint8_t pri, char **nv)\n{\n  memset(frame, 0, sizeof(spdylay_syn_stream));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_SYN_STREAM;\n  frame->hd.flags = flags;\n  frame->stream_id = stream_id;\n  frame->assoc_stream_id = assoc_stream_id;\n  frame->pri = pri;\n  frame->nv = nv;\n}\n\nvoid spdylay_frame_syn_stream_free(spdylay_syn_stream *frame)\n{\n  spdylay_frame_nv_del(frame->nv);\n}\n\nvoid spdylay_frame_syn_reply_init(spdylay_syn_reply *frame,\n                                  uint16_t version, uint8_t flags,\n                                  int32_t stream_id, char **nv)\n{\n  memset(frame, 0, sizeof(spdylay_syn_reply));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_SYN_REPLY;\n  frame->hd.flags = flags;\n  frame->stream_id = stream_id;\n  frame->nv = nv;\n}\n\nvoid spdylay_frame_syn_reply_free(spdylay_syn_reply *frame)\n{\n  spdylay_frame_nv_del(frame->nv);\n}\n\nvoid spdylay_frame_ping_init(spdylay_ping *frame,\n                             uint16_t version, uint32_t unique_id)\n{\n  memset(frame, 0, sizeof(spdylay_ping));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_PING;\n  frame->hd.flags = SPDYLAY_CTRL_FLAG_NONE;\n  frame->hd.length = 4;\n  frame->unique_id = unique_id;\n}\n\nvoid spdylay_frame_ping_free(spdylay_ping *frame _U_)\n{}\n\nvoid spdylay_frame_goaway_init(spdylay_goaway *frame,\n                               uint16_t version, int32_t last_good_stream_id,\n                               uint32_t status_code)\n{\n  memset(frame, 0, sizeof(spdylay_goaway));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_GOAWAY;\n  if(version == SPDYLAY_PROTO_SPDY2) {\n    frame->hd.length = 4;\n  } else if(version == SPDYLAY_PROTO_SPDY3) {\n    frame->hd.length = 8;\n    frame->status_code = status_code;\n  } else {\n    frame->hd.length = 0;\n  }\n  frame->last_good_stream_id = last_good_stream_id;\n}\n\nvoid spdylay_frame_goaway_free(spdylay_goaway *frame _U_)\n{}\n\nvoid spdylay_frame_headers_init(spdylay_headers *frame,\n                                uint16_t version, uint8_t flags,\n                                int32_t stream_id, char **nv)\n{\n  memset(frame, 0, sizeof(spdylay_headers));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_HEADERS;\n  frame->hd.flags = flags;\n  frame->stream_id = stream_id;\n  frame->nv = nv;\n}\n\nvoid spdylay_frame_headers_free(spdylay_headers *frame)\n{\n  spdylay_frame_nv_del(frame->nv);\n}\n\nvoid spdylay_frame_rst_stream_init(spdylay_rst_stream *frame,\n                                   uint16_t version,\n                                   int32_t stream_id, uint32_t status_code)\n{\n  memset(frame, 0, sizeof(spdylay_rst_stream));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_RST_STREAM;\n  frame->hd.flags = 0;\n  frame->hd.length = 8;\n  frame->stream_id = stream_id;\n  frame->status_code = status_code;\n}\n\nvoid spdylay_frame_rst_stream_free(spdylay_rst_stream *frame _U_)\n{}\n\nvoid spdylay_frame_window_update_init(spdylay_window_update *frame,\n                                      uint16_t version,\n                                      int32_t stream_id,\n                                      int32_t delta_window_size)\n{\n  memset(frame, 0, sizeof(spdylay_window_update));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_WINDOW_UPDATE;\n  frame->hd.flags = 0;\n  frame->hd.length = 8;\n  frame->stream_id = stream_id;\n  frame->delta_window_size = delta_window_size;\n}\n\nvoid spdylay_frame_window_update_free(spdylay_window_update *frame _U_)\n{}\n\nvoid spdylay_frame_settings_init(spdylay_settings *frame,\n                                 uint16_t version, uint8_t flags,\n                                 spdylay_settings_entry *iv, size_t niv)\n{\n  memset(frame, 0, sizeof(spdylay_settings));\n  frame->hd.version = version;\n  frame->hd.type = SPDYLAY_SETTINGS;\n  frame->hd.flags = flags;\n  frame->hd.length = 4+(int32_t)niv*8;\n  frame->niv = niv;\n  frame->iv = iv;\n}\n\nvoid spdylay_frame_settings_free(spdylay_settings *frame)\n{\n  free(frame->iv);\n}\n\nvoid spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,\n                             uint8_t flags,\n                             const spdylay_data_provider *data_prd)\n{\n  memset(frame, 0, sizeof(spdylay_data));\n  frame->stream_id = stream_id;\n  frame->flags = flags;\n  frame->data_prd = *data_prd;\n}\n\nvoid spdylay_frame_data_free(spdylay_data *frame _U_)\n{}\n\nssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,\n                                      size_t *buflen_ptr,\n                                      uint8_t **nvbuf_ptr,\n                                      size_t *nvbuflen_ptr,\n                                      spdylay_syn_stream *frame,\n                                      spdylay_zlib *deflater)\n{\n  ssize_t framelen;\n  size_t len_size = spdylay_frame_get_len_size(frame->hd.version);\n  if(len_size == 0) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,\n                                         nvbuf_ptr, nvbuflen_ptr,\n                                         frame->nv,\n                                         SPDYLAY_SYN_STREAM_NV_OFFSET,\n                                         len_size,\n                                         deflater);\n  if(framelen < 0) {\n    return framelen;\n  }\n  frame->hd.length = (int32_t)framelen-SPDYLAY_FRAME_HEAD_LENGTH;\n  memset(*buf_ptr, 0, SPDYLAY_SYN_STREAM_NV_OFFSET);\n  /* pack ctrl header after length is determined */\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);\n  spdylay_put_uint32be(&(*buf_ptr)[12], frame->assoc_stream_id);\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    (*buf_ptr)[16] = (frame->pri << 5);\n    (*buf_ptr)[17] = frame->slot;\n  } else {\n    (*buf_ptr)[16] = (frame->pri << 6);\n  }\n  return framelen;\n}\n\nint spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,\n                                    const uint8_t *head, size_t headlen,\n                                    const uint8_t *payload, size_t payloadlen,\n                                    spdylay_buffer *inflatebuf)\n{\n  int r;\n  size_t len_size;\n  r = spdylay_frame_unpack_syn_stream_without_nv(frame, head, headlen,\n                                                 payload, payloadlen);\n  len_size = spdylay_frame_get_len_size(frame->hd.version);\n  if(len_size == 0) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  if(r == 0) {\n    r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size);\n  }\n  return r;\n}\n\nint spdylay_frame_unpack_syn_stream_without_nv(spdylay_syn_stream *frame,\n                                               const uint8_t *head,\n                                               size_t headlen,\n                                               const uint8_t *payload,\n                                               size_t payloadlen)\n{\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  if(headlen + payloadlen != SPDYLAY_SYN_STREAM_NV_OFFSET) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;\n  frame->assoc_stream_id =\n    spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK;\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    frame->pri = (*(payload+8) >> 5);\n    frame->slot = payload[9];\n  } else {\n    frame->pri = (*(payload+8) >> 6);\n    frame->slot = 0;\n  }\n  frame->nv = NULL;\n  return 0;\n}\n\nssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,\n                                     size_t *buflen_ptr,\n                                     uint8_t **nvbuf_ptr,\n                                     size_t *nvbuflen_ptr,\n                                     spdylay_syn_reply *frame,\n                                     spdylay_zlib *deflater)\n{\n  ssize_t framelen;\n  size_t len_size;\n  ssize_t nv_offset;\n  len_size = spdylay_frame_get_len_size(frame->hd.version);\n  if(len_size == 0) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version);\n  assert(nv_offset > 0);\n  framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,\n                                         nvbuf_ptr, nvbuflen_ptr,\n                                         frame->nv, nv_offset,\n                                         len_size, deflater);\n  if(framelen < 0) {\n    return framelen;\n  }\n  frame->hd.length = (int32_t)(framelen-SPDYLAY_FRAME_HEAD_LENGTH);\n  memset(*buf_ptr, 0, nv_offset);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);\n  return framelen;\n}\n\nint spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,\n                                   const uint8_t *head, size_t headlen,\n                                   const uint8_t *payload, size_t payloadlen,\n                                   spdylay_buffer *inflatebuf)\n{\n  int r;\n  r = spdylay_frame_unpack_syn_reply_without_nv(frame, head, headlen,\n                                                payload, payloadlen);\n  if(r == 0) {\n    size_t len_size;\n    len_size = spdylay_frame_get_len_size(frame->hd.version);\n    if(len_size == 0) {\n      return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n    }\n    r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size);\n  }\n  return r;\n}\n\nint spdylay_frame_unpack_syn_reply_without_nv(spdylay_syn_reply *frame,\n                                              const uint8_t *head,\n                                              size_t headlen,\n                                              const uint8_t *payload,\n                                              size_t payloadlen)\n{\n  ssize_t nv_offset;\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version);\n  assert(nv_offset > 0);\n  if((ssize_t)(headlen + payloadlen) != nv_offset) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;\n  frame->nv = NULL;\n  return 0;\n}\n\nssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                spdylay_ping *frame)\n{\n  ssize_t framelen = 12;\n  int r;\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  memset(*buf_ptr, 0, framelen);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->unique_id);\n  return framelen;\n}\n\nint spdylay_frame_unpack_ping(spdylay_ping *frame,\n                              const uint8_t *head, size_t headlen _U_,\n                              const uint8_t *payload, size_t payloadlen)\n{\n  if(payloadlen != 4) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  frame->unique_id = spdylay_get_uint32(payload);\n  return 0;\n}\n\nssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                  spdylay_goaway *frame)\n{\n  ssize_t framelen;\n  int r;\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {\n    framelen = 12;\n  } else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    framelen = 16;\n  } else {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  memset(*buf_ptr, 0, framelen);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->last_good_stream_id);\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code);\n  }\n  return framelen;\n}\n\nint spdylay_frame_unpack_goaway(spdylay_goaway *frame,\n                                const uint8_t *head, size_t headlen _U_,\n                                const uint8_t *payload, size_t payloadlen)\n{\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {\n    if(payloadlen != 4) {\n      return SPDYLAY_ERR_INVALID_FRAME;\n    }\n  } else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    if(payloadlen != 8) {\n      return SPDYLAY_ERR_INVALID_FRAME;\n    }\n  } else {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  frame->last_good_stream_id = spdylay_get_uint32(payload) &\n    SPDYLAY_STREAM_ID_MASK;\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY3) {\n    frame->status_code = spdylay_get_uint32(payload+4);\n  } else {\n    frame->status_code = 0;\n  }\n  return 0;\n}\n\nssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                   uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr,\n                                   spdylay_headers *frame,\n                                   spdylay_zlib *deflater)\n{\n  ssize_t framelen;\n  size_t len_size;\n  ssize_t nv_offset;\n  len_size = spdylay_frame_get_len_size(frame->hd.version);\n  if(len_size == 0) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version);\n  assert(nv_offset > 0);\n  framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr,\n                                         nvbuf_ptr, nvbuflen_ptr,\n                                         frame->nv, nv_offset,\n                                         len_size, deflater);\n  if(framelen < 0) {\n    return framelen;\n  }\n  frame->hd.length = (int32_t)(framelen-SPDYLAY_FRAME_HEAD_LENGTH);\n  memset(*buf_ptr, 0, nv_offset);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);\n  return framelen;\n}\n\nint spdylay_frame_unpack_headers(spdylay_headers *frame,\n                                 const uint8_t *head, size_t headlen,\n                                 const uint8_t *payload, size_t payloadlen,\n                                 spdylay_buffer *inflatebuf)\n{\n  int r;\n  r = spdylay_frame_unpack_headers_without_nv(frame, head, headlen,\n                                              payload, payloadlen);\n  if(r == 0) {\n    size_t len_size;\n    len_size = spdylay_frame_get_len_size(frame->hd.version);\n    if(len_size == 0) {\n      return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n    }\n    r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size);\n  }\n  return r;\n}\n\nint spdylay_frame_unpack_headers_without_nv(spdylay_headers *frame,\n                                            const uint8_t *head,\n                                            size_t headlen,\n                                            const uint8_t *payload,\n                                            size_t payloadlen)\n{\n  ssize_t nv_offset;\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version);\n  assert(nv_offset > 0);\n  if((ssize_t)(headlen + payloadlen) != nv_offset) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;\n  frame->nv = NULL;\n  return 0;\n}\n\nssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                      spdylay_rst_stream *frame)\n{\n  ssize_t framelen = 16;\n  int r;\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  memset(*buf_ptr, 0, framelen);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);\n  spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code);\n  return framelen;\n}\n\nint spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame,\n                                    const uint8_t *head, size_t headlen _U_,\n                                    const uint8_t *payload, size_t payloadlen)\n{\n  if(payloadlen != 8) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;\n  frame->status_code = spdylay_get_uint32(payload+4);\n  return 0;\n}\n\nssize_t spdylay_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                         spdylay_window_update *frame)\n{\n  ssize_t framelen = 16;\n  int r;\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  memset(*buf_ptr, 0, framelen);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id);\n  spdylay_put_uint32be(&(*buf_ptr)[12], frame->delta_window_size);\n  return framelen;\n}\n\nint spdylay_frame_unpack_window_update(spdylay_window_update *frame,\n                                       const uint8_t *head, size_t headlen _U_,\n                                       const uint8_t *payload,\n                                       size_t payloadlen)\n{\n  if(payloadlen != 8) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;\n  frame->delta_window_size = spdylay_get_uint32(&payload[4]) &\n    SPDYLAY_DELTA_WINDOW_SIZE_MASK;\n  return 0;\n}\n\nssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                    spdylay_settings *frame)\n{\n  ssize_t framelen = SPDYLAY_FRAME_HEAD_LENGTH+frame->hd.length;\n  size_t i;\n  int r;\n  if(frame->hd.version != SPDYLAY_PROTO_SPDY2 &&\n     frame->hd.version != SPDYLAY_PROTO_SPDY3) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  memset(*buf_ptr, 0, framelen);\n  spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd);\n  spdylay_put_uint32be(&(*buf_ptr)[8],(uint32_t)frame->niv);\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {\n    for(i = 0; i < frame->niv; ++i) {\n      int off = (int)(i*8);\n      /* spdy/2 spec says ID is network byte order, but publicly\n         deployed server sends little endian host byte order. */\n      (*buf_ptr)[12+off+0] = (frame->iv[i].settings_id) & 0xff;\n      (*buf_ptr)[12+off+1] = (frame->iv[i].settings_id >> 8) & 0xff;\n      (*buf_ptr)[12+off+2] = (frame->iv[i].settings_id >>16) & 0xff;\n      (*buf_ptr)[15+off] = frame->iv[i].flags;\n      spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value);\n    }\n  } else {\n    for(i = 0; i < frame->niv; ++i) {\n      int off = (int)(i*8);\n      spdylay_put_uint32be(&(*buf_ptr)[12+off], frame->iv[i].settings_id);\n      (*buf_ptr)[12+off] = frame->iv[i].flags;\n      spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value);\n    }\n  }\n  return framelen;\n}\n\nint spdylay_frame_unpack_settings(spdylay_settings *frame,\n                                  const uint8_t *head, size_t headlen _U_,\n                                  const uint8_t *payload, size_t payloadlen)\n{\n  size_t i;\n  if(payloadlen < 4) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  spdylay_frame_unpack_ctrl_hd(&frame->hd, head);\n  if(frame->hd.version != SPDYLAY_PROTO_SPDY2 &&\n     frame->hd.version != SPDYLAY_PROTO_SPDY3) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  frame->niv = spdylay_get_uint32(payload);\n  if(payloadlen != 4+frame->niv*8) {\n    return SPDYLAY_ERR_INVALID_FRAME;\n  }\n  frame->iv = malloc(frame->niv*sizeof(spdylay_settings_entry));\n  if(frame->iv == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  if(frame->hd.version == SPDYLAY_PROTO_SPDY2) {\n    for(i = 0; i < frame->niv; ++i) {\n      size_t off = i*8;\n      /* ID is little endian. See comments in\n         spdylay_frame_pack_settings(). */\n      frame->iv[i].settings_id = payload[4+off] + (payload[4+off+1] << 8)\n        +(payload[4+off+2] << 16);\n      frame->iv[i].flags = payload[7+off];\n      frame->iv[i].value = spdylay_get_uint32(&payload[8+off]);\n    }\n  } else {\n    for(i = 0; i < frame->niv; ++i) {\n      size_t off = i*8;\n      frame->iv[i].settings_id = spdylay_get_uint32(&payload[4+off]) &\n        SPDYLAY_SETTINGS_ID_MASK;\n      frame->iv[i].flags = payload[4+off];\n      frame->iv[i].value = spdylay_get_uint32(&payload[8+off]);\n    }\n  }\n  return 0;\n}\n\nspdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv,\n                                              size_t niv)\n{\n  spdylay_settings_entry *iv_copy;\n  size_t len = niv*sizeof(spdylay_settings_entry);\n  iv_copy = malloc(len);\n  if(iv_copy == NULL) {\n    return NULL;\n  }\n  memcpy(iv_copy, iv, len);\n  return iv_copy;\n}\n\nstatic int spdylay_settings_entry_compar(const void *lhs, const void *rhs)\n{\n  return ((spdylay_settings_entry *)lhs)->settings_id\n    -((spdylay_settings_entry *)rhs)->settings_id;\n}\n\nvoid spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv)\n{\n  qsort(iv, niv, sizeof(spdylay_settings_entry), spdylay_settings_entry_compar);\n}\n\nssize_t spdylay_frame_nv_offset(spdylay_frame_type type, uint16_t version)\n{\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    return SPDYLAY_SYN_STREAM_NV_OFFSET;\n  case SPDYLAY_SYN_REPLY: {\n    if(version == SPDYLAY_PROTO_SPDY2) {\n      return SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET;\n    } else if(version == SPDYLAY_PROTO_SPDY3) {\n      return SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET;\n    }\n    break;\n    }\n  case SPDYLAY_HEADERS: {\n    if(version == SPDYLAY_PROTO_SPDY2) {\n      return SPDYLAY_SPDY2_HEADERS_NV_OFFSET;\n    } else if(version == SPDYLAY_PROTO_SPDY3) {\n      return SPDYLAY_SPDY3_HEADERS_NV_OFFSET;\n    }\n    break;\n  }\n  default:\n    break;\n  }\n  return -1;\n}\n\nint spdylay_frame_nv_check_null(const char **nv)\n{\n  size_t i, j;\n  for(i = 0; nv[i]; i += 2) {\n    if(nv[i][0] == '\\0' || nv[i+1] == NULL) {\n      return 0;\n    }\n    for(j = 0; nv[i][j]; ++j) {\n      unsigned char c = nv[i][j];\n      if(c < 0x20 || c > 0x7e) {\n        return 0;\n      }\n    }\n  }\n  return 1;\n}\n"
  },
  {
    "path": "lib/spdylay_frame.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_FRAME_H\n#define SPDYLAY_FRAME_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_zlib.h\"\n#include \"spdylay_buffer.h\"\n\n#define SPDYLAY_STREAM_ID_MASK 0x7fffffff\n/* This is actually the maximum length of a control frame in SPDY/2\n   and 3. */\n#define SPDYLAY_LENGTH_MASK 0xffffff\n#define SPDYLAY_VERSION_MASK 0x7fff\n#define SPDYLAY_DELTA_WINDOW_SIZE_MASK 0x7fffffff\n#define SPDYLAY_SETTINGS_ID_MASK 0xffffff\n\n/* The length of DATA frame payload. */\n#define SPDYLAY_DATA_PAYLOAD_LENGTH 4096\n\n/* The number of bytes of frame header. */\n#define SPDYLAY_FRAME_HEAD_LENGTH 8\n\n/* The offset to the name/value header block in the frame (including\n   frame header) */\n#define SPDYLAY_SYN_STREAM_NV_OFFSET 18\n\n#define SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET 14\n#define SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET 12\n\n#define SPDYLAY_SPDY2_HEADERS_NV_OFFSET 14\n#define SPDYLAY_SPDY3_HEADERS_NV_OFFSET 12\n\n#define spdylay_frame_get_nv_len(RED, LEN_SIZE)                   \\\n  (LEN_SIZE == 2 ? spdylay_buffer_reader_uint16(RED) :            \\\n   spdylay_buffer_reader_uint32(RED))\n\n#define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE)                    \\\n  (LEN_SIZE == 2 ?                                                      \\\n   spdylay_put_uint16be(OUT, VAL) : spdylay_put_uint32be(OUT, VAL))\n\n/* Category of SPDY frames. */\ntypedef enum {\n  /* Control frame */\n  SPDYLAY_CTRL,\n  /* DATA frame */\n  SPDYLAY_DATA\n} spdylay_frame_category;\n\n/**\n * @struct\n * The DATA frame. It has the following members:\n */\ntypedef struct {\n  /**\n   * The data to be sent for this DATA frame.\n   */\n  spdylay_data_provider data_prd;\n  /**\n   * The stream ID.\n   */\n  int32_t stream_id;\n  /**\n   * The DATA frame flags. See :type:`spdylay_data_flag`.\n   */\n  uint8_t flags;\n  /**\n   * The flag to indicate whether EOF was reached or not. Initially\n   * |eof| is 0. It becomes 1 after all data were read.\n   */\n  uint8_t eof;\n} spdylay_data;\n\n/*\n * Returns the number of bytes in length of name/value pair for the\n * given protocol version |version|. If |version| is not supported,\n * returns 0.\n */\nsize_t spdylay_frame_get_len_size(uint16_t version);\n\n/*\n * Packs SYN_STREAM frame |frame| in wire format and store it in\n * |*buf_ptr|.  The capacity of |*buf_ptr| is |*buflen_ptr| bytes.\n * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire\n * format temporarily. Its length is |*nvbuflen_ptr| bytes.  This\n * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store\n * frame and name/value pairs. When expansion occurred, memory\n * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed.\n * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are\n * updated accordingly.\n *\n * frame->hd.length is assigned after length is determined during\n * packing process.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_ZLIB\n *     The deflate operation failed.\n * SPDYLAY_ERR_FRAME_TOO_LARGE\n *     The length of the frame is too large.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,\n                                      size_t *buflen_ptr,\n                                      uint8_t **nvbuf_ptr,\n                                      size_t *nvbuflen_ptr,\n                                      spdylay_syn_stream *frame,\n                                      spdylay_zlib *deflater);\n\n/*\n * Unpacks SYN_STREAM frame byte sequence into |frame|.  The control\n * frame header is given in |head| with |headlen| length. In spdy/3\n * spec, headlen is 8 bytes. |payload| is the data after length field\n * of the header and just before name/value header block.\n *\n * The |inflatebuf| contains inflated name/value header block in wire\n * foramt.\n *\n * This function also validates the name/value pairs. If unpacking\n * succeeds but validation fails, it is indicated by returning\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK\n *     Unpacking succeeds but the header block is invalid.\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,\n                                    const uint8_t *head, size_t headlen,\n                                    const uint8_t *payload, size_t payloadlen,\n                                    spdylay_buffer *inflatebuf);\n\n/*\n * Unpacks SYN_STREAM frame byte sequence into |frame|. This function\n * only unapcks bytes that come before name/value header block.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_syn_stream_without_nv(spdylay_syn_stream *frame,\n                                               const uint8_t *head,\n                                               size_t headlen,\n                                               const uint8_t *payload,\n                                               size_t payloadlen);\n\n/*\n * Packs SYN_REPLY frame |frame| in wire frame format and store it in\n * |*buf_ptr|.  The capacity of |*buf_ptr| is |*buflen_ptr| bytes.\n * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire\n * format temporarily. Its length is |*nvbuflen_ptr| bytes.  This\n * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store\n * frame and name/value pairs. When expansion occurred, memory\n * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed.\n * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are\n * updated accordingly.\n *\n * frame->hd.length is assigned after length is determined during\n * packing process.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_ZLIB\n *     The deflate operation failed.\n * SPDYLAY_ERR_FRAME_TOO_LARGE\n *     The length of the frame is too large.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,\n                                     size_t *buflen_ptr,\n                                     uint8_t **nvbuf_ptr,\n                                     size_t *nvbuflen_ptr,\n                                     spdylay_syn_reply *frame,\n                                     spdylay_zlib *deflater);\n\n/*\n * Unpacks SYN_REPLY frame byte sequence into |frame|.\n *\n * The |inflatebuf| contains inflated name/value header block in wire\n * foramt.\n *\n * This function also validates the name/value pairs. If unpacking\n * succeeds but validation fails, it is indicated by returning\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK\n *     Unpacking succeeds but the header block is invalid.\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,\n                                   const uint8_t *head, size_t headlen,\n                                   const uint8_t *payload, size_t payloadlen,\n                                   spdylay_buffer *inflatebuf);\n\n/*\n * Unpacks SYN_REPLY frame byte sequence into |frame|. This function\n * only unapcks bytes that come before name/value header block.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_syn_reply_without_nv(spdylay_syn_reply *frame,\n                                              const uint8_t *head,\n                                              size_t headlen,\n                                              const uint8_t *payload,\n                                              size_t payloadlen);\n\n/*\n * Packs PING frame |frame| in wire format and store it in\n * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                spdylay_ping *frame);\n\n/*\n * Unpacks PING wire format into |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_ping(spdylay_ping *frame,\n                              const uint8_t *head, size_t headlen,\n                              const uint8_t *payload, size_t payloadlen);\n\n/*\n * Packs GOAWAY frame |frame | in wire format and store it in\n * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                  spdylay_goaway *frame);\n\n/*\n * Unpacks GOAWAY wire format into |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_goaway(spdylay_goaway *frame,\n                                const uint8_t *head, size_t headlen,\n                                const uint8_t *payload, size_t payloadlen);\n\n/*\n * Packs HEADERS frame |frame| in wire format and store it in\n * |*buf_ptr|.  The capacity of |*buf_ptr| is |*buflen_ptr| bytes.\n * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire\n * format temporarily. Its length is |*nvbuflen_ptr| bytes.  This\n * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store\n * frame and name/value pairs. When expansion occurred, memory\n * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed.\n * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are\n * updated accordingly.\n *\n * frame->hd.length is assigned after length is determined during\n * packing process.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_ZLIB\n *     The deflate operation failed.\n * SPDYLAY_ERR_FRAME_TOO_LARGE\n *     The length of the frame is too large.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                   uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr,\n                                   spdylay_headers *frame,\n                                   spdylay_zlib *deflater);\n\n/*\n * Unpacks HEADERS wire format into |frame|.\n *\n * The |inflatebuf| contains inflated name/value header block in wire\n * foramt.\n *\n * This function also validates the name/value pairs. If unpacking\n * succeeds but validation fails, it is indicated by returning\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK\n *     Unpacking succeeds but the header block is invalid.\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_frame_unpack_headers(spdylay_headers *frame,\n                                 const uint8_t *head, size_t headlen,\n                                 const uint8_t *payload, size_t payloadlen,\n                                 spdylay_buffer *inflatebuf);\n\n/*\n * Unpacks HEADERS frame byte sequence into |frame|. This function\n * only unapcks bytes that come before name/value header block.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_headers_without_nv(spdylay_headers *frame,\n                                            const uint8_t *head,\n                                            size_t headlen,\n                                            const uint8_t *payload,\n                                            size_t payloadlen);\n\n/*\n * Packs RST_STREAM frame |frame| in wire frame format and store it in\n * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16\n * bytes long.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                      spdylay_rst_stream *frame);\n\n/*\n * Unpacks RST_STREAM frame byte sequence into |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame,\n                                    const uint8_t *head, size_t headlen,\n                                    const uint8_t *payload, size_t payloadlen);\n\n\n/*\n * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it\n * in |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|. In SPDY/3 spec, WINDOW_UPDATE wire format is always 16\n * bytes long.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                         spdylay_window_update *frame);\n\n/*\n * Unpacks WINDOW_UPDATE frame byte sequence into |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_unpack_window_update(spdylay_window_update *frame,\n                                       const uint8_t *head, size_t headlen,\n                                       const uint8_t *payload,\n                                       size_t payloadlen);\n\n/*\n * Packs SETTINGS frame |frame| in wire format and store it in\n * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|.\n *\n * This function returns the size of packed frame if it succeeds, or\n * returns one of the following negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr,\n                                    spdylay_settings *frame);\n\n/*\n * Unpacks SETTINGS wire format into |frame|.\n *\n * This function returns 0 if it succeeds or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_frame_unpack_settings(spdylay_settings *frame,\n                                  const uint8_t *head, size_t headlen,\n                                  const uint8_t *payload, size_t payloadlen);\n\n/*\n * Returns number of bytes to pack name/value pairs |nv|. This\n * function expects |nv| is sorted in ascending order of key.\n * |len_size| is the number of bytes in length of name/value pair and\n * it must be 2 or 4.\n *\n * This function can handles duplicate keys and concatenation of thier\n * values with '\\0'.\n */\nsize_t spdylay_frame_count_nv_space(char **nv, size_t len_size);\n\n/*\n * Packs name/value pairs in |nv| in |buf|. |buf| must have at least\n * spdylay_frame_count_nv_space(nv) bytes.  |len_size| is the number\n * of bytes in length of name/value pair and it must be 2 or 4.\n */\nssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size);\n\n/*\n * Packs name/value pairs in |nv| in |*buf_ptr| with offset\n * |nv_offset|.  It means first byte of packed name/value pairs is\n * stored in |*buf_ptr|+|nv_offset|.  |*buf_ptr| and |*nvbuf_ptr| are\n * expanded as necessary.\n *\n * This function returns the number of the bytes for the frame\n * containing this name/value pairs if it succeeds, or one of the\n * following negative error codes:\n *\n * SPDYLAY_ERR_ZLIB\n *     The deflate operation failed.\n * SPDYLAY_ERR_FRAME_TOO_LARGE\n *     The length of the frame is too large.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr,\n                                    size_t *buflen_ptr,\n                                    uint8_t **nvbuf_ptr,\n                                    size_t *nvbuflen_ptr,\n                                    char **nv, size_t nv_offset,\n                                    size_t len_size,\n                                    spdylay_zlib *deflater);\n\n/*\n * Counts number of name/value pair in |in| and computes length of\n * buffers to store unpacked name/value pair and store them in\n * |*nvlen_ptr| and |*buflen_ptr| respectively. |len_size| is the\n * number of bytes in length of name/value pair and it must be 2 or\n * 4. We use folloing data structure in |*buflen_ptr| size.  First\n * part of the data is array of pointer to name/value pair.  Supporse\n * the buf pointer points to the data region and N is the number of\n * name/value pair.  First (N*2+1)*sizeof(char*) bytes contain array\n * of pointer to name/value pair and terminating NULL.  Each pointer\n * to name/value pair points to the string in remaining data.  For\n * each name/value pair, the name is copied to the remaining data with\n * terminating NULL character. The value is also copied to the\n * position after the data with terminating NULL character. The\n * corresponding index is assigned to these pointers. If the value\n * contains multiple values (delimited by single NULL), for each such\n * data, corresponding index is assigned to name/value pointers. In\n * this case, the name string is reused.\n *\n * With the above stragety, |*buflen_ptr| is calculated as\n * (N*2+1)*sizeof(char*)+sum(strlen(name)+1+strlen(value)+1){for each\n * name/value pair}.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_FRAME\n *     The input data are invalid.\n */\nint spdylay_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr,\n                                        spdylay_buffer *in, size_t len_size);\n\n/*\n * Unpacks name/value header block in wire format |in| and stores them\n * in |*nv_ptr|.  Thif function allocates enough memory to store\n * name/value pairs in |*nv_ptr|.  |len_size| is the number of bytes\n * in length of name/value pair and it must be 2 or 4.\n *\n * This function also validates the name/value pairs. If unpacking\n * succeeds but validation fails, it is indicated by returning\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK.\n *\n * If error other than SPDYLAY_ERR_INVALID_HEADER_BLOCK is returned,\n * the |nv_ptr| is not assigned. In other words,\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK means unpacking succeeded, but\n * header block validation failed.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_HEADER_BLOCK\n *     Unpacking succeeds but the header block is invalid. The\n *     possible reasons are: There are duplicate header names; or the\n *     header names are not encoded in US-ASCII character set and not\n *     lower cased; or the header name is zero-length string.\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_frame_unpack_nv(char ***nv_ptr, spdylay_buffer *in,\n                            size_t len_size);\n\n/*\n * Initializes SYN_STREAM frame |frame| with given values.  |frame|\n * takes ownership of |nv|, so caller must not free it. If stream_id\n * is not assigned yet, it must be 0.\n */\nvoid spdylay_frame_syn_stream_init(spdylay_syn_stream *frame,\n                                   uint16_t version, uint8_t flags,\n                                   int32_t stream_id, int32_t assoc_stream_id,\n                                   uint8_t pri, char **nv);\n\nvoid spdylay_frame_syn_stream_free(spdylay_syn_stream *frame);\n\nvoid spdylay_frame_syn_reply_init(spdylay_syn_reply *frame,\n                                  uint16_t version, uint8_t flags,\n                                  int32_t stream_id, char **nv);\n\nvoid spdylay_frame_syn_reply_free(spdylay_syn_reply *frame);\n\nvoid spdylay_frame_ping_init(spdylay_ping *frame, uint16_t version,\n                             uint32_t unique_id);\n\nvoid spdylay_frame_ping_free(spdylay_ping *frame);\n\n/*\n * Initializes GOAWAY frame |frame| with given values.  The\n * |status_code| is ignored if |version| == SPDYLAY_PROTO_SPDY2.\n */\nvoid spdylay_frame_goaway_init(spdylay_goaway *frame, uint16_t version,\n                               int32_t last_good_stream_id,\n                               uint32_t status_code);\n\nvoid spdylay_frame_goaway_free(spdylay_goaway *frame);\n\nvoid spdylay_frame_headers_init(spdylay_headers *frame, uint16_t version,\n                                uint8_t flags,\n                                int32_t stream_id, char **nv);\n\nvoid spdylay_frame_headers_free(spdylay_headers *frame);\n\nvoid spdylay_frame_rst_stream_init(spdylay_rst_stream *frame,\n                                   uint16_t version,\n                                   int32_t stream_id, uint32_t status_code);\n\nvoid spdylay_frame_rst_stream_free(spdylay_rst_stream *frame);\n\nvoid spdylay_frame_window_update_init(spdylay_window_update *frame,\n                                      uint16_t version,\n                                      int32_t stream_id,\n                                      int32_t delta_window_size);\n\nvoid spdylay_frame_window_update_free(spdylay_window_update *frame);\n\n/*\n * Initializes SETTINGS frame |frame| with given values. |frame| takes\n * ownership of |iv|, so caller must not free it. The |flags| are\n * bitwise-OR of one or more of spdylay_settings_flag.\n */\nvoid spdylay_frame_settings_init(spdylay_settings *frame,\n                                 uint16_t version, uint8_t flags,\n                                 spdylay_settings_entry *iv, size_t niv);\n\nvoid spdylay_frame_settings_free(spdylay_settings *frame);\n\nvoid spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id,\n                             uint8_t flags,\n                             const spdylay_data_provider *data_prd);\n\nvoid spdylay_frame_data_free(spdylay_data *frame);\n\n/*\n * Returns 1 if the first byte of this frame indicates it is a control\n * frame.\n */\nint spdylay_frame_is_ctrl_frame(uint8_t first_byte);\n\n/*\n * Deallocates memory of name/value pair |nv|.\n */\nvoid spdylay_frame_nv_del(char **nv);\n\n/*\n * Makes a deep copy of |nv| and returns the copy.  This function\n * returns the pointer to the copy if it succeeds, or NULL.  To free\n * allocated memory, use spdylay_frame_nv_del().\n */\nchar** spdylay_frame_nv_copy(const char **nv);\n\n/*\n * Sorts |nv| in the ascending order of name.\n */\nvoid spdylay_frame_nv_sort(char **nv);\n\n/*\n * Makes names in |nv| lower cased.\n */\nvoid spdylay_frame_nv_downcase(char **nv);\n\n/*\n * This function first makes a copy of |nv| using\n * spdylay_frame_nv_copy().  If it succeeds, then call\n * spdylay_frame_nv_downcase() and spdylay_frame_nv_sort() with the\n * copied name/value pairs.\n *\n * This function returns the copied name/value pairs if it succeeds,\n * or NULL.\n */\nchar** spdylay_frame_nv_norm_copy(const char **nv);\n\n/*\n * Translates the |nv| in SPDY/3 header names into SPDY/2.\n */\nvoid spdylay_frame_nv_3to2(char **nv);\n\n/*\n * Translates the |nv| in SPDY/2 header names into SPDY/3.\n */\nvoid spdylay_frame_nv_2to3(char **nv);\n\n/*\n * Assigns the members of the |origin| using \":scheme\" and \":host\"\n * values in |nv|.\n *\n * If \":host\" value contains ':', this function parses the chracters\n * after ':' as integer and uses it as port number.\n *\n * If ':' is missing in :host value, the default port number is used.\n * The only defined default port number is 443.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error code:\n *\n * SPDYLAY_ERR_INVALID_ARGUMENT\n *     The |nv| lacks either :scheme or :host, or both.\n */\nint spdylay_frame_nv_set_origin(char **nv, spdylay_origin *origin);\n\n/*\n * Makes copy of |iv| and return the copy. The |niv| is the number of\n * entries in |iv|. This function returns the pointer to the copy if\n * it succeeds, or NULL.\n */\nspdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv,\n                                              size_t niv);\n\n/*\n * Sorts the |iv| with the ascending order of the settings_id member.\n * The number of the element in the array pointed by the |iv| is given\n * by the |niv|.\n */\nvoid spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv);\n\n/*\n * Returns the offset of the name/header block in the frame, including\n * frame header. If |type| is neither SPDYLAY_SYN_STREAM,\n * SPDYLAY_SYN_REPLY nor SPDYLAY_HEADERS, this function returns -1.\n * If |version| is unknown, this function returns -1.\n */\nssize_t spdylay_frame_nv_offset(spdylay_frame_type type, uint16_t version);\n\n/*\n * Checks names are not empty string and do not contain control\n * characters and values are not NULL.\n *\n * This function returns nonzero if it succeeds, or 0.\n */\nint spdylay_frame_nv_check_null(const char **nv);\n\n#endif /* SPDYLAY_FRAME_H */\n"
  },
  {
    "path": "lib/spdylay_gzip.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_gzip.h\"\n\n#include <assert.h>\n\nint spdylay_gzip_inflate_new(spdylay_gzip **inflater_ptr)\n{\n  int rv;\n  *inflater_ptr = malloc(sizeof(spdylay_gzip));\n  if(*inflater_ptr == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  (*inflater_ptr)->finished = 0;\n  (*inflater_ptr)->zst.next_in = Z_NULL;\n  (*inflater_ptr)->zst.avail_in = 0;\n  (*inflater_ptr)->zst.zalloc = Z_NULL;\n  (*inflater_ptr)->zst.zfree = Z_NULL;\n  (*inflater_ptr)->zst.opaque = Z_NULL;\n  rv = inflateInit2(&(*inflater_ptr)->zst, 47);\n  if(rv != Z_OK) {\n    free(*inflater_ptr);\n    return SPDYLAY_ERR_GZIP;\n  }\n  return 0;\n}\n\nvoid spdylay_gzip_inflate_del(spdylay_gzip *inflater)\n{\n  if(inflater != NULL) {\n    inflateEnd(&inflater->zst);\n    free(inflater);\n  }\n}\n\nint spdylay_gzip_inflate(spdylay_gzip *inflater,\n                         uint8_t *out, size_t *outlen_ptr,\n                         const uint8_t *in, size_t *inlen_ptr)\n{\n  int rv;\n  if(inflater->finished) {\n    return SPDYLAY_ERR_GZIP;\n  }\n  inflater->zst.avail_in = (uint)*inlen_ptr;\n  inflater->zst.next_in = (unsigned char*)in;\n  inflater->zst.avail_out = (uint)*outlen_ptr;\n  inflater->zst.next_out = out;\n\n  rv = inflate(&inflater->zst, Z_NO_FLUSH);\n\n  *inlen_ptr -= inflater->zst.avail_in;\n  *outlen_ptr -= inflater->zst.avail_out;\n  switch(rv) {\n  case Z_STREAM_END:\n    inflater->finished = 1;\n  case Z_OK:\n  case Z_BUF_ERROR:\n    return 0;\n  case Z_DATA_ERROR:\n  case Z_STREAM_ERROR:\n  case Z_NEED_DICT:\n  case Z_MEM_ERROR:\n    return SPDYLAY_ERR_GZIP;\n  default:\n    assert(0);\n    /* We need this for some compilers */\n    return 0;\n  }\n}\n"
  },
  {
    "path": "lib/spdylay_gzip.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_GZIP_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n#include <zlib.h>\n\n#include <spdylay/spdylay.h>\n\nstruct spdylay_gzip {\n  z_stream zst;\n  int8_t finished;\n};\n\n#endif /* SPDYLAY_GZIP_H */\n"
  },
  {
    "path": "lib/spdylay_helper.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_helper.h\"\n\n#include <string.h>\n\n#include \"spdylay_net.h\"\n\nvoid spdylay_put_uint16be(uint8_t *buf, uint16_t n)\n{\n  uint16_t x = htons(n);\n  memcpy(buf, &x, sizeof(uint16_t));\n}\n\nvoid spdylay_put_uint32be(uint8_t *buf, uint32_t n)\n{\n  uint32_t x = htonl(n);\n  memcpy(buf, &x, sizeof(uint32_t));\n}\n\nuint16_t spdylay_get_uint16(const uint8_t *data)\n{\n  uint16_t n;\n  memcpy(&n, data, sizeof(uint16_t));\n  return ntohs(n);\n}\n\nuint32_t spdylay_get_uint32(const uint8_t *data)\n{\n  uint32_t n;\n  memcpy(&n, data, sizeof(uint32_t));\n  return ntohl(n);\n}\n\nint spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,\n                           size_t min_length)\n{\n  if(min_length > *buflen_ptr) {\n    uint8_t *temp;\n    min_length = (min_length+4095)/4096*4096;\n    temp = malloc(min_length);\n    if(temp == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    } else {\n      free(*buf_ptr);\n      *buf_ptr = temp;\n      *buflen_ptr = min_length;\n    }\n  }\n  return 0;\n}\n\nconst char* spdylay_strerror(int error_code)\n{\n  switch(error_code) {\n  case 0:\n    return \"Success\";\n  case SPDYLAY_ERR_INVALID_ARGUMENT:\n    return \"Invalid argument\";\n  case SPDYLAY_ERR_ZLIB:\n    return \"Zlib error\";\n  case SPDYLAY_ERR_UNSUPPORTED_VERSION:\n    return \"Unsupported SPDY version\";\n  case SPDYLAY_ERR_WOULDBLOCK:\n    return \"Operation would block\";\n  case SPDYLAY_ERR_PROTO:\n    return \"Protocol error\";\n  case SPDYLAY_ERR_INVALID_FRAME:\n    return \"Invalid frame octets\";\n  case SPDYLAY_ERR_EOF:\n    return \"EOF\";\n  case SPDYLAY_ERR_DEFERRED:\n    return \"Data transfer deferred\";\n  case SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE:\n    return \"No more Stream ID available\";\n  case SPDYLAY_ERR_STREAM_CLOSED:\n    return \"Stream was already closed or invalid\";\n  case SPDYLAY_ERR_STREAM_CLOSING:\n    return \"Stream is closing\";\n  case SPDYLAY_ERR_STREAM_SHUT_WR:\n    return \"The transmission is not allowed for this stream\";\n  case SPDYLAY_ERR_INVALID_STREAM_ID:\n    return \"Stream ID is invalid\";\n  case SPDYLAY_ERR_INVALID_STREAM_STATE:\n    return \"Invalid stream state\";\n  case SPDYLAY_ERR_DEFERRED_DATA_EXIST:\n    return \"Another DATA frame has already been deferred\";\n  case SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED:\n    return \"SYN_STREAM is not allowed\";\n  case SPDYLAY_ERR_GOAWAY_ALREADY_SENT:\n    return \"GOAWAY has already been sent\";\n  case SPDYLAY_ERR_INVALID_HEADER_BLOCK:\n    return \"Invalid header block\";\n  case SPDYLAY_ERR_INVALID_STATE:\n    return \"Invalid state\";\n  case SPDYLAY_ERR_GZIP:\n    return \"Gzip error\";\n  case SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE:\n    return \"The user callback function failed due to the temporal error\";\n  case SPDYLAY_ERR_FRAME_TOO_LARGE:\n    return \"The length of the frame is too large\";\n  case SPDYLAY_ERR_NOMEM:\n    return \"Out of memory\";\n  case SPDYLAY_ERR_CALLBACK_FAILURE:\n    return \"The user callback function failed\";\n  default:\n    return \"Unknown error code\";\n  }\n}\n"
  },
  {
    "path": "lib/spdylay_helper.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_HELPER_H\n#define SPDYLAY_HELPER_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n\n#define spdylay_min(A, B) ((A) < (B) ? (A) : (B))\n#define spdylay_max(A, B) ((A) > (B) ? (A) : (B))\n\n/*\n * Copies 2 byte unsigned integer |n| in host byte order to |buf| in\n * network byte order.\n */\nvoid spdylay_put_uint16be(uint8_t *buf, uint16_t n);\n\n/*\n * Copies 4 byte unsigned integer |n| in host byte order to |buf| in\n * network byte order.\n */\nvoid spdylay_put_uint32be(uint8_t *buf, uint32_t n);\n\n/*\n * Retrieves 2 byte unsigned integer stored in |data| in network byte\n * order and returns it in host byte order.\n */\nuint16_t spdylay_get_uint16(const uint8_t *data);\n\n/*\n * Retrieves 4 byte unsigned integer stored in |data| in network byte\n * order and returns it in host byte order.\n */\nuint32_t spdylay_get_uint32(const uint8_t *data);\n\n/*\n * Ensures that buffer |*buf_ptr| with |*buflen_ptr| length has at\n * least |min_length| bytes. If |min_length| > |*buflen_ptr|,\n * allocates new buffer having at least |min_length| bytes and assigns\n * its pointer to |*buf_ptr| and allocated number of bytes to\n * |*buflen_ptr|. The memory pointed by |*buf_ptr| previously is\n * freed. No memory copy is done between old and new buffer.\n * |*buf_ptr| and |*buflen_ptr| are only updated iff this function\n * succeeds.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr,\n                           size_t min_length);\n\n#endif /* SPDYLAY_HELPER_H */\n"
  },
  {
    "path": "lib/spdylay_int.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_INT_H\n#define SPDYLAY_INT_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <stdint.h>\n\n/* Macros, types and constants for internal use */\n\ntypedef int (*spdylay_compar)(const void *lhs, const void *rhs);\n\n#endif /* SPDYLAY_INT_H */\n"
  },
  {
    "path": "lib/spdylay_map.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_map.h\"\n\n#include <string.h>\n\n#define INITIAL_TABLE_LENGTH 16\n\nint spdylay_map_init(spdylay_map *map)\n{\n  map->tablelen = INITIAL_TABLE_LENGTH;\n  map->table = malloc(sizeof(spdylay_map_entry*) * map->tablelen);\n  if(map->table == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  memset(map->table, 0, sizeof(spdylay_map_entry*) * map->tablelen);\n  map->size = 0;\n  return 0;\n}\n\nvoid spdylay_map_free(spdylay_map *map)\n{\n  free(map->table);\n}\n\nvoid spdylay_map_each_free(spdylay_map *map,\n                           int (*func)(spdylay_map_entry *entry, void *ptr),\n                           void *ptr)\n{\n  size_t i;\n  for(i = 0; i < map->tablelen; ++i) {\n    spdylay_map_entry *entry;\n    for(entry = map->table[i]; entry;) {\n      spdylay_map_entry *next = entry->next;\n      func(entry, ptr);\n      entry = next;\n    }\n    map->table[i] = NULL;\n  }\n}\n\nint spdylay_map_each(spdylay_map *map,\n                     int (*func)(spdylay_map_entry *entry, void *ptr),\n                     void *ptr)\n{\n  int rv;\n  size_t i;\n  for(i = 0; i < map->tablelen; ++i) {\n    spdylay_map_entry *entry;\n    for(entry = map->table[i]; entry; entry = entry->next) {\n      rv = func(entry, ptr);\n      if(rv != 0) {\n        return rv;\n      }\n    }\n  }\n  return 0;\n}\n\nvoid spdylay_map_entry_init(spdylay_map_entry *entry, key_type key)\n{\n  entry->key = key;\n  entry->next = NULL;\n}\n\n/* Same hash function in openjdk HashMap source code. */\n/* The |mod| must be power of 2 */\nstatic int32_t hash(int32_t h, int32_t mod)\n{\n  h ^= (h >> 20) ^ (h >> 12);\n  return (h ^ (h >> 7) ^ (h >> 4)) & (mod - 1);\n}\n\nstatic int insert(spdylay_map_entry **table, size_t tablelen,\n                  spdylay_map_entry *entry)\n{\n  int32_t h = hash(entry->key, (int32_t)tablelen);\n  if(table[h] == NULL) {\n    table[h] = entry;\n  } else {\n    spdylay_map_entry *p;\n    /* We won't allow duplicated key, so check it out. */\n    for(p = table[h]; p; p = p->next) {\n      if(p->key == entry->key) {\n        return SPDYLAY_ERR_INVALID_ARGUMENT;\n      }\n    }\n    entry->next = table[h];\n    table[h] = entry;\n  }\n  return 0;\n}\n\n/* new_tablelen must be power of 2 */\nstatic int resize(spdylay_map *map, size_t new_tablelen)\n{\n  size_t i;\n  spdylay_map_entry **new_table;\n  new_table = malloc(sizeof(spdylay_map_entry*) * new_tablelen);\n  if(new_table == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  memset(new_table, 0, sizeof(spdylay_map_entry*) * new_tablelen);\n  for(i = 0; i < map->tablelen; ++i) {\n    spdylay_map_entry *entry;\n    for(entry = map->table[i]; entry;) {\n      spdylay_map_entry *next = entry->next;\n      entry->next = NULL;\n      /* This function must succeed */\n      insert(new_table, new_tablelen, entry);\n      entry = next;\n    }\n  }\n  free(map->table);\n  map->tablelen = new_tablelen;\n  map->table = new_table;\n  return 0;\n}\n\nint spdylay_map_insert(spdylay_map *map, spdylay_map_entry *new_entry)\n{\n  int rv;\n  /* Load factor is 0.75 */\n  if((map->size + 1) * 4 > map->tablelen * 3) {\n    rv = resize(map, map->tablelen * 2);\n    if(rv != 0) {\n      return rv;\n    }\n  }\n  rv = insert(map->table, map->tablelen, new_entry);\n  if(rv != 0) {\n    return rv;\n  }\n  ++map->size;\n  return 0;\n}\n\nspdylay_map_entry* spdylay_map_find(spdylay_map *map, key_type key)\n{\n  int32_t h;\n  spdylay_map_entry *entry;\n  h = hash(key, (int32_t)map->tablelen);\n  for(entry = map->table[h]; entry; entry = entry->next) {\n    if(entry->key == key) {\n      return entry;\n    }\n  }\n  return NULL;\n}\n\nint spdylay_map_remove(spdylay_map *map, key_type key)\n{\n  int32_t h;\n  spdylay_map_entry *entry, *prev;\n  h = hash(key, (int32_t)map->tablelen);\n  prev = NULL;\n  for(entry = map->table[h]; entry; entry = entry->next) {\n    if(entry->key == key) {\n      if(prev == NULL) {\n        map->table[h] = entry->next;\n      } else {\n        prev->next = entry->next;\n      }\n      --map->size;\n      return 0;\n    }\n    prev = entry;\n  }\n  return SPDYLAY_ERR_INVALID_ARGUMENT;\n}\n\nsize_t spdylay_map_size(spdylay_map *map)\n{\n  return map->size;\n}\n"
  },
  {
    "path": "lib/spdylay_map.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_MAP_H\n#define SPDYLAY_MAP_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_int.h\"\n\n/* Implementation of unordered map */\n\ntypedef uint32_t key_type;\n\ntypedef struct spdylay_map_entry {\n  struct spdylay_map_entry *next;\n  key_type key;\n} spdylay_map_entry;\n\ntypedef struct {\n  spdylay_map_entry **table;\n  size_t tablelen;\n  size_t size;\n} spdylay_map;\n\n/*\n * Initializes the map |map|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *   Out of memory\n */\nint spdylay_map_init(spdylay_map *map);\n\n/*\n * Deallocates any resources allocated for |map|. The stored entries\n * are not freed by this function. Use spdylay_map_each_free() to free\n * each entries.\n */\nvoid spdylay_map_free(spdylay_map *map);\n\n/*\n * Deallocates each entries using |func| function and any resources\n * allocated for |map|. The |func| function is responsible for freeing\n * given the |entry| object. The |ptr| will be passed to the |func| as\n * send argument. The return value of the |func| will be ignored.\n */\nvoid spdylay_map_each_free(spdylay_map *map,\n                           int (*func)(spdylay_map_entry *entry, void *ptr),\n                           void *ptr);\n\n/*\n * Initializes the |entry| with the |key|. All entries to be inserted\n * to the map must be initialized with this function.\n */\nvoid spdylay_map_entry_init(spdylay_map_entry *entry, key_type key);\n\n/*\n * Inserts the new |entry| with the key |entry->key| to the map |map|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_ARGUMENT\n *     The item associated by |key| already exists.\n * SPDYLAY_ERR_NOMEM\n *   Out of memory\n */\nint spdylay_map_insert(spdylay_map *map, spdylay_map_entry *entry);\n\n/*\n * Returns the entry associated by the key |key|.  If there is no such\n * entry, this function returns NULL.\n */\nspdylay_map_entry* spdylay_map_find(spdylay_map *map, key_type key);\n\n/*\n * Removes the entry associated by the key |key| from the |map|.  The\n * removed entry is not freed by this function.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_ARGUMENT\n *     The entry associated by |key| does not exist.\n */\nint spdylay_map_remove(spdylay_map *map, key_type key);\n\n/*\n * Returns the number of items stored in the map |map|.\n */\nsize_t spdylay_map_size(spdylay_map *map);\n\n/*\n * Applies the function |func| to each entry in the |map| with the\n * optional user supplied pointer |ptr|.\n *\n * If the |func| returns 0, this function calls the |func| with the\n * next entry. If the |func| returns nonzero, it will not call the\n * |func| for further entries and return the return value of the\n * |func| immediately.  Thus, this function returns 0 if all the\n * invocations of the |func| return 0, or nonzero value which the last\n * invocation of |func| returns.\n *\n * Don't use this function to free each entry. Use\n * spdylay_map_each_free() instead.\n */\nint spdylay_map_each(spdylay_map *map,\n                     int (*func)(spdylay_map_entry *entry, void *ptr),\n                     void *ptr);\n\n#endif /* SPDYLAY_MAP_H */\n"
  },
  {
    "path": "lib/spdylay_net.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_NET_H\n#define SPDYLAY_NET_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#ifdef HAVE_ARPA_INET_H\n#  include <arpa/inet.h>\n#endif /* HAVE_ARPA_INET_H */\n\n#ifdef HAVE_NETINET_IN_H\n#  include <netinet/in.h>\n#endif /* HAVE_NETINET_IN_H */\n\n#ifdef HAVE_WINSOCK2_H\n#  include <winsock2.h>\n#endif /* HAVE_WINSOCK2_H */\n\n#endif /* SPDYLAY_NET_H */\n"
  },
  {
    "path": "lib/spdylay_npn.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_npn.h\"\n\n#include <string.h>\n\nstatic const spdylay_npn_proto proto_list[] = {\n  { (const unsigned char*)\"spdy/3.1\", 8, SPDYLAY_PROTO_SPDY3_1 },\n  { (const unsigned char*)\"spdy/3\", 6, SPDYLAY_PROTO_SPDY3 },\n  { (const unsigned char*)\"spdy/2\", 6, SPDYLAY_PROTO_SPDY2 }\n};\n\nconst spdylay_npn_proto* spdylay_npn_get_proto_list(size_t *len_ptr)\n{\n  *len_ptr = sizeof(proto_list)/sizeof(spdylay_npn_proto);\n  return proto_list;\n}\n\nint spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen,\n                                 const unsigned char *in, unsigned int inlen)\n{\n  const unsigned int SPDYLAY_SPDY_NOT_SELECTED = 99;\n  int http_selected = 0;\n  unsigned int selected_spdy_proto_pri = SPDYLAY_SPDY_NOT_SELECTED;\n  unsigned int i = 0;\n  for(; i < inlen; i += in[i]+1) {\n    unsigned int j;\n    for(j = 0; j < sizeof(proto_list)/sizeof(spdylay_npn_proto); ++j) {\n      if(in[i] == proto_list[j].len &&\n         i + 1 + in[i] <= inlen &&\n         memcmp(&in[i+1], proto_list[j].proto, in[i]) == 0) {\n\n        if(selected_spdy_proto_pri > j) {\n          *out = (unsigned char*)&in[i+1];\n          *outlen = in[i];\n          selected_spdy_proto_pri = j;\n        }\n      }\n    }\n    if(selected_spdy_proto_pri == SPDYLAY_SPDY_NOT_SELECTED &&\n       in[i] == 8 && i + 1 + in[i] <= inlen &&\n       memcmp(&in[i+1], \"http/1.1\", in[i]) == 0) {\n      http_selected = 1;\n      *out = (unsigned char*)&in[i+1];\n      *outlen = in[i];\n      /* Go through to the next iteration, because \"spdy/X\" may be\n         there */\n    }\n  }\n\n  if(selected_spdy_proto_pri == SPDYLAY_SPDY_NOT_SELECTED) {\n    if(http_selected) {\n      return 0;\n    }\n\n    return -1;\n  }\n\n  return proto_list[selected_spdy_proto_pri].version;\n}\n\nuint16_t spdylay_npn_get_version(const unsigned char *proto, size_t protolen)\n{\n  if(proto == NULL) {\n    return 0;\n  } else {\n    if(protolen == 8) {\n      if(memcmp(\"spdy/3.1\", proto, 8) == 0) {\n        return SPDYLAY_PROTO_SPDY3_1;\n      }\n    } else if(protolen == 6) {\n      if(memcmp(\"spdy/3\", proto, 6) == 0) {\n        return SPDYLAY_PROTO_SPDY3;\n      } else if(memcmp(\"spdy/2\", proto, 6) == 0) {\n        return SPDYLAY_PROTO_SPDY2;\n      }\n    }\n    return 0;\n  }\n}\n"
  },
  {
    "path": "lib/spdylay_npn.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_NPN_H\n#define SPDYLAY_NPN_H\n\n#ifdef HAVE_CONFIG\n# include <config.h>\n#endif /* HAVE_CONFIG */\n\n#include <spdylay/spdylay.h>\n\n#endif /* SPDYLAY_NPN_H */\n"
  },
  {
    "path": "lib/spdylay_outbound_item.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_outbound_item.h\"\n\n#include <assert.h>\n\nvoid spdylay_outbound_item_free(spdylay_outbound_item *item)\n{\n  if(item == NULL) {\n    return;\n  }\n  if(item->frame_cat == SPDYLAY_CTRL) {\n    spdylay_frame_type frame_type;\n    spdylay_frame *frame;\n    frame_type = spdylay_outbound_item_get_ctrl_frame_type(item);\n    frame = spdylay_outbound_item_get_ctrl_frame(item);\n    switch(frame_type) {\n    case SPDYLAY_SYN_STREAM:\n      spdylay_frame_syn_stream_free(&frame->syn_stream);\n      free(((spdylay_syn_stream_aux_data*)item->aux_data)->data_prd);\n      break;\n    case SPDYLAY_SYN_REPLY:\n      spdylay_frame_syn_reply_free(&frame->syn_reply);\n      break;\n    case SPDYLAY_RST_STREAM:\n      spdylay_frame_rst_stream_free(&frame->rst_stream);\n      break;\n    case SPDYLAY_SETTINGS:\n      spdylay_frame_settings_free(&frame->settings);\n      break;\n    case SPDYLAY_NOOP:\n      /* We don't have any public API to add NOOP, so here is\n         unreachable. */\n      assert(0);\n    case SPDYLAY_PING:\n      spdylay_frame_ping_free(&frame->ping);\n      break;\n    case SPDYLAY_GOAWAY:\n      spdylay_frame_goaway_free(&frame->goaway);\n      break;\n    case SPDYLAY_HEADERS:\n      spdylay_frame_headers_free(&frame->headers);\n      break;\n    case SPDYLAY_WINDOW_UPDATE:\n      spdylay_frame_window_update_free(&frame->window_update);\n      break;\n    case SPDYLAY_CREDENTIAL:\n      assert(0);\n      break;\n    }\n  } else if(item->frame_cat == SPDYLAY_DATA) {\n    spdylay_data *data_frame;\n    data_frame = spdylay_outbound_item_get_data_frame(item);\n    spdylay_frame_data_free(data_frame);\n  } else {\n    /* Unreachable */\n    assert(0);\n  }\n  free(item->frame);\n  free(item->aux_data);\n}\n"
  },
  {
    "path": "lib/spdylay_outbound_item.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_OUTBOUND_ITEM_H\n#define SPDYLAY_OUTBOUND_ITEM_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_frame.h\"\n\n/* Priority for PING */\n#define SPDYLAY_OB_PRI_PING -10\n\ntypedef struct {\n  spdylay_data_provider *data_prd;\n  void *stream_user_data;\n} spdylay_syn_stream_aux_data;\n\ntypedef struct {\n  void *frame;\n  void *aux_data;\n  int64_t seq;\n  /* Type of |frame|. SPDYLAY_CTRL: spdylay_frame*, SPDYLAY_DATA:\n     spdylay_data* */\n  spdylay_frame_category frame_cat;\n  int pri;\n} spdylay_outbound_item;\n\n/*\n * Deallocates resource for |item|. If |item| is NULL, this function\n * does nothing.\n */\nvoid spdylay_outbound_item_free(spdylay_outbound_item *item);\n\n/* Macros to cast spdylay_outbound_item.frame to the proper type. */\n#define spdylay_outbound_item_get_ctrl_frame(ITEM) ((spdylay_frame*)ITEM->frame)\n#define spdylay_outbound_item_get_ctrl_frame_type(ITEM) \\\n  (((spdylay_frame*)ITEM->frame)->ctrl.hd.type)\n#define spdylay_outbound_item_get_data_frame(ITEM) ((spdylay_data*)ITEM->frame)\n\n#endif /* SPDYLAY_OUTBOUND_ITEM_H */\n"
  },
  {
    "path": "lib/spdylay_pq.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_pq.h\"\n\nint spdylay_pq_init(spdylay_pq *pq, spdylay_compar compar)\n{\n  pq->capacity = 128;\n  pq->q = malloc(pq->capacity * sizeof(void*));\n  if(pq->q == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  pq->length = 0;\n  pq->compar = compar;\n  return 0;\n}\n\nvoid spdylay_pq_free(spdylay_pq *pq)\n{\n  free(pq->q);\n  pq->q = NULL;\n}\n\nstatic void swap(spdylay_pq *pq, size_t i, size_t j)\n{\n  void *t = pq->q[i];\n  pq->q[i] = pq->q[j];\n  pq->q[j] = t;\n}\n\nstatic void bubble_up(spdylay_pq *pq, size_t index)\n{\n  if(index == 0) {\n    return;\n  } else {\n    size_t parent = (index-1)/2;\n    if(pq->compar(pq->q[parent], pq->q[index]) > 0) {\n      swap(pq, parent, index);\n      bubble_up(pq, parent);\n    }\n  }\n}\n\nint spdylay_pq_push(spdylay_pq *pq, void *item)\n{\n  if(pq->capacity <= pq->length) {\n    void *nq;\n    nq = realloc(pq->q, (pq->capacity*2) * sizeof(void*));\n    if(nq == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    }\n    pq->capacity *= 2;\n    pq->q = nq;\n  }\n  pq->q[pq->length] = item;\n  ++pq->length;\n  bubble_up(pq, pq->length-1);\n  return 0;\n}\n\nvoid* spdylay_pq_top(spdylay_pq *pq)\n{\n  if(pq->length == 0) {\n    return NULL;\n  } else {\n    return pq->q[0];\n  }\n}\n\nstatic void bubble_down(spdylay_pq *pq, size_t index)\n{\n  size_t lchild = index*2+1;\n  size_t minindex = index;\n  size_t i, j;\n  for(i = 0; i < 2; ++i) {\n    j = lchild+i;\n    if(j >= pq->length) {\n      break;\n    }\n    if(pq->compar(pq->q[minindex], pq->q[j]) > 0) {\n      minindex = j;\n    }\n  }\n  if(minindex != index) {\n    swap(pq, index, minindex);\n    bubble_down(pq, minindex);\n  }\n}\n\nvoid spdylay_pq_pop(spdylay_pq *pq)\n{\n  if(pq->length > 0) {\n    pq->q[0] = pq->q[pq->length-1];\n    --pq->length;\n    bubble_down(pq, 0);\n  }\n}\n\nint spdylay_pq_empty(spdylay_pq *pq)\n{\n  return pq->length == 0;\n}\n\nsize_t spdylay_pq_size(spdylay_pq *pq)\n{\n  return pq->length;\n}\n"
  },
  {
    "path": "lib/spdylay_pq.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_PQ_H\n#define SPDYLAY_PQ_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_int.h\"\n\n/* Implementation of priority queue */\n\ntypedef struct {\n  /* The pointer to the pointer to the item stored */\n  void **q;\n  /* The number of items sotred */\n  size_t length;\n  /* The maximum number of items this pq can store. This is\n     automatically extended when length is reached to this value. */\n  size_t capacity;\n  /* The compare function between items */\n  spdylay_compar compar;\n} spdylay_pq;\n\n/*\n * Initializes priority queue |pq| with compare function |cmp|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_pq_init(spdylay_pq *pq, spdylay_compar cmp);\n\n/*\n * Deallocates any resources allocated for |pq|.  The stored items are\n * not freed by this function.\n */\nvoid spdylay_pq_free(spdylay_pq *pq);\n\n/*\n * Adds |item| to the priority queue |pq|.\n *\n * This function returns 0 if it succeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_pq_push(spdylay_pq *pq, void *item);\n\n/*\n * Returns item at the top of the queue |pq|. If the queue is empty,\n * this function returns NULL.\n */\nvoid* spdylay_pq_top(spdylay_pq *pq);\n\n/*\n * Pops item at the top of the queue |pq|. The popped item is not\n * freed by this function.\n */\nvoid spdylay_pq_pop(spdylay_pq *pq);\n\n/*\n * Returns nonzero if the queue |pq| is empty.\n */\nint spdylay_pq_empty(spdylay_pq *pq);\n\n/*\n * Returns the number of items in the queue |pq|.\n */\nsize_t spdylay_pq_size(spdylay_pq *pq);\n\n#endif /* SPDYLAY_PQ_H */\n"
  },
  {
    "path": "lib/spdylay_queue.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_queue.h\"\n\n#include <string.h>\n#include <assert.h>\n\nvoid spdylay_queue_init(spdylay_queue *queue)\n{\n  queue->front = queue->back = NULL;\n}\n\nvoid spdylay_queue_free(spdylay_queue *queue)\n{\n  if(!queue) {\n    return;\n  } else {\n    spdylay_queue_cell *p = queue->front;\n    while(p) {\n      spdylay_queue_cell *next = p->next;\n      free(p);\n      p = next;\n    }\n  }\n}\n\nint spdylay_queue_push(spdylay_queue *queue, void *data)\n{\n  spdylay_queue_cell *new_cell = (spdylay_queue_cell*)malloc\n    (sizeof(spdylay_queue_cell));\n  if(!new_cell) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  new_cell->data = data;\n  new_cell->next = NULL;\n  if(queue->back) {\n    queue->back->next = new_cell;\n    queue->back = new_cell;\n\n  } else {\n    queue->front = queue->back = new_cell;\n  }\n  return 0;\n}\n\nvoid spdylay_queue_pop(spdylay_queue *queue)\n{\n  spdylay_queue_cell *front = queue->front;\n  assert(front);\n  queue->front = front->next;\n  if(front == queue->back) {\n    queue->back = NULL;\n  }\n  free(front);\n}\n\nvoid* spdylay_queue_front(spdylay_queue *queue)\n{\n  assert(queue->front);\n  return queue->front->data;\n}\n\nvoid* spdylay_queue_back(spdylay_queue *queue)\n{\n  assert(queue->back);\n  return queue->back->data;\n}\n\nint spdylay_queue_empty(spdylay_queue *queue)\n{\n  return queue->front == NULL;\n}\n"
  },
  {
    "path": "lib/spdylay_queue.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_QUEUE_H\n#define SPDYLAY_QUEUE_H\n\n#ifdef HAVE_CONFIG_H\n#  include \"config.h\"\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n\ntypedef struct spdylay_queue_cell {\n  void *data;\n  struct spdylay_queue_cell *next;\n} spdylay_queue_cell;\n\ntypedef struct {\n  spdylay_queue_cell *front, *back;\n} spdylay_queue;\n\nvoid spdylay_queue_init(spdylay_queue *queue);\nvoid spdylay_queue_free(spdylay_queue *queue);\nint spdylay_queue_push(spdylay_queue *queue, void *data);\nvoid spdylay_queue_pop(spdylay_queue *queue);\nvoid* spdylay_queue_front(spdylay_queue *queue);\nvoid* spdylay_queue_back(spdylay_queue *queue);\nint spdylay_queue_empty(spdylay_queue *queue);\n\n#endif /* SPDYLAY_QUEUE_H */\n"
  },
  {
    "path": "lib/spdylay_session.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_session.h\"\n\n#include <string.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <assert.h>\n\n#include \"spdylay_helper.h\"\n#include \"spdylay_net.h\"\n\n/*\n * Returns non-zero if the number of outgoing opened streams is larger\n * than or equal to\n * remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS].\n */\nstatic int spdylay_session_is_outgoing_concurrent_streams_max\n(spdylay_session *session)\n{\n  return session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]\n    <= session->num_outgoing_streams;\n}\n\n/*\n * Returns non-zero if the number of incoming opened streams is larger\n * than or equal to\n * local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS].\n */\nstatic int spdylay_session_is_incoming_concurrent_streams_max\n(spdylay_session *session)\n{\n  return session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]\n    <= session->num_incoming_streams;\n}\n\n/*\n * Returns non-zero if |error| is non-fatal error.\n */\nstatic int spdylay_is_non_fatal(int error)\n{\n  return error < 0 && error > SPDYLAY_ERR_FATAL;\n}\n\n/*\n * Returns non-zero if |error| is fatal error.\n */\nstatic int spdylay_is_fatal(int error)\n{\n  return error < SPDYLAY_ERR_FATAL;\n}\n\nint spdylay_session_fail_session(spdylay_session *session,\n                                 uint32_t status_code)\n{\n  session->goaway_flags |= SPDYLAY_GOAWAY_FAIL_ON_SEND;\n  return spdylay_submit_goaway(session, status_code);\n}\n\nint spdylay_session_is_my_stream_id(spdylay_session *session,\n                                    int32_t stream_id)\n{\n  int r;\n  if(stream_id == 0) {\n    return 0;\n  }\n  r = stream_id % 2;\n  return (session->server && r == 0) || (!session->server && r == 1);\n}\n\nspdylay_stream* spdylay_session_get_stream(spdylay_session *session,\n                                           int32_t stream_id)\n{\n  return (spdylay_stream*)spdylay_map_find(&session->streams, stream_id);\n}\n\nstatic int spdylay_outbound_item_compar(const void *lhsx, const void *rhsx)\n{\n  const spdylay_outbound_item *lhs, *rhs;\n  lhs = (const spdylay_outbound_item*)lhsx;\n  rhs = (const spdylay_outbound_item*)rhsx;\n  if(lhs->pri == rhs->pri) {\n    return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0);\n  } else {\n    return lhs->pri-rhs->pri;\n  }\n}\n\nstatic void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe)\n{\n  iframe->state = SPDYLAY_RECV_HEAD;\n  iframe->payloadlen = iframe->buflen = iframe->off = 0;\n  iframe->headbufoff = 0;\n  spdylay_buffer_reset(&iframe->inflatebuf);\n  iframe->error_code = 0;\n}\n\n/*\n * Returns the number of bytes before name/value header block for the\n * incoming frame. If the incoming frame does not have name/value\n * block, this function returns -1.\n */\nstatic size_t spdylay_inbound_frame_payload_nv_offset\n(spdylay_inbound_frame *iframe)\n{\n  uint16_t type, version;\n  ssize_t offset;\n  type = spdylay_get_uint16(&iframe->headbuf[2]);\n  version = spdylay_get_uint16(&iframe->headbuf[0]) & SPDYLAY_VERSION_MASK;\n  offset = spdylay_frame_nv_offset(type, version);\n  if(offset != -1) {\n    offset -= SPDYLAY_HEAD_LEN;\n  }\n  return offset;\n}\n\nstatic int spdylay_session_new(spdylay_session **session_ptr,\n                               uint16_t version,\n                               const spdylay_session_callbacks *callbacks,\n                               void *user_data,\n                               int hd_comp)\n{\n  int r;\n  if(version != SPDYLAY_PROTO_SPDY2 && version != SPDYLAY_PROTO_SPDY3 &&\n     version != SPDYLAY_PROTO_SPDY3_1) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  *session_ptr = malloc(sizeof(spdylay_session));\n  if(*session_ptr == NULL) {\n    r = SPDYLAY_ERR_NOMEM;\n    goto fail_session;\n  }\n  memset(*session_ptr, 0, sizeof(spdylay_session));\n\n  /* next_stream_id, last_recv_stream_id and next_unique_id are\n     initialized in either spdylay_session_client_new or\n     spdylay_session_server_new */\n\n  switch(version) {\n  case SPDYLAY_PROTO_SPDY2:\n    (*session_ptr)->version = version;\n    (*session_ptr)->flow_control = SPDYLAY_FLOW_CONTROL_NONE;\n    break;\n  case SPDYLAY_PROTO_SPDY3:\n    (*session_ptr)->version = version;\n    (*session_ptr)->flow_control = SPDYLAY_FLOW_CONTROL_STREAM;\n    break;\n  case SPDYLAY_PROTO_SPDY3_1:\n    (*session_ptr)->version = SPDYLAY_PROTO_SPDY3;\n    (*session_ptr)->flow_control = SPDYLAY_FLOW_CONTROL_STREAM |\n      SPDYLAY_FLOW_CONTROL_CONNECTION;\n    (*session_ptr)->window_size = SPDYLAY_INITIAL_WINDOW_SIZE;\n    break;\n  }\n\n  (*session_ptr)->last_ping_unique_id = 0;\n\n  (*session_ptr)->next_seq = 0;\n\n  (*session_ptr)->goaway_flags = SPDYLAY_GOAWAY_NONE;\n  (*session_ptr)->last_good_stream_id = 0;\n\n  (*session_ptr)->max_recv_ctrl_frame_buf = (1 << 24)-1;\n\n  r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater,\n                                   hd_comp,\n                                   (*session_ptr)->version);\n  if(r != 0) {\n    goto fail_hd_deflater;\n  }\n  r = spdylay_zlib_inflate_hd_init(&(*session_ptr)->hd_inflater,\n                                   (*session_ptr)->version);\n  if(r != 0) {\n    goto fail_hd_inflater;\n  }\n  r = spdylay_map_init(&(*session_ptr)->streams);\n  if(r != 0) {\n    goto fail_streams;\n  }\n  r = spdylay_pq_init(&(*session_ptr)->ob_pq, spdylay_outbound_item_compar);\n  if(r != 0) {\n    goto fail_ob_pq;\n  }\n  r = spdylay_pq_init(&(*session_ptr)->ob_ss_pq, spdylay_outbound_item_compar);\n  if(r != 0) {\n    goto fail_ob_ss_pq;\n  }\n\n  (*session_ptr)->aob.framebuf = malloc\n    (SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH);\n  if((*session_ptr)->aob.framebuf == NULL) {\n    r = SPDYLAY_ERR_NOMEM;\n    goto fail_aob_framebuf;\n  }\n  (*session_ptr)->aob.framebufmax = SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH;\n\n  (*session_ptr)->nvbuf = malloc(SPDYLAY_INITIAL_NV_BUFFER_LENGTH);\n  if((*session_ptr)->nvbuf == NULL) {\n    r = SPDYLAY_ERR_NOMEM;\n    goto fail_nvbuf;\n  }\n  (*session_ptr)->nvbuflen = SPDYLAY_INITIAL_NV_BUFFER_LENGTH;\n\n  memset((*session_ptr)->remote_settings, 0,\n         sizeof((*session_ptr)->remote_settings));\n  (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =\n    SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS;\n  (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] =\n    SPDYLAY_INITIAL_WINDOW_SIZE;\n\n  memset((*session_ptr)->local_settings, 0,\n         sizeof((*session_ptr)->local_settings));\n  (*session_ptr)->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =\n    SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS;\n  (*session_ptr)->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] =\n    SPDYLAY_INITIAL_WINDOW_SIZE;\n\n  (*session_ptr)->callbacks = *callbacks;\n  (*session_ptr)->user_data = user_data;\n\n  (*session_ptr)->iframe.buf = malloc(SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH);\n  if((*session_ptr)->iframe.buf == NULL) {\n    r = SPDYLAY_ERR_NOMEM;\n    goto fail_iframe_buf;\n  }\n  (*session_ptr)->iframe.bufmax = SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH;\n  spdylay_buffer_init(&(*session_ptr)->iframe.inflatebuf, 4096);\n\n  spdylay_inbound_frame_reset(&(*session_ptr)->iframe);\n\n  return 0;\n\n fail_iframe_buf:\n  free((*session_ptr)->nvbuf);\n fail_nvbuf:\n  free((*session_ptr)->aob.framebuf);\n fail_aob_framebuf:\n  spdylay_pq_free(&(*session_ptr)->ob_ss_pq);\n fail_ob_ss_pq:\n  spdylay_pq_free(&(*session_ptr)->ob_pq);\n fail_ob_pq:\n  spdylay_map_free(&(*session_ptr)->streams);\n fail_streams:\n  spdylay_zlib_inflate_free(&(*session_ptr)->hd_inflater);\n fail_hd_inflater:\n  spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater);\n fail_hd_deflater:\n  free(*session_ptr);\n fail_session:\n  return r;\n}\n\nint spdylay_session_client_new(spdylay_session **session_ptr,\n                               uint16_t version,\n                               const spdylay_session_callbacks *callbacks,\n                               void *user_data)\n{\n  int r;\n  /* For client side session, header compression is disabled. */\n  r = spdylay_session_new(session_ptr, version, callbacks, user_data, 0);\n  if(r == 0) {\n    /* IDs for use in client */\n    (*session_ptr)->next_stream_id = 1;\n    (*session_ptr)->last_recv_stream_id = 0;\n    (*session_ptr)->next_unique_id = 1;\n  }\n  return r;\n}\n\nint spdylay_session_server_new(spdylay_session **session_ptr,\n                               uint16_t version,\n                               const spdylay_session_callbacks *callbacks,\n                               void *user_data)\n{\n  int r;\n  /* Enable header compression on server side. */\n  r = spdylay_session_new(session_ptr, version, callbacks, user_data,\n                          1 /* hd_comp */);\n  if(r == 0) {\n    (*session_ptr)->server = 1;\n    /* IDs for use in client */\n    (*session_ptr)->next_stream_id = 2;\n    (*session_ptr)->last_recv_stream_id = 0;\n    (*session_ptr)->next_unique_id = 2;\n  }\n  return r;\n}\n\nstatic int spdylay_free_streams(spdylay_map_entry *entry, void *ptr _U_)\n{\n  spdylay_stream_free((spdylay_stream*)entry);\n  free(entry);\n  return 0;\n}\n\nstatic void spdylay_session_ob_pq_free(spdylay_pq *pq)\n{\n  while(!spdylay_pq_empty(pq)) {\n    spdylay_outbound_item *item = (spdylay_outbound_item*)spdylay_pq_top(pq);\n    spdylay_outbound_item_free(item);\n    free(item);\n    spdylay_pq_pop(pq);\n  }\n  spdylay_pq_free(pq);\n}\n\nstatic void spdylay_active_outbound_item_reset\n(spdylay_active_outbound_item *aob)\n{\n  spdylay_outbound_item_free(aob->item);\n  free(aob->item);\n  aob->item = NULL;\n  aob->framebuflen = aob->framebufoff = 0;\n}\n\nvoid spdylay_session_del(spdylay_session *session)\n{\n  if(session == NULL) {\n    return;\n  }\n  spdylay_map_each_free(&session->streams, spdylay_free_streams, NULL);\n  spdylay_map_free(&session->streams);\n  spdylay_session_ob_pq_free(&session->ob_pq);\n  spdylay_session_ob_pq_free(&session->ob_ss_pq);\n  spdylay_zlib_deflate_free(&session->hd_deflater);\n  spdylay_zlib_inflate_free(&session->hd_inflater);\n  spdylay_active_outbound_item_reset(&session->aob);\n  free(session->aob.framebuf);\n  free(session->nvbuf);\n  spdylay_buffer_free(&session->iframe.inflatebuf);\n  free(session->iframe.buf);\n  free(session);\n}\n\nint spdylay_session_add_frame(spdylay_session *session,\n                              spdylay_frame_category frame_cat,\n                              void *abs_frame,\n                              void *aux_data)\n{\n  int r = 0;\n  spdylay_outbound_item *item;\n  item = malloc(sizeof(spdylay_outbound_item));\n  if(item == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  item->frame_cat = frame_cat;\n  item->frame = abs_frame;\n  item->aux_data = aux_data;\n  item->seq = session->next_seq++;\n  /* Set priority lowest at the moment. */\n  item->pri = spdylay_session_get_pri_lowest(session);\n  if(frame_cat == SPDYLAY_CTRL) {\n    spdylay_frame *frame = (spdylay_frame*)abs_frame;\n    spdylay_frame_type frame_type = frame->ctrl.hd.type;\n    switch(frame_type) {\n    case SPDYLAY_SYN_STREAM:\n      /* we have already allocated stream ID serially, so we have to\n         send SYN_STREAM in the order they added.  Use same priority\n         here. */\n      break;\n    case SPDYLAY_SYN_REPLY: {\n      spdylay_stream *stream = spdylay_session_get_stream\n        (session, frame->syn_reply.stream_id);\n      if(stream) {\n        item->pri = stream->pri;\n      }\n      break;\n    }\n    case SPDYLAY_RST_STREAM: {\n      spdylay_stream *stream = spdylay_session_get_stream\n        (session, frame->rst_stream.stream_id);\n      if(stream) {\n        stream->state = SPDYLAY_STREAM_CLOSING;\n        item->pri = stream->pri;\n      }\n      break;\n    }\n    case SPDYLAY_SETTINGS:\n      /* Should SPDYLAY_SETTINGS have higher priority? */\n      item->pri = -1;\n      break;\n    case SPDYLAY_NOOP:\n      /* We don't have any public API to add NOOP, so here is\n         unreachable. */\n      assert(0);\n    case SPDYLAY_PING:\n      /* Ping has highest priority. */\n      item->pri = SPDYLAY_OB_PRI_PING;\n      break;\n    case SPDYLAY_GOAWAY:\n      /* Should GOAWAY have higher priority? */\n      break;\n    case SPDYLAY_HEADERS: {\n      spdylay_stream *stream = spdylay_session_get_stream\n        (session, frame->headers.stream_id);\n      if(stream) {\n        item->pri = stream->pri;\n      }\n      break;\n    }\n    case SPDYLAY_WINDOW_UPDATE: {\n      spdylay_stream *stream = spdylay_session_get_stream\n        (session, frame->window_update.stream_id);\n      if(stream) {\n        item->pri = stream->pri;\n      }\n      break;\n    }\n    case SPDYLAY_CREDENTIAL:\n      /* CREDENTIAL was removed */\n      assert(0);\n      break;\n    }\n    if(frame_type == SPDYLAY_SYN_STREAM) {\n      r = spdylay_pq_push(&session->ob_ss_pq, item);\n    } else {\n      r = spdylay_pq_push(&session->ob_pq, item);\n    }\n  } else if(frame_cat == SPDYLAY_DATA) {\n    spdylay_data *data_frame = (spdylay_data*)abs_frame;\n    spdylay_stream *stream = spdylay_session_get_stream(session,\n                                                        data_frame->stream_id);\n    if(stream) {\n      item->pri = stream->pri;\n    }\n    r = spdylay_pq_push(&session->ob_pq, item);\n  } else {\n    /* Unreachable */\n    assert(0);\n  }\n  if(r != 0) {\n    free(item);\n    return r;\n  }\n  return 0;\n}\n\nint spdylay_session_add_rst_stream(spdylay_session *session,\n                                   int32_t stream_id, uint32_t status_code)\n{\n  int r;\n  spdylay_frame *frame;\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_rst_stream_init(&frame->rst_stream, session->version,\n                                stream_id, status_code);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_rst_stream_free(&frame->rst_stream);\n    free(frame);\n    return r;\n  }\n  return 0;\n}\n\nspdylay_stream* spdylay_session_open_stream(spdylay_session *session,\n                                            int32_t stream_id,\n                                            uint8_t flags, uint8_t pri,\n                                            spdylay_stream_state initial_state,\n                                            void *stream_user_data)\n{\n  int r;\n  spdylay_stream *stream = malloc(sizeof(spdylay_stream));\n  if(stream == NULL) {\n    return NULL;\n  }\n  spdylay_stream_init(stream, stream_id, flags, pri, initial_state,\n                      session->remote_settings\n                      [SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE],\n                      stream_user_data);\n  r = spdylay_map_insert(&session->streams, &stream->map_entry);\n  if(r != 0) {\n    free(stream);\n    stream = NULL;\n  }\n  if(spdylay_session_is_my_stream_id(session, stream_id)) {\n    ++session->num_outgoing_streams;\n  } else {\n    ++session->num_incoming_streams;\n  }\n  return stream;\n}\n\nint spdylay_session_close_stream(spdylay_session *session, int32_t stream_id,\n                                 spdylay_status_code status_code)\n{\n  spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    /* We call on_stream_close_callback even if stream->state is\n       SPDYLAY_STREAM_INITIAL. This will happen while sending request\n       HEADERS, a local endpoint receives RST_STREAM for that\n       stream. It may be PROTOCOL_ERROR, but without notifying stream\n       closure will hang the stream in a local endpoint.\n     */\n    if(session->callbacks.on_stream_close_callback) {\n      session->callbacks.on_stream_close_callback(session, stream_id,\n                                                  status_code,\n                                                  session->user_data);\n    }\n    if(spdylay_session_is_my_stream_id(session, stream_id)) {\n      --session->num_outgoing_streams;\n    } else {\n      --session->num_incoming_streams;\n    }\n    spdylay_map_remove(&session->streams, stream_id);\n    spdylay_stream_free(stream);\n    free(stream);\n    return 0;\n  } else {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n}\n\nint spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session,\n                                              spdylay_stream *stream)\n{\n  if((stream->shut_flags & SPDYLAY_SHUT_RDWR) == SPDYLAY_SHUT_RDWR) {\n    return spdylay_session_close_stream(session, stream->stream_id,\n                                        SPDYLAY_OK);\n  } else {\n    return 0;\n  }\n}\n\nvoid spdylay_session_close_pushed_streams(spdylay_session *session,\n                                          int32_t stream_id,\n                                          spdylay_status_code status_code)\n{\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    size_t i;\n    for(i = 0; i < stream->pushed_streams_length; ++i) {\n      spdylay_session_close_stream(session, stream->pushed_streams[i],\n                                   status_code);\n    }\n  }\n}\n\nstatic int spdylay_predicate_stream_for_send(spdylay_stream *stream)\n{\n  if(stream == NULL) {\n    return SPDYLAY_ERR_STREAM_CLOSED;\n  } else if(stream->shut_flags & SPDYLAY_SHUT_WR) {\n    return SPDYLAY_ERR_STREAM_SHUT_WR;\n  } else {\n    return 0;\n  }\n}\n\n/*\n * This function checks SYN_STREAM frame |frame| can be sent at this\n * time.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_STREAM_CLOSED\n *     The Associated-To-Stream is already closed or does not exist.\n * SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE\n *     Stream ID has reached the maximum value. Therefore no stream ID\n *     is available.\n */\nstatic int spdylay_session_predicate_syn_stream_send\n(spdylay_session *session,\n spdylay_syn_stream *frame)\n{\n  if(session->goaway_flags) {\n    /* When GOAWAY is sent or received, peer must not send new\n       SYN_STREAM. */\n    return SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED;\n  }\n  if(frame->assoc_stream_id != 0) {\n    /* Check associated stream is active. */\n    /* We assume here that if frame->assoc_stream_id != 0,\n       session->server is always 1. */\n    if(spdylay_session_get_stream(session, frame->assoc_stream_id) ==\n       NULL) {\n      return SPDYLAY_ERR_STREAM_CLOSED;\n    }\n  }\n  return 0;\n}\n\n/*\n * This function checks SYN_REPLY with the stream ID |stream_id| can\n * be sent at this time.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_STREAM_CLOSED\n *     The stream is already closed or does not exist.\n * SPDYLAY_ERR_STREAM_SHUT_WR\n *     The transmission is not allowed for this stream (e.g., a frame\n *     with FIN flag set has already sent)\n * SPDYLAY_ERR_INVALID_STREAM_ID\n *     The stream ID is invalid.\n * SPDYLAY_ERR_STREAM_CLOSING\n *     RST_STREAM was queued for this stream.\n * SPDYLAY_ERR_INVALID_STREAM_STATE\n *     The state of the stream is not valid (e.g., SYN_REPLY has\n *     already sent).\n */\nstatic int spdylay_session_predicate_syn_reply_send(spdylay_session *session,\n                                                    int32_t stream_id)\n{\n  spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);\n  int r;\n  r = spdylay_predicate_stream_for_send(stream);\n  if(r != 0) {\n    return r;\n  }\n  if(spdylay_session_is_my_stream_id(session, stream_id)) {\n    return SPDYLAY_ERR_INVALID_STREAM_ID;\n  } else {\n    if(stream->state == SPDYLAY_STREAM_OPENING) {\n      return 0;\n    } else if(stream->state == SPDYLAY_STREAM_CLOSING) {\n      return SPDYLAY_ERR_STREAM_CLOSING;\n    } else {\n      return SPDYLAY_ERR_INVALID_STREAM_STATE;\n    }\n  }\n}\n\n/*\n * This function checks HEADERS with the stream ID |stream_id| can\n * be sent at this time.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_STREAM_CLOSED\n *     The stream is already closed or does not exist.\n * SPDYLAY_ERR_STREAM_SHUT_WR\n *     The transmission is not allowed for this stream (e.g., a frame\n *     with FIN flag set has already sent)\n * SPDYLAY_ERR_STREAM_CLOSING\n *     RST_STREAM was queued for this stream.\n * SPDYLAY_ERR_INVALID_STREAM_STATE\n *     The state of the stream is not valid (e.g., if the local peer\n *     is receiving side and SYN_REPLY has not been sent).\n */\nstatic int spdylay_session_predicate_headers_send(spdylay_session *session,\n                                                  int32_t stream_id)\n{\n  spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);\n  int r;\n  r = spdylay_predicate_stream_for_send(stream);\n  if(r != 0) {\n    return r;\n  }\n  if(spdylay_session_is_my_stream_id(session, stream_id)) {\n    if(stream->state != SPDYLAY_STREAM_CLOSING) {\n      return 0;\n    } else {\n      return SPDYLAY_ERR_STREAM_CLOSING;\n    }\n  } else {\n    if(stream->state == SPDYLAY_STREAM_OPENED) {\n      return 0;\n    } else if(stream->state == SPDYLAY_STREAM_CLOSING) {\n      return SPDYLAY_ERR_STREAM_CLOSING;\n    } else {\n      return SPDYLAY_ERR_INVALID_STREAM_STATE;\n    }\n  }\n}\n\n/*\n * This function checks WINDOW_UPDATE with the stream ID |stream_id|\n * can be sent at this time. Note that FIN flag of the previous frame\n * does not affect the transmission of the WINDOW_UPDATE frame.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_STREAM_CLOSED\n *     The stream is already closed or does not exist.\n * SPDYLAY_ERR_STREAM_CLOSING\n *     RST_STREAM was queued for this stream.\n */\nstatic int spdylay_session_predicate_window_update_send\n(spdylay_session *session,\n int32_t stream_id)\n{\n  spdylay_stream *stream;\n  if((session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) &&\n     stream_id == 0) {\n    return 0;\n  }\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream == NULL) {\n    return SPDYLAY_ERR_STREAM_CLOSED;\n  }\n  if(stream->state != SPDYLAY_STREAM_CLOSING) {\n    return 0;\n  } else {\n    return SPDYLAY_ERR_STREAM_CLOSING;\n  }\n}\n\n/*\n * Returns the maximum length of next data read. If the flow control\n * is enabled, the return value takes into account the current window\n * size.\n */\nstatic size_t spdylay_session_next_data_read(spdylay_session *session,\n                                             spdylay_stream *stream)\n{\n  int32_t window_size = SPDYLAY_DATA_PAYLOAD_LENGTH;\n  if(session->flow_control == SPDYLAY_FLOW_CONTROL_NONE) {\n    return window_size;\n  }\n  window_size = spdylay_min(window_size, stream->window_size);\n  if(session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) {\n    window_size = spdylay_min(window_size, session->window_size);\n  }\n  if(window_size > 0) {\n    return window_size;\n  }\n  return 0;\n}\n\n/*\n * This function checks DATA with the stream ID |stream_id| can be\n * sent at this time.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_STREAM_CLOSED\n *     The stream is already closed or does not exist.\n * SPDYLAY_ERR_STREAM_SHUT_WR\n *     The transmission is not allowed for this stream (e.g., a frame\n *     with FIN flag set has already sent)\n * SPDYLAY_ERR_DEFERRED_DATA_EXIST\n *     Another DATA frame has already been deferred.\n * SPDYLAY_ERR_STREAM_CLOSING\n *     RST_STREAM was queued for this stream.\n * SPDYLAY_ERR_INVALID_STREAM_STATE\n *     The state of the stream is not valid (e.g., if the local peer\n *     is receiving side and SYN_REPLY has not been sent).\n */\nstatic int spdylay_session_predicate_data_send(spdylay_session *session,\n                                               int32_t stream_id)\n{\n  spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);\n  int r;\n  r = spdylay_predicate_stream_for_send(stream);\n  if(r != 0) {\n    return r;\n  }\n  if(stream->deferred_data != NULL) {\n    /* stream->deferred_data != NULL means previously queued DATA\n       frame has not been sent. We don't allow new DATA frame is sent\n       in this case. */\n    return SPDYLAY_ERR_DEFERRED_DATA_EXIST;\n  }\n  if(spdylay_session_is_my_stream_id(session, stream_id)) {\n    /* If stream->state is SPDYLAY_STREAM_CLOSING, RST_STREAM was\n       queued but not yet sent. In this case, we won't send DATA\n       frames. This is because in the current architecture, DATA and\n       RST_STREAM in the same stream have same priority and DATA is\n       small seq number. So RST_STREAM will not be sent until all DATA\n       frames are sent. This is not desirable situation; we want to\n       close stream as soon as possible. To achieve this, we remove\n       DATA frame before RST_STREAM. */\n    if(stream->state != SPDYLAY_STREAM_CLOSING) {\n      return 0;\n    } else {\n      return SPDYLAY_ERR_STREAM_CLOSING;\n    }\n  } else {\n    if(stream->state == SPDYLAY_STREAM_OPENED) {\n      return 0;\n    } else if(stream->state == SPDYLAY_STREAM_CLOSING) {\n      return SPDYLAY_ERR_STREAM_CLOSING;\n    } else {\n      return SPDYLAY_ERR_INVALID_STREAM_STATE;\n    }\n  }\n}\n\nstatic ssize_t spdylay_session_prep_frame(spdylay_session *session,\n                                          spdylay_outbound_item *item)\n{\n  ssize_t framebuflen = 0;\n  if(item->frame_cat == SPDYLAY_CTRL) {\n    spdylay_frame *frame;\n    spdylay_frame_type frame_type;\n    frame = spdylay_outbound_item_get_ctrl_frame(item);\n    frame_type = spdylay_outbound_item_get_ctrl_frame_type(item);\n    switch(frame_type) {\n    case SPDYLAY_SYN_STREAM: {\n      spdylay_syn_stream_aux_data *aux_data;\n      int r;\n      r = spdylay_session_predicate_syn_stream_send(session,\n                                                    &frame->syn_stream);\n      if(r != 0) {\n        aux_data = (spdylay_syn_stream_aux_data *)item->aux_data;\n        /* we need stream available for callbacks, so that application\n           can call spdylay_session_get_stream_user_data(). */\n        if(spdylay_session_open_stream(\n                session, frame->syn_stream.stream_id,\n                frame->syn_stream.hd.flags, frame->syn_stream.pri,\n                SPDYLAY_STREAM_INITIAL, aux_data->stream_user_data) == NULL) {\n          return SPDYLAY_ERR_NOMEM;\n        }\n\n        return r;\n      }\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_3to2(frame->syn_stream.nv);\n        spdylay_frame_nv_sort(frame->syn_stream.nv);\n      }\n      framebuflen = spdylay_frame_pack_syn_stream(&session->aob.framebuf,\n                                                  &session->aob.framebufmax,\n                                                  &session->nvbuf,\n                                                  &session->nvbuflen,\n                                                  &frame->syn_stream,\n                                                  &session->hd_deflater);\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame->syn_stream.nv);\n        spdylay_frame_nv_sort(frame->syn_stream.nv);\n      }\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      aux_data = (spdylay_syn_stream_aux_data*)item->aux_data;\n      if(spdylay_session_open_stream(\n              session, frame->syn_stream.stream_id, frame->syn_stream.hd.flags,\n              frame->syn_stream.pri, SPDYLAY_STREAM_INITIAL,\n              aux_data->stream_user_data) == NULL) {\n        return SPDYLAY_ERR_NOMEM;\n      }\n      break;\n    }\n    case SPDYLAY_SYN_REPLY: {\n      int r;\n      r = spdylay_session_predicate_syn_reply_send(session,\n                                                   frame->syn_reply.stream_id);\n      if(r != 0) {\n        return r;\n      }\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_3to2(frame->syn_reply.nv);\n        spdylay_frame_nv_sort(frame->syn_reply.nv);\n      }\n      framebuflen = spdylay_frame_pack_syn_reply(&session->aob.framebuf,\n                                                 &session->aob.framebufmax,\n                                                 &session->nvbuf,\n                                                 &session->nvbuflen,\n                                                 &frame->syn_reply,\n                                                 &session->hd_deflater);\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame->syn_reply.nv);\n        spdylay_frame_nv_sort(frame->syn_reply.nv);\n      }\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    }\n    case SPDYLAY_RST_STREAM:\n      framebuflen = spdylay_frame_pack_rst_stream(&session->aob.framebuf,\n                                                  &session->aob.framebufmax,\n                                                  &frame->rst_stream);\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    case SPDYLAY_SETTINGS:\n      framebuflen = spdylay_frame_pack_settings(&session->aob.framebuf,\n                                                &session->aob.framebufmax,\n                                                &frame->settings);\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    case SPDYLAY_NOOP:\n      /* We don't have any public API to add NOOP, so here is\n         unreachable. */\n      assert(0);\n    case SPDYLAY_PING:\n      framebuflen = spdylay_frame_pack_ping(&session->aob.framebuf,\n                                            &session->aob.framebufmax,\n                                            &frame->ping);\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    case SPDYLAY_HEADERS: {\n      int r;\n      r = spdylay_session_predicate_headers_send(session,\n                                                 frame->headers.stream_id);\n      if(r != 0) {\n        return r;\n      }\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_3to2(frame->headers.nv);\n        spdylay_frame_nv_sort(frame->headers.nv);\n      }\n      framebuflen = spdylay_frame_pack_headers(&session->aob.framebuf,\n                                               &session->aob.framebufmax,\n                                               &session->nvbuf,\n                                               &session->nvbuflen,\n                                               &frame->headers,\n                                               &session->hd_deflater);\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame->headers.nv);\n        spdylay_frame_nv_sort(frame->headers.nv);\n      }\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    }\n    case SPDYLAY_WINDOW_UPDATE: {\n      int r;\n      r = spdylay_session_predicate_window_update_send\n        (session, frame->window_update.stream_id);\n      if(r != 0) {\n        return r;\n      }\n      framebuflen = spdylay_frame_pack_window_update(&session->aob.framebuf,\n                                                     &session->aob.framebufmax,\n                                                     &frame->window_update);\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    }\n    case SPDYLAY_GOAWAY:\n      if(session->goaway_flags & SPDYLAY_GOAWAY_SEND) {\n        /* TODO The spec does not mandate that both endpoints have to\n           exchange GOAWAY. This implementation allows receiver of first\n           GOAWAY can sent its own GOAWAY to tell the remote peer that\n           last-good-stream-id. */\n        return SPDYLAY_ERR_GOAWAY_ALREADY_SENT;\n      }\n      framebuflen = spdylay_frame_pack_goaway(&session->aob.framebuf,\n                                              &session->aob.framebufmax,\n                                              &frame->goaway);\n      if(framebuflen < 0) {\n        return framebuflen;\n      }\n      break;\n    case SPDYLAY_CREDENTIAL:\n      assert(0);\n      break;\n    }\n  } else if(item->frame_cat == SPDYLAY_DATA) {\n    size_t next_readmax;\n    spdylay_stream *stream;\n    spdylay_data *data_frame;\n    int r;\n    data_frame = spdylay_outbound_item_get_data_frame(item);\n    r = spdylay_session_predicate_data_send(session, data_frame->stream_id);\n    if(r != 0) {\n      return r;\n    }\n    stream = spdylay_session_get_stream(session, data_frame->stream_id);\n    /* Assuming stream is not NULL */\n    assert(stream);\n    next_readmax = spdylay_session_next_data_read(session, stream);\n    if(next_readmax == 0) {\n      spdylay_stream_defer_data(stream, item, SPDYLAY_DEFERRED_FLOW_CONTROL);\n      return SPDYLAY_ERR_DEFERRED;\n    }\n    framebuflen = spdylay_session_pack_data(session,\n                                            &session->aob.framebuf,\n                                            &session->aob.framebufmax,\n                                            next_readmax,\n                                            data_frame);\n    if(framebuflen == SPDYLAY_ERR_DEFERRED) {\n      spdylay_stream_defer_data(stream, item, SPDYLAY_DEFERRED_NONE);\n      return SPDYLAY_ERR_DEFERRED;\n    } else if(framebuflen == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {\n      r = spdylay_session_add_rst_stream(session, data_frame->stream_id,\n                                         SPDYLAY_INTERNAL_ERROR);\n      if(r == 0) {\n        return framebuflen;\n      } else {\n        return r;\n      }\n    } else if(framebuflen < 0) {\n      return framebuflen;\n    }\n  } else {\n    /* Unreachable */\n    assert(0);\n  }\n  return framebuflen;\n}\n\nspdylay_outbound_item* spdylay_session_get_ob_pq_top\n(spdylay_session *session)\n{\n  return (spdylay_outbound_item*)spdylay_pq_top(&session->ob_pq);\n}\n\nspdylay_outbound_item* spdylay_session_get_next_ob_item\n(spdylay_session *session)\n{\n  if(spdylay_pq_empty(&session->ob_pq)) {\n    if(spdylay_pq_empty(&session->ob_ss_pq)) {\n      return NULL;\n    } else {\n      /* Return item only when concurrent connection limit is not\n         reached */\n      if(spdylay_session_is_outgoing_concurrent_streams_max(session)) {\n        return NULL;\n      } else {\n        return spdylay_pq_top(&session->ob_ss_pq);\n      }\n    }\n  } else {\n    if(spdylay_pq_empty(&session->ob_ss_pq)) {\n      return spdylay_pq_top(&session->ob_pq);\n    } else {\n      spdylay_outbound_item *item, *syn_stream_item;\n      item = spdylay_pq_top(&session->ob_pq);\n      syn_stream_item = spdylay_pq_top(&session->ob_ss_pq);\n      if(spdylay_session_is_outgoing_concurrent_streams_max(session) ||\n         item->pri < syn_stream_item->pri ||\n         (item->pri == syn_stream_item->pri &&\n          item->seq < syn_stream_item->seq)) {\n        return item;\n      } else {\n        return syn_stream_item;\n      }\n    }\n  }\n}\n\nspdylay_outbound_item* spdylay_session_pop_next_ob_item\n(spdylay_session *session)\n{\n  if(spdylay_pq_empty(&session->ob_pq)) {\n    if(spdylay_pq_empty(&session->ob_ss_pq)) {\n      return NULL;\n    } else {\n      /* Pop item only when concurrent connection limit is not\n         reached */\n      if(spdylay_session_is_outgoing_concurrent_streams_max(session)) {\n        return NULL;\n      } else {\n        spdylay_outbound_item *item;\n        item = spdylay_pq_top(&session->ob_ss_pq);\n        spdylay_pq_pop(&session->ob_ss_pq);\n        return item;\n      }\n    }\n  } else {\n    if(spdylay_pq_empty(&session->ob_ss_pq)) {\n      spdylay_outbound_item *item;\n      item = spdylay_pq_top(&session->ob_pq);\n      spdylay_pq_pop(&session->ob_pq);\n      return item;\n    } else {\n      spdylay_outbound_item *item, *syn_stream_item;\n      item = spdylay_pq_top(&session->ob_pq);\n      syn_stream_item = spdylay_pq_top(&session->ob_ss_pq);\n      if(spdylay_session_is_outgoing_concurrent_streams_max(session) ||\n         item->pri < syn_stream_item->pri ||\n         (item->pri == syn_stream_item->pri &&\n          item->seq < syn_stream_item->seq)) {\n        spdylay_pq_pop(&session->ob_pq);\n        return item;\n      } else {\n        spdylay_pq_pop(&session->ob_ss_pq);\n        return syn_stream_item;\n      }\n    }\n  }\n}\n\n/*\n * Called after a frame is sent.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n * SPDYLAY_ERR_CALLBACK_FAILURE\n *     The callback function failed.\n */\nstatic int spdylay_session_after_frame_sent(spdylay_session *session)\n{\n  /* TODO handle FIN flag. */\n  spdylay_outbound_item *item = session->aob.item;\n  if(item->frame_cat == SPDYLAY_CTRL) {\n    spdylay_frame *frame;\n    spdylay_frame_type type;\n    frame = spdylay_outbound_item_get_ctrl_frame(session->aob.item);\n    type = spdylay_outbound_item_get_ctrl_frame_type(session->aob.item);\n    if(session->callbacks.on_ctrl_send_callback) {\n      session->callbacks.on_ctrl_send_callback\n        (session, type, frame, session->user_data);\n    }\n    switch(type) {\n    case SPDYLAY_SYN_STREAM: {\n      spdylay_stream *stream =\n        spdylay_session_get_stream(session, frame->syn_stream.stream_id);\n      if(stream) {\n        spdylay_syn_stream_aux_data *aux_data;\n        stream->state = SPDYLAY_STREAM_OPENING;\n        if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);\n        }\n        if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n        }\n        spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        /* We assume aux_data is a pointer to spdylay_syn_stream_aux_data */\n        aux_data = (spdylay_syn_stream_aux_data*)item->aux_data;\n        if(aux_data->data_prd) {\n          int r;\n          /* spdylay_submit_data() makes a copy of aux_data->data_prd */\n          r = spdylay_submit_data(session, frame->syn_stream.stream_id,\n                                  SPDYLAY_DATA_FLAG_FIN, aux_data->data_prd);\n          if(r != 0) {\n            /* FATAL error */\n            assert(r < SPDYLAY_ERR_FATAL);\n            /* TODO If r is not FATAL, we should send RST_STREAM. */\n            return r;\n          }\n        }\n      }\n      break;\n    }\n    case SPDYLAY_SYN_REPLY: {\n      spdylay_stream *stream =\n        spdylay_session_get_stream(session, frame->syn_reply.stream_id);\n      if(stream) {\n        stream->state = SPDYLAY_STREAM_OPENED;\n        if(frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);\n        }\n        spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        if(item->aux_data) {\n          /* We assume aux_data is a pointer to spdylay_data_provider */\n          spdylay_data_provider *data_prd =\n            (spdylay_data_provider*)item->aux_data;\n          int r;\n          r = spdylay_submit_data(session, frame->syn_reply.stream_id,\n                                  SPDYLAY_DATA_FLAG_FIN, data_prd);\n          if(r != 0) {\n            /* FATAL error */\n            assert(r < SPDYLAY_ERR_FATAL);\n            /* TODO If r is not FATAL, we should send RST_STREAM. */\n            return r;\n          }\n        }\n      }\n      break;\n    }\n    case SPDYLAY_RST_STREAM:\n      if(!session->server &&\n         spdylay_session_is_my_stream_id(session,\n                                         frame->rst_stream.stream_id) &&\n         frame->rst_stream.status_code == SPDYLAY_CANCEL) {\n        spdylay_session_close_pushed_streams(session,\n                                             frame->rst_stream.stream_id,\n                                             frame->rst_stream.status_code);\n      }\n      spdylay_session_close_stream(session, frame->rst_stream.stream_id,\n                                   frame->rst_stream.status_code);\n      break;\n    case SPDYLAY_SETTINGS:\n      /* nothing to do */\n      break;\n    case SPDYLAY_NOOP:\n      /* We don't have any public API to add NOOP, so here is\n         unreachable. */\n      assert(0);\n    case SPDYLAY_PING:\n      /* We record the time now and show application code RTT when\n         reply PING is received. */\n      session->last_ping_unique_id = frame->ping.unique_id;\n      break;\n    case SPDYLAY_GOAWAY:\n      session->goaway_flags |= SPDYLAY_GOAWAY_SEND;\n      break;\n    case SPDYLAY_HEADERS: {\n      spdylay_stream *stream =\n        spdylay_session_get_stream(session, frame->headers.stream_id);\n      if(stream) {\n        if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);\n        }\n        spdylay_session_close_stream_if_shut_rdwr(session, stream);\n      }\n      break;\n    }\n    case SPDYLAY_WINDOW_UPDATE:\n      break;\n    case SPDYLAY_CREDENTIAL:\n      assert(0);\n      break;\n    }\n    spdylay_active_outbound_item_reset(&session->aob);\n  } else if(item->frame_cat == SPDYLAY_DATA) {\n    int r;\n    spdylay_data *data_frame;\n    data_frame = spdylay_outbound_item_get_data_frame(session->aob.item);\n    if(session->callbacks.on_data_send_callback) {\n      session->callbacks.on_data_send_callback\n        (session,\n         data_frame->eof ? data_frame->flags :\n         (data_frame->flags & (~SPDYLAY_DATA_FLAG_FIN)),\n         data_frame->stream_id,\n         (int)(session->aob.framebuflen-SPDYLAY_HEAD_LEN), session->user_data);\n    }\n    if(data_frame->eof && (data_frame->flags & SPDYLAY_DATA_FLAG_FIN)) {\n      spdylay_stream *stream =\n        spdylay_session_get_stream(session, data_frame->stream_id);\n      if(stream) {\n        spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);\n        spdylay_session_close_stream_if_shut_rdwr(session, stream);\n      }\n    }\n    /* If session is closed or RST_STREAM was queued, we won't send\n       further data. */\n    if(data_frame->eof ||\n       spdylay_session_predicate_data_send(session,\n                                           data_frame->stream_id) != 0) {\n      spdylay_active_outbound_item_reset(&session->aob);\n    } else {\n      spdylay_outbound_item* next_item;\n      next_item = spdylay_session_get_next_ob_item(session);\n      /* If priority of this stream is higher or equal to other stream\n         waiting at the top of the queue, we continue to send this\n         data. */\n      if(next_item == NULL || session->aob.item->pri < next_item->pri) {\n        size_t next_readmax;\n        spdylay_stream *stream;\n        stream = spdylay_session_get_stream(session, data_frame->stream_id);\n        /* Assuming stream is not NULL */\n        assert(stream);\n        next_readmax = spdylay_session_next_data_read(session, stream);\n        if(next_readmax == 0) {\n          spdylay_stream_defer_data(stream, session->aob.item,\n                                    SPDYLAY_DEFERRED_FLOW_CONTROL);\n          session->aob.item = NULL;\n          spdylay_active_outbound_item_reset(&session->aob);\n          return 0;\n        }\n        r = (int)spdylay_session_pack_data(session,\n                                      &session->aob.framebuf,\n                                      &session->aob.framebufmax,\n                                      next_readmax,\n                                      data_frame);\n        if(r == SPDYLAY_ERR_DEFERRED) {\n          spdylay_stream_defer_data(stream, session->aob.item,\n                                    SPDYLAY_DEFERRED_NONE);\n          session->aob.item = NULL;\n          spdylay_active_outbound_item_reset(&session->aob);\n        } else if(r == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {\n          /* Stop DATA frame chain and issue RST_STREAM to close the\n             stream.  We don't return\n             SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */\n          r = spdylay_session_add_rst_stream(session, data_frame->stream_id,\n                                             SPDYLAY_INTERNAL_ERROR);\n          spdylay_active_outbound_item_reset(&session->aob);\n          if(r != 0) {\n            return r;\n          }\n        } else if(r < 0) {\n          /* In this context, r is either SPDYLAY_ERR_NOMEM or\n             SPDYLAY_ERR_CALLBACK_FAILURE */\n          spdylay_active_outbound_item_reset(&session->aob);\n          return r;\n        } else {\n          session->aob.framebuflen = r;\n          session->aob.framebufoff = 0;\n        }\n      } else {\n        session->aob.item->seq = session->next_seq++;\n        r = spdylay_pq_push(&session->ob_pq, session->aob.item);\n        if(r == 0) {\n          session->aob.item = NULL;\n          spdylay_active_outbound_item_reset(&session->aob);\n        } else {\n          /* FATAL error */\n          assert(r < SPDYLAY_ERR_FATAL);\n          spdylay_active_outbound_item_reset(&session->aob);\n          return r;\n        }\n      }\n    }\n  } else {\n    /* Unreachable */\n    assert(0);\n  }\n  return 0;\n}\n\nint spdylay_session_send(spdylay_session *session)\n{\n  int r;\n  while(1) {\n    const uint8_t *data;\n    size_t datalen;\n    ssize_t sentlen;\n    if(session->aob.item == NULL) {\n      spdylay_outbound_item *item;\n      ssize_t framebuflen;\n      item = spdylay_session_pop_next_ob_item(session);\n      if(item == NULL) {\n        break;\n      }\n      framebuflen = spdylay_session_prep_frame(session, item);\n      if(framebuflen == SPDYLAY_ERR_DEFERRED) {\n        continue;\n      } else if(framebuflen < 0) {\n        if(item->frame_cat == SPDYLAY_CTRL &&\n           session->callbacks.on_ctrl_not_send_callback &&\n           spdylay_is_non_fatal((int)framebuflen)) {\n          /* The library is responsible for the transmission of\n             WINDOW_UPDATE frame, so we don't call error callback for\n             it. */\n          spdylay_frame_type frame_type;\n          frame_type = spdylay_outbound_item_get_ctrl_frame_type(item);\n          if(frame_type != SPDYLAY_WINDOW_UPDATE) {\n            session->callbacks.on_ctrl_not_send_callback\n              (session,\n               frame_type,\n               spdylay_outbound_item_get_ctrl_frame(item),\n               (int)framebuflen,\n               session->user_data);\n          }\n        }\n        if (item->frame_cat == SPDYLAY_CTRL) {\n          spdylay_frame *frame;\n          spdylay_frame_type frame_type;\n          frame_type = spdylay_outbound_item_get_ctrl_frame_type(item);\n          if (frame_type == SPDYLAY_SYN_STREAM) {\n\n            frame = spdylay_outbound_item_get_ctrl_frame(item);\n            spdylay_session_close_stream(session, frame->syn_stream.stream_id,\n                                         SPDYLAY_INTERNAL_ERROR);\n          }\n        }\n        spdylay_outbound_item_free(item);\n        free(item);\n        if(spdylay_is_fatal((int)framebuflen)) {\n          return (int)framebuflen;\n        } else {\n          continue;\n        }\n      }\n      session->aob.item = item;\n      session->aob.framebuflen = framebuflen;\n      /* Call before_send callback */\n      if(item->frame_cat == SPDYLAY_CTRL &&\n         session->callbacks.before_ctrl_send_callback) {\n        session->callbacks.before_ctrl_send_callback\n          (session,\n           spdylay_outbound_item_get_ctrl_frame_type(item),\n           spdylay_outbound_item_get_ctrl_frame(item),\n           session->user_data);\n      }\n    }\n    data = session->aob.framebuf + session->aob.framebufoff;\n    datalen = session->aob.framebuflen - session->aob.framebufoff;\n    sentlen = session->callbacks.send_callback(session, data, datalen, 0,\n                                               session->user_data);\n    if(sentlen < 0) {\n      if(sentlen == SPDYLAY_ERR_WOULDBLOCK) {\n        return 0;\n      } else {\n        return SPDYLAY_ERR_CALLBACK_FAILURE;\n      }\n    } else {\n      if(session->flow_control &&\n         session->aob.item->frame_cat == SPDYLAY_DATA) {\n        spdylay_data *frame;\n        spdylay_stream *stream;\n        int32_t len;\n        if(session->aob.framebufoff < SPDYLAY_FRAME_HEAD_LENGTH) {\n          len = (int)(session->aob.framebufoff + sentlen - SPDYLAY_FRAME_HEAD_LENGTH);\n        } else {\n          len = (int)sentlen;\n        }\n        if(session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) {\n          session->window_size -= len;\n        }\n        frame = spdylay_outbound_item_get_data_frame(session->aob.item);\n        stream = spdylay_session_get_stream(session, frame->stream_id);\n        if(stream) {\n          stream->window_size -= len;\n        }\n      }\n      session->aob.framebufoff += sentlen;\n      if(session->aob.framebufoff == session->aob.framebuflen) {\n        /* Frame has completely sent */\n        r = spdylay_session_after_frame_sent(session);\n        if(r < 0) {\n          /* FATAL */\n          assert(r < SPDYLAY_ERR_FATAL);\n          return r;\n        }\n      }\n    }\n  }\n  return 0;\n}\n\nstatic ssize_t spdylay_recv(spdylay_session *session, uint8_t *buf, size_t len)\n{\n  ssize_t r;\n  r = session->callbacks.recv_callback\n    (session, buf, len, 0, session->user_data);\n  if(r > 0) {\n    if((size_t)r > len) {\n      return SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(r < 0) {\n    if(r != SPDYLAY_ERR_WOULDBLOCK && r != SPDYLAY_ERR_EOF) {\n      r = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  }\n  return r;\n}\n\nstatic void spdylay_session_call_on_request_recv\n(spdylay_session *session, int32_t stream_id)\n{\n  if(session->callbacks.on_request_recv_callback) {\n    session->callbacks.on_request_recv_callback(session, stream_id,\n                                                session->user_data);\n  }\n}\n\nstatic void spdylay_session_call_on_ctrl_frame_received\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame)\n{\n  if(session->callbacks.on_ctrl_recv_callback) {\n    session->callbacks.on_ctrl_recv_callback\n      (session, type, frame, session->user_data);\n  }\n}\n\n/*\n * Checks whether received stream_id is valid.\n * This function returns 1 if it succeeds, or 0.\n */\nstatic int spdylay_session_is_new_peer_stream_id(spdylay_session *session,\n                                                 int32_t stream_id)\n{\n  if(stream_id == 0) {\n    return 0;\n  }\n  if(session->server) {\n    return stream_id % 2 == 1 && session->last_recv_stream_id < stream_id;\n  } else {\n    return stream_id % 2 == 0 && session->last_recv_stream_id < stream_id;\n  }\n}\n\n/*\n * Returns non-zero iff version == session->version\n */\nstatic int spdylay_session_check_version(spdylay_session *session,\n                                         uint16_t version)\n{\n  return session->version == version;\n}\n\n/*\n * Validates received SYN_STREAM frame |frame|.  This function returns\n * 0 if it succeeds, or non-zero spdylay_status_code.\n */\nstatic int spdylay_session_validate_syn_stream(spdylay_session *session,\n                                               spdylay_syn_stream *frame)\n{\n  if(!spdylay_session_check_version(session, frame->hd.version)) {\n    return SPDYLAY_UNSUPPORTED_VERSION;\n  }\n  if(session->server) {\n    if(frame->assoc_stream_id != 0 ||\n       (frame->hd.flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL)) {\n      return SPDYLAY_PROTOCOL_ERROR;\n    }\n  } else {\n    if(frame->assoc_stream_id == 0) {\n      /* spdy/2 spec: When a client receives a SYN_STREAM from the\n         server with an Associated-To-Stream-ID of 0, it must reply with\n         a RST_STREAM with error code INVALID_STREAM. */\n      return SPDYLAY_INVALID_STREAM;\n    }\n    if((frame->hd.flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) == 0 ||\n       spdylay_session_get_stream(session, frame->assoc_stream_id) == NULL) {\n      /* It seems spdy/2 spec does not say which status code should be\n         returned in these cases. */\n      return SPDYLAY_PROTOCOL_ERROR;\n    }\n  }\n  if(spdylay_session_is_incoming_concurrent_streams_max(session)) {\n    /* spdy/2 spec does not clearly say what to do when max concurrent\n       streams number is reached. The mod_spdy sends\n       SPDYLAY_REFUSED_STREAM and we think it is reasonable. So we\n       follow it. */\n    return SPDYLAY_REFUSED_STREAM;\n  }\n  return 0;\n}\n\n\nstatic int spdylay_session_handle_invalid_stream\n(spdylay_session *session,\n int32_t stream_id,\n spdylay_frame_type type,\n spdylay_frame *frame,\n spdylay_status_code status_code)\n{\n  int r;\n  r = spdylay_session_add_rst_stream(session, stream_id, status_code);\n  if(r != 0) {\n    return r;\n  }\n  if(session->callbacks.on_invalid_ctrl_recv_callback) {\n    session->callbacks.on_invalid_ctrl_recv_callback\n      (session, type, frame, status_code, session->user_data);\n  }\n  return 0;\n}\n\nint spdylay_session_on_syn_stream_received(spdylay_session *session,\n                                           spdylay_frame *frame)\n{\n  int r = 0;\n  int status_code;\n  if(session->goaway_flags) {\n    /* We don't accept SYN_STREAM after GOAWAY is sent or received. */\n    return 0;\n  }\n  if(session->last_recv_stream_id == frame->syn_stream.stream_id) {\n    /* SPDY/3 spec says if an endpoint receives same stream ID twice,\n       it MUST issue a stream error with status code\n       PROTOCOL_ERROR. */\n    status_code = SPDYLAY_PROTOCOL_ERROR;\n  } else if(!spdylay_session_is_new_peer_stream_id\n            (session, frame->syn_stream.stream_id)) {\n    /* SPDY/3 spec says if an endpoint receives a SYN_STREAM with a\n       stream ID which is less than any previously received\n       SYN_STREAM, it MUST issue a session error with status\n       PROTOCOL_ERROR */\n    if(session->callbacks.on_invalid_ctrl_recv_callback) {\n      session->callbacks.on_invalid_ctrl_recv_callback(session,\n                                                       SPDYLAY_SYN_STREAM,\n                                                       frame,\n                                                       SPDYLAY_PROTOCOL_ERROR,\n                                                       session->user_data);\n    }\n    return spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n  } else {\n    session->last_recv_stream_id = frame->syn_stream.stream_id;\n    status_code = spdylay_session_validate_syn_stream(session,\n                                                      &frame->syn_stream);\n  }\n  if(status_code == 0) {\n    uint8_t flags = frame->syn_stream.hd.flags;\n    if((flags & SPDYLAY_CTRL_FLAG_FIN) &&\n       (flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL)) {\n      /* If the stream is UNIDIRECTIONAL and FIN bit set, we can close\n         stream upon receiving SYN_STREAM. So, the stream needs not to\n         be opened. */\n    } else {\n      spdylay_stream *stream;\n      stream = spdylay_session_open_stream(session, frame->syn_stream.stream_id,\n                                           frame->syn_stream.hd.flags,\n                                           frame->syn_stream.pri,\n                                           SPDYLAY_STREAM_OPENING,\n                                           NULL);\n      if(stream) {\n        if(flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n        }\n        if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);\n        }\n        /* We don't call spdylay_session_close_stream_if_shut_rdwr()\n           here because either SPDYLAY_CTRL_FLAG_FIN or\n           SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL is not set here. */\n      }\n    }\n    spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_STREAM,\n                                                frame);\n    if(flags & SPDYLAY_CTRL_FLAG_FIN) {\n      spdylay_session_call_on_request_recv(session,\n                                           frame->syn_stream.stream_id);\n      if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) {\n        /* Note that we call on_stream_close_callback without opening\n           stream. */\n        if(session->callbacks.on_stream_close_callback) {\n          session->callbacks.on_stream_close_callback\n            (session, frame->syn_stream.stream_id, SPDYLAY_OK,\n             session->user_data);\n        }\n      }\n    }\n  } else {\n    r = spdylay_session_handle_invalid_stream\n      (session, frame->syn_stream.stream_id, SPDYLAY_SYN_STREAM, frame,\n       status_code);\n  }\n  return r;\n}\n\nint spdylay_session_on_syn_reply_received(spdylay_session *session,\n                                          spdylay_frame *frame)\n{\n  int r = 0;\n  int valid = 0;\n  int status_code = SPDYLAY_PROTOCOL_ERROR;\n  spdylay_stream *stream;\n  if(!spdylay_session_check_version(session, frame->syn_reply.hd.version)) {\n    return 0;\n  }\n  if((stream = spdylay_session_get_stream(session,\n                                          frame->syn_reply.stream_id)) &&\n     (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {\n    if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) {\n      if(stream->state == SPDYLAY_STREAM_OPENING) {\n        valid = 1;\n        stream->state = SPDYLAY_STREAM_OPENED;\n        spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_REPLY,\n                                                    frame);\n        if(frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          /* This is the last frame of this stream, so disallow\n             further receptions. */\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n          spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        }\n      } else if(stream->state == SPDYLAY_STREAM_CLOSING) {\n        /* This is race condition. SPDYLAY_STREAM_CLOSING indicates\n           that we queued RST_STREAM but it has not been sent. It will\n           eventually sent, so we just ignore this frame. */\n        valid = 1;\n      } else {\n        if(session->version == SPDYLAY_PROTO_SPDY3) {\n          /* SPDY/3 spec says if multiple SYN_REPLY frames for the\n             same active stream ID are received, the receiver must\n             issue a stream error with the status code\n             STREAM_IN_USE. */\n          status_code = SPDYLAY_STREAM_IN_USE;\n        }\n      }\n    }\n  }\n  if(!valid) {\n    r = spdylay_session_handle_invalid_stream\n      (session, frame->syn_reply.stream_id, SPDYLAY_SYN_REPLY, frame,\n       status_code);\n  }\n  return r;\n}\n\nint spdylay_session_on_rst_stream_received(spdylay_session *session,\n                                           spdylay_frame *frame)\n{\n  if(!spdylay_session_check_version(session, frame->rst_stream.hd.version)) {\n    return 0;\n  }\n  spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_RST_STREAM,\n                                              frame);\n  if(session->server &&\n     !spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) &&\n     frame->rst_stream.status_code == SPDYLAY_CANCEL) {\n    spdylay_session_close_pushed_streams(session, frame->rst_stream.stream_id,\n                                         frame->rst_stream.status_code);\n  }\n  spdylay_session_close_stream(session, frame->rst_stream.stream_id,\n                               frame->rst_stream.status_code);\n  return 0;\n}\n\nstatic int spdylay_update_initial_window_size_func(spdylay_map_entry *entry,\n                                                   void *ptr)\n{\n  spdylay_update_window_size_arg *arg;\n  spdylay_stream *stream;\n  arg = (spdylay_update_window_size_arg*)ptr;\n  stream = (spdylay_stream*)entry;\n  spdylay_stream_update_initial_window_size(stream,\n                                            arg->new_window_size,\n                                            arg->old_window_size);\n  /* If window size gets positive, push deferred DATA frame to\n     outbound queue. */\n  if(stream->window_size > 0 &&\n     stream->deferred_data &&\n     (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) {\n    int rv;\n    rv = spdylay_pq_push(&arg->session->ob_pq, stream->deferred_data);\n    if(rv == 0) {\n      spdylay_stream_detach_deferred_data(stream);\n    } else {\n      /* FATAL */\n      assert(rv < SPDYLAY_ERR_FATAL);\n      return rv;\n    }\n  }\n  return 0;\n}\n\n/*\n * Updates the initial window size of all active streams.\n * If error occurs, all streams may not be updated.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nstatic int spdylay_session_update_initial_window_size\n(spdylay_session *session,\n int32_t new_initial_window_size)\n{\n  spdylay_update_window_size_arg arg;\n  arg.session = session;\n  arg.new_window_size = new_initial_window_size;\n  arg.old_window_size =\n    session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE];\n  return spdylay_map_each(&session->streams,\n                          spdylay_update_initial_window_size_func,\n                          &arg);\n}\n\nvoid spdylay_session_update_local_settings(spdylay_session *session,\n                                           spdylay_settings_entry *iv,\n                                           size_t niv)\n{\n  size_t i;\n  for(i = 0; i < niv; ++i) {\n    assert(iv[i].settings_id > 0 && iv[i].settings_id <= SPDYLAY_SETTINGS_MAX);\n    session->local_settings[iv[i].settings_id] = iv[i].value;\n  }\n}\n\nint spdylay_session_on_settings_received(spdylay_session *session,\n                                         spdylay_frame *frame)\n{\n  int rv;\n  size_t i;\n  int check[SPDYLAY_SETTINGS_MAX+1];\n  if(!spdylay_session_check_version(session, frame->settings.hd.version)) {\n    return 0;\n  }\n  /* Check ID/value pairs and persist them if necessary. */\n  memset(check, 0, sizeof(check));\n  for(i = 0; i < frame->settings.niv; ++i) {\n    spdylay_settings_entry *entry = &frame->settings.iv[i];\n    /* SPDY/3 spec says if the multiple values for the same ID were\n       found, use the first one and ignore the rest. */\n    if(entry->settings_id > SPDYLAY_SETTINGS_MAX || entry->settings_id == 0 ||\n       check[entry->settings_id] == 1) {\n      continue;\n    }\n    check[entry->settings_id] = 1;\n    if(entry->settings_id == SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE &&\n       session->flow_control) {\n      /* Update the initial window size of the all active streams */\n      /* Check that initial_window_size < (1u << 31) */\n      if(entry->value < (1u << 31)) {\n        rv = spdylay_session_update_initial_window_size(session, entry->value);\n        if(rv != 0) {\n          return rv;\n        }\n      }\n    }\n    session->remote_settings[entry->settings_id] = entry->value;\n  }\n  spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SETTINGS, frame);\n  return 0;\n}\n\nint spdylay_session_on_ping_received(spdylay_session *session,\n                                     spdylay_frame *frame)\n{\n  int r = 0;\n  if(!spdylay_session_check_version(session, frame->ping.hd.version)) {\n    return 0;\n  }\n  if(frame->ping.unique_id != 0) {\n    if(session->last_ping_unique_id == frame->ping.unique_id) {\n      /* This is ping reply from peer */\n      /* Assign 0 to last_ping_unique_id so that we can ignore same\n         ID. */\n      session->last_ping_unique_id = 0;\n      spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame);\n    } else if((session->server && frame->ping.unique_id % 2 == 1) ||\n              (!session->server && frame->ping.unique_id % 2 == 0)) {\n      /* Peer sent ping, so ping it back */\n      r = spdylay_session_add_ping(session, frame->ping.unique_id);\n      spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame);\n    }\n  }\n  return r;\n}\n\nint spdylay_session_on_goaway_received(spdylay_session *session,\n                                       spdylay_frame *frame)\n{\n  if(!spdylay_session_check_version(session, frame->goaway.hd.version)) {\n    return 0;\n  }\n  session->last_good_stream_id = frame->goaway.last_good_stream_id;\n  session->goaway_flags |= SPDYLAY_GOAWAY_RECV;\n  spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_GOAWAY, frame);\n  return 0;\n}\n\nstatic int push_back_deferred_data_func(spdylay_map_entry *entry, void *ptr)\n{\n  spdylay_session *session;\n  spdylay_stream *stream;\n  session = (spdylay_session*)ptr;\n  stream = (spdylay_stream*)entry;\n  /* If DATA frame is deferred due to flow control, push it back to\n     outbound queue. */\n  if(stream->deferred_data &&\n     (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL) &&\n     stream->window_size > 0) {\n    int rv;\n    rv = spdylay_pq_push(&session->ob_pq, stream->deferred_data);\n    if(rv == 0) {\n      spdylay_stream_detach_deferred_data(stream);\n    } else {\n      /* FATAL */\n      assert(rv < SPDYLAY_ERR_FATAL);\n      return rv;\n    }\n  }\n  return 0;\n}\n\n/*\n * Push back deferred DATA frames to queue if they are deferred due to\n * connection-level flow control.\n */\nstatic int push_back_deferred_data(spdylay_session *session)\n{\n  return spdylay_map_each(&session->streams,\n                          push_back_deferred_data_func, session);\n}\n\nint spdylay_session_on_window_update_received(spdylay_session *session,\n                                              spdylay_frame *frame)\n{\n  spdylay_stream *stream;\n  if(!spdylay_session_check_version(session, frame->window_update.hd.version)) {\n    return 0;\n  }\n  if(!session->flow_control) {\n    return 0;\n  }\n  if((session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) &&\n     frame->window_update.stream_id == 0) {\n    if(INT32_MAX - frame->window_update.delta_window_size <\n       session->window_size) {\n      if(session->callbacks.on_invalid_ctrl_recv_callback) {\n        session->callbacks.on_invalid_ctrl_recv_callback(session,\n                                                         SPDYLAY_WINDOW_UPDATE,\n                                                         frame,\n                                                         SPDYLAY_PROTOCOL_ERROR,\n                                                         session->user_data);\n      }\n      return spdylay_session_fail_session(session,\n                                          SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    session->window_size += frame->window_update.delta_window_size;\n    if(session->window_size > 0) {\n      int rv;\n      rv = push_back_deferred_data(session);\n      if(rv != 0) {\n        /* FATAL */\n        assert(rv < SPDYLAY_ERR_FATAL);\n        return rv;\n      }\n    }\n    spdylay_session_call_on_ctrl_frame_received(session,\n                                                SPDYLAY_WINDOW_UPDATE, frame);\n    return 0;\n  }\n  stream = spdylay_session_get_stream(session, frame->window_update.stream_id);\n  if(stream) {\n    if(INT32_MAX-frame->window_update.delta_window_size < stream->window_size) {\n      int rv;\n      rv = spdylay_session_handle_invalid_stream\n        (session, frame->window_update.stream_id, SPDYLAY_WINDOW_UPDATE, frame,\n         SPDYLAY_FLOW_CONTROL_ERROR);\n      return rv;\n    }\n    stream->window_size += frame->window_update.delta_window_size;\n    if(stream->window_size > 0 &&\n       stream->deferred_data != NULL &&\n       (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) {\n      int rv;\n      rv = spdylay_pq_push(&session->ob_pq, stream->deferred_data);\n      if(rv == 0) {\n        spdylay_stream_detach_deferred_data(stream);\n      } else if(rv < 0) {\n        /* FATAL */\n        assert(rv < SPDYLAY_ERR_FATAL);\n        return rv;\n      }\n    }\n    spdylay_session_call_on_ctrl_frame_received(session,\n                                                SPDYLAY_WINDOW_UPDATE, frame);\n  }\n  return 0;\n}\n\nint spdylay_session_on_headers_received(spdylay_session *session,\n                                        spdylay_frame *frame)\n{\n  int r = 0;\n  int valid = 0;\n  spdylay_stream *stream;\n  if(!spdylay_session_check_version(session, frame->headers.hd.version)) {\n    return 0;\n  }\n  if((stream = spdylay_session_get_stream(session,\n                                          frame->headers.stream_id)) &&\n     (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {\n    if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) {\n      if(stream->state == SPDYLAY_STREAM_OPENED) {\n        valid = 1;\n        spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS,\n                                                    frame);\n        if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n          spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        }\n      } else if(stream->state == SPDYLAY_STREAM_CLOSING) {\n        /* This is race condition. SPDYLAY_STREAM_CLOSING indicates\n           that we queued RST_STREAM but it has not been sent. It will\n           eventually sent, so we just ignore this frame. */\n        valid = 1;\n      }\n    } else {\n      /* If this is remote peer initiated stream, it is OK unless it\n         have sent FIN frame already. But if stream is in\n         SPDYLAY_STREAM_CLOSING, we discard the frame. This is a race\n         condition. */\n      valid = 1;\n      if(stream->state != SPDYLAY_STREAM_CLOSING) {\n        spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS,\n                                                    frame);\n        if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n          spdylay_session_call_on_request_recv(session,\n                                               frame->headers.stream_id);\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n          spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        }\n      }\n    }\n  }\n  if(!valid) {\n    r = spdylay_session_handle_invalid_stream\n      (session, frame->headers.stream_id, SPDYLAY_HEADERS, frame,\n       SPDYLAY_PROTOCOL_ERROR);\n  }\n  return r;\n}\n\nstatic void spdylay_session_handle_parse_error(spdylay_session *session,\n                                               spdylay_frame_type type,\n                                               int error_code)\n{\n  if(session->callbacks.on_ctrl_recv_parse_error_callback) {\n    session->callbacks.on_ctrl_recv_parse_error_callback\n      (session,\n       type,\n       session->iframe.headbuf,\n       sizeof(session->iframe.headbuf),\n       session->iframe.buf,\n       session->iframe.buflen,\n       error_code,\n       session->user_data);\n  }\n}\n\nstatic int spdylay_get_status_code_from_error_code(int error_code)\n{\n  switch(error_code) {\n  case(SPDYLAY_ERR_FRAME_TOO_LARGE):\n    return SPDYLAY_FRAME_TOO_LARGE;\n  default:\n    return SPDYLAY_PROTOCOL_ERROR;\n  }\n}\n\n/* For errors, this function only returns FATAL error. */\nstatic int spdylay_session_process_ctrl_frame(spdylay_session *session)\n{\n  int r = 0;\n  uint16_t type;\n  spdylay_frame frame;\n  type = spdylay_get_uint16(&session->iframe.headbuf[2]);\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    if(session->iframe.error_code == 0) {\n      r = spdylay_frame_unpack_syn_stream(&frame.syn_stream,\n                                          session->iframe.headbuf,\n                                          sizeof(session->iframe.headbuf),\n                                          session->iframe.buf,\n                                          session->iframe.buflen,\n                                          &session->iframe.inflatebuf);\n    } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_frame_unpack_syn_stream_without_nv\n        (&frame.syn_stream,\n         session->iframe.headbuf, sizeof(session->iframe.headbuf),\n         session->iframe.buf, session->iframe.buflen);\n      if(r == 0) {\n        r = session->iframe.error_code;\n      }\n    } else {\n      r = session->iframe.error_code;\n    }\n    if(r == 0) {\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame.syn_stream.nv);\n      }\n      r = spdylay_session_on_syn_stream_received(session, &frame);\n      spdylay_frame_syn_stream_free(&frame.syn_stream);\n    } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK ||\n              r == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_session_handle_invalid_stream\n        (session, frame.syn_stream.stream_id, SPDYLAY_SYN_STREAM, &frame,\n         spdylay_get_status_code_from_error_code(r));\n      spdylay_frame_syn_stream_free(&frame.syn_stream);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_SYN_REPLY:\n    if(session->iframe.error_code == 0) {\n      r = spdylay_frame_unpack_syn_reply(&frame.syn_reply,\n                                         session->iframe.headbuf,\n                                         sizeof(session->iframe.headbuf),\n                                         session->iframe.buf,\n                                         session->iframe.buflen,\n                                         &session->iframe.inflatebuf);\n    } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_frame_unpack_syn_reply_without_nv\n        (&frame.syn_reply,\n         session->iframe.headbuf, sizeof(session->iframe.headbuf),\n         session->iframe.buf, session->iframe.buflen);\n      if(r == 0) {\n        r = session->iframe.error_code;\n      }\n    } else {\n      r = session->iframe.error_code;\n    }\n    if(r == 0) {\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame.syn_reply.nv);\n      }\n      r = spdylay_session_on_syn_reply_received(session, &frame);\n      spdylay_frame_syn_reply_free(&frame.syn_reply);\n    } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK ||\n              r == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_session_handle_invalid_stream\n        (session, frame.syn_reply.stream_id, SPDYLAY_SYN_REPLY, &frame,\n         spdylay_get_status_code_from_error_code(r));\n      spdylay_frame_syn_reply_free(&frame.syn_reply);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_RST_STREAM:\n    r = spdylay_frame_unpack_rst_stream(&frame.rst_stream,\n                                        session->iframe.headbuf,\n                                        sizeof(session->iframe.headbuf),\n                                        session->iframe.buf,\n                                        session->iframe.buflen);\n    if(r == 0) {\n      r = spdylay_session_on_rst_stream_received(session, &frame);\n      spdylay_frame_rst_stream_free(&frame.rst_stream);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_SETTINGS:\n    r = spdylay_frame_unpack_settings(&frame.settings,\n                                      session->iframe.headbuf,\n                                      sizeof(session->iframe.headbuf),\n                                      session->iframe.buf,\n                                      session->iframe.buflen);\n    if(r == 0) {\n      r = spdylay_session_on_settings_received(session, &frame);\n      spdylay_frame_settings_free(&frame.settings);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_NOOP:\n    break;\n  case SPDYLAY_PING:\n    r = spdylay_frame_unpack_ping(&frame.ping,\n                                  session->iframe.headbuf,\n                                  sizeof(session->iframe.headbuf),\n                                  session->iframe.buf,\n                                  session->iframe.buflen);\n    if(r == 0) {\n      r = spdylay_session_on_ping_received(session, &frame);\n      spdylay_frame_ping_free(&frame.ping);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_GOAWAY:\n    r = spdylay_frame_unpack_goaway(&frame.goaway,\n                                    session->iframe.headbuf,\n                                    sizeof(session->iframe.headbuf),\n                                    session->iframe.buf,\n                                    session->iframe.buflen);\n    if(r == 0) {\n      r = spdylay_session_on_goaway_received(session, &frame);\n      spdylay_frame_goaway_free(&frame.goaway);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_HEADERS:\n    if(session->iframe.error_code == 0) {\n      r = spdylay_frame_unpack_headers(&frame.headers,\n                                       session->iframe.headbuf,\n                                       sizeof(session->iframe.headbuf),\n                                       session->iframe.buf,\n                                       session->iframe.buflen,\n                                       &session->iframe.inflatebuf);\n    } else if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_frame_unpack_headers_without_nv\n        (&frame.headers,\n         session->iframe.headbuf, sizeof(session->iframe.headbuf),\n         session->iframe.buf, session->iframe.buflen);\n      if(r == 0) {\n        r = session->iframe.error_code;\n      }\n    } else {\n      r = session->iframe.error_code;\n    }\n    if(r == 0) {\n      if(session->version == SPDYLAY_PROTO_SPDY2) {\n        spdylay_frame_nv_2to3(frame.headers.nv);\n      }\n      r = spdylay_session_on_headers_received(session, &frame);\n      spdylay_frame_headers_free(&frame.headers);\n    } else if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK ||\n              r == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n      r = spdylay_session_handle_invalid_stream\n        (session, frame.headers.stream_id, SPDYLAY_HEADERS, &frame,\n         spdylay_get_status_code_from_error_code(r));\n      spdylay_frame_headers_free(&frame.headers);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_WINDOW_UPDATE:\n    r = spdylay_frame_unpack_window_update(&frame.window_update,\n                                           session->iframe.headbuf,\n                                           sizeof(session->iframe.headbuf),\n                                           session->iframe.buf,\n                                           session->iframe.buflen);\n    if(r == 0) {\n      r = spdylay_session_on_window_update_received(session, &frame);\n      spdylay_frame_window_update_free(&frame.window_update);\n    } else if(spdylay_is_non_fatal(r)) {\n      spdylay_session_handle_parse_error(session, type, r);\n      r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n    }\n    break;\n  case SPDYLAY_CREDENTIAL:\n    /* ignore CREDENTIAL */\n    break;\n  default:\n    /* Unknown frame */\n    if(session->callbacks.on_unknown_ctrl_recv_callback) {\n      session->callbacks.on_unknown_ctrl_recv_callback\n        (session,\n         session->iframe.headbuf,\n         sizeof(session->iframe.headbuf),\n         session->iframe.buf,\n         session->iframe.buflen,\n         session->user_data);\n    }\n  }\n  if(spdylay_is_fatal(r)) {\n    return r;\n  } else {\n    return 0;\n  }\n}\n\nint spdylay_session_on_data_received(spdylay_session *session,\n                                     uint8_t flags, int32_t length,\n                                     int32_t stream_id)\n{\n  int r = 0;\n  spdylay_goaway_status_code status_code = 0;\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {\n      int valid = 0;\n      if(spdylay_session_is_my_stream_id(session, stream_id)) {\n        if(stream->state == SPDYLAY_STREAM_OPENED) {\n          valid = 1;\n          if(session->callbacks.on_data_recv_callback) {\n            session->callbacks.on_data_recv_callback\n              (session, flags, stream_id, length, session->user_data);\n          }\n        } else if(stream->state != SPDYLAY_STREAM_CLOSING) {\n          status_code = SPDYLAY_GOAWAY_PROTOCOL_ERROR;\n        }\n      } else if(stream->state != SPDYLAY_STREAM_CLOSING) {\n        /* It is OK if this is remote peer initiated stream and we did\n           not receive FIN unless stream is in SPDYLAY_STREAM_CLOSING\n           state. This is a race condition. */\n        valid = 1;\n        if(session->callbacks.on_data_recv_callback) {\n          session->callbacks.on_data_recv_callback\n            (session, flags, stream_id, length, session->user_data);\n        }\n        if(flags & SPDYLAY_DATA_FLAG_FIN) {\n          spdylay_session_call_on_request_recv(session, stream_id);\n        }\n      }\n      if(valid) {\n        if(flags & SPDYLAY_DATA_FLAG_FIN) {\n          spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n          spdylay_session_close_stream_if_shut_rdwr(session, stream);\n        }\n      }\n    } else {\n      if(stream->state != SPDYLAY_STREAM_CLOSING) {\n        status_code = SPDYLAY_GOAWAY_PROTOCOL_ERROR;\n      }\n    }\n  } else {\n    /* This should be treated as stream error, but it results in lots\n       of RST_STREAM. So just ignore frame against nonexistent stream\n       for now. */\n    /* status_code = SPDYLAY_INVALID_STREAM; */\n  }\n  if(status_code != 0) {\n    /* Currently all error conditions can be considered as violation\n       of the protocol. Just tear down the session. */\n    r = spdylay_session_fail_session(session, status_code);\n  }\n  return r;\n}\n\n/* For errors, this function only returns FATAL error. */\nstatic int spdylay_session_process_data_frame(spdylay_session *session)\n{\n  uint8_t flags;\n  int32_t length;\n  int32_t stream_id;\n  int r;\n  stream_id = spdylay_get_uint32(session->iframe.headbuf) &\n    SPDYLAY_STREAM_ID_MASK;\n  flags = session->iframe.headbuf[4];\n  length = spdylay_get_uint32(&session->iframe.headbuf[4]) &\n    SPDYLAY_LENGTH_MASK;\n  r = spdylay_session_on_data_received(session, flags, length, stream_id);\n  if(spdylay_is_fatal(r)) {\n    return r;\n  } else {\n    return 0;\n  }\n}\n\nstatic int update_recv_window_size(spdylay_session *session,\n                                   int32_t *recv_window_size_ptr,\n                                   int32_t stream_id,\n                                   int32_t delta_size,\n                                   int32_t initial_window_size)\n{\n  /* If SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE(2) is set and the\n     application does not send WINDOW_UPDATE and the remote endpoint\n     keeps sending data, *recv_window_size_ptr will eventually\n     overflow. */\n  if(*recv_window_size_ptr > INT32_MAX - delta_size) {\n    return spdylay_session_fail_session(session,\n                                        SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n  } else {\n    *recv_window_size_ptr += delta_size;\n  }\n  if(!(session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&\n     !(session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2)) {\n    /* This is just a heuristics. */\n    /* We have to use local_settings here because it is the constraint\n       the remote endpoint should honor. */\n    if(*recv_window_size_ptr >= initial_window_size / 2) {\n      int rv;\n      rv = spdylay_session_add_window_update(session, stream_id,\n                                            *recv_window_size_ptr);\n      if(rv != 0) {\n        return rv;\n      }\n      *recv_window_size_ptr = 0;\n    }\n  }\n  return 0;\n}\n\n/*\n * Accumulates received bytes |delta_size| to connection-level window\n * size and decides whether to send WINDOW_UPDATE. If\n * SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE(2) is set, WINDOW_UPDATE will not\n * be sent.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nstatic int spdylay_session_update_recv_connection_window_size\n(spdylay_session *session,\n int32_t delta_size)\n{\n  return update_recv_window_size(session,\n                                 &session->recv_window_size,\n                                 0,\n                                 delta_size,\n                                 SPDYLAY_INITIAL_WINDOW_SIZE);\n}\n\n/*\n * Accumulates received bytes |delta_size| to stream-level window size\n * and decides whether to send WINDOW_UPDATE. If\n * SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE(2) is set, WINDOW_UPDATE will not\n * be sent.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nstatic int spdylay_session_update_recv_window_size(spdylay_session *session,\n                                                   int32_t stream_id,\n                                                   int32_t delta_size)\n{\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    return update_recv_window_size(session,\n                                   &stream->recv_window_size,\n                                   stream_id,\n                                   delta_size,\n                                   session->local_settings\n                                   [SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);\n  }\n  return 0;\n}\n\nstatic int update_consumed_size\n(spdylay_session *session,\n int32_t *consumed_size_ptr, int32_t *recv_window_size_ptr,\n int32_t stream_id,  int32_t delta_size,\n int32_t initial_window_size)\n{\n  int rv;\n\n  if(*consumed_size_ptr > INT32_MAX - delta_size) {\n    return spdylay_session_fail_session(session,\n                                        SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n  }\n\n  *consumed_size_ptr += delta_size;\n\n  if(*consumed_size_ptr >= initial_window_size / 2) {\n\n    rv = spdylay_session_add_window_update(session, stream_id,\n                                           *consumed_size_ptr);\n\n    if(rv != 0) {\n      return rv;\n    }\n\n    *recv_window_size_ptr -= *consumed_size_ptr;\n    *consumed_size_ptr = 0;\n  }\n\n  return 0;\n}\n\nstatic int spdylay_session_update_connection_consumed_size\n(spdylay_session *session, int32_t delta_size)\n{\n  return update_consumed_size\n    (session, &session->consumed_size, &session->recv_window_size,\n     0, delta_size, SPDYLAY_INITIAL_WINDOW_SIZE);\n}\n\nstatic int spdylay_session_update_stream_consumed_size\n(spdylay_session *session, spdylay_stream *stream, int32_t delta_size)\n{\n  return update_consumed_size\n    (session, &stream->consumed_size, &stream->recv_window_size,\n     stream->stream_id, delta_size,\n     session->local_settings\n     [SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);\n}\n\n/*\n * Returns nonzero if the reception of DATA for stream |stream_id| is\n * allowed.\n */\nstatic int spdylay_session_check_data_recv_allowed(spdylay_session *session,\n                                                   int32_t stream_id)\n{\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {\n      if(spdylay_session_is_my_stream_id(session, stream_id)) {\n        if(stream->state == SPDYLAY_STREAM_OPENED) {\n          return 1;\n        }\n      } else if(stream->state != SPDYLAY_STREAM_CLOSING) {\n        /* It is OK if this is remote peer initiated stream and we did\n           not receive FIN unless stream is in SPDYLAY_STREAM_CLOSING\n           state. This is a race condition. */\n        return 1;\n      }\n    }\n  }\n  return 0;\n}\n\nssize_t spdylay_session_mem_recv(spdylay_session *session,\n                                 const uint8_t *in, size_t inlen)\n{\n  const uint8_t *inmark, *inlimit;\n  inmark = in;\n  inlimit = in+inlen;\n  while(1) {\n    ssize_t r;\n    if(session->iframe.state == SPDYLAY_RECV_HEAD) {\n      size_t remheadbytes;\n      size_t readlen;\n      size_t bufavail = inlimit-inmark;\n      if(bufavail == 0) {\n        break;\n      }\n      remheadbytes = SPDYLAY_HEAD_LEN-session->iframe.headbufoff;\n      readlen = spdylay_min(remheadbytes, bufavail);\n      memcpy(session->iframe.headbuf+session->iframe.headbufoff,\n             inmark, readlen);\n      inmark += readlen;\n      session->iframe.headbufoff += readlen;\n      if(session->iframe.headbufoff == SPDYLAY_HEAD_LEN) {\n        session->iframe.state = SPDYLAY_RECV_PAYLOAD;\n        session->iframe.payloadlen =\n          spdylay_get_uint32(&session->iframe.headbuf[4]) &\n          SPDYLAY_LENGTH_MASK;\n        if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {\n          /* control frame */\n          ssize_t buflen;\n          buflen = spdylay_inbound_frame_payload_nv_offset(&session->iframe);\n          if(buflen == -1) {\n            /* Check if payloadlen is small enough for buffering */\n            if(session->iframe.payloadlen > session->max_recv_ctrl_frame_buf) {\n              session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE;\n              session->iframe.state = SPDYLAY_RECV_PAYLOAD_IGN;\n              buflen = 0;\n            } else {\n              buflen = session->iframe.payloadlen;\n            }\n          } else if(buflen < (ssize_t)session->iframe.payloadlen) {\n            if(session->iframe.payloadlen > session->max_recv_ctrl_frame_buf) {\n              session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE;\n            }\n            /* We are going to receive payload even if the receiving\n               frame is too large to synchronize zlib context. For\n               name/value header block, we will just burn zlib cycle\n               and discard outputs. */\n            session->iframe.state = SPDYLAY_RECV_PAYLOAD_PRE_NV;\n          }\n          /* buflen >= session->iframe.payloadlen means frame is\n             malformed. In this case, we just buffer these bytes and\n             handle error later. */\n          session->iframe.buflen = buflen;\n          r = spdylay_reserve_buffer(&session->iframe.buf,\n                                     &session->iframe.bufmax,\n                                     buflen);\n          if(r != 0) {\n            /* FATAL */\n            assert(r < SPDYLAY_ERR_FATAL);\n            return r;\n          }\n        } else {\n          /* Check stream is open. If it is not open or closing,\n             ignore payload. */\n          int32_t stream_id;\n          stream_id = spdylay_get_uint32(session->iframe.headbuf);\n          if(!spdylay_session_check_data_recv_allowed(session, stream_id)) {\n            session->iframe.state = SPDYLAY_RECV_PAYLOAD_IGN;\n          }\n        }\n      } else {\n        break;\n      }\n    }\n    if(session->iframe.state == SPDYLAY_RECV_PAYLOAD ||\n       session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV ||\n       session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV ||\n       session->iframe.state == SPDYLAY_RECV_PAYLOAD_IGN) {\n      size_t rempayloadlen;\n      size_t bufavail, readlen;\n\n      rempayloadlen = session->iframe.payloadlen - session->iframe.off;\n      bufavail = inlimit - inmark;\n      if(rempayloadlen > 0 && bufavail == 0) {\n        break;\n      }\n      readlen =  spdylay_min(bufavail, rempayloadlen);\n      if(session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV) {\n        size_t pnvlen, rpnvlen, readpnvlen;\n        pnvlen = spdylay_inbound_frame_payload_nv_offset(&session->iframe);\n        rpnvlen = pnvlen - session->iframe.off;\n        readpnvlen = spdylay_min(rpnvlen, readlen);\n\n        memcpy(session->iframe.buf+session->iframe.off, inmark, readpnvlen);\n        readlen -= readpnvlen;\n        session->iframe.off += readpnvlen;\n        inmark += readpnvlen;\n\n        if(session->iframe.off == pnvlen) {\n          session->iframe.state = SPDYLAY_RECV_PAYLOAD_NV;\n        }\n      }\n      if(session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV) {\n        /* For frame with name/value header block, the compressed\n           portion of the block is incrementally decompressed. The\n           result is stored in inflatebuf. */\n        if(session->iframe.error_code == 0 ||\n           session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n          ssize_t decomplen;\n          if(session->iframe.error_code == SPDYLAY_ERR_FRAME_TOO_LARGE) {\n            spdylay_buffer_reset(&session->iframe.inflatebuf);\n          }\n          decomplen = spdylay_zlib_inflate_hd(&session->hd_inflater,\n                                              &session->iframe.inflatebuf,\n                                              inmark, readlen);\n          if(decomplen < 0) {\n            /* We are going to overwrite error_code here if it is\n               already set. But it is fine because the only possible\n               nonzero error code here is SPDYLAY_ERR_FRAME_TOO_LARGE\n               and zlib/fatal error can override it. */\n            session->iframe.error_code = (int)decomplen;\n          } else if(spdylay_buffer_length(&session->iframe.inflatebuf)\n                    > session->max_recv_ctrl_frame_buf) {\n            /* If total length in inflatebuf exceeds certain limit,\n               set TOO_LARGE_FRAME to error_code and issue RST_STREAM\n               later. */\n            session->iframe.error_code = SPDYLAY_ERR_FRAME_TOO_LARGE;\n          }\n        }\n      } else if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {\n        if(session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN) {\n          memcpy(session->iframe.buf+session->iframe.off, inmark, readlen);\n        }\n      }\n      session->iframe.off += readlen;\n      inmark += readlen;\n\n      if(!spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0]) &&\n         readlen > 0) {\n        /* For data frame, We don't buffer data. Instead, just pass\n           received data to callback function. */\n        int32_t data_stream_id = spdylay_get_uint32(session->iframe.headbuf) &\n          SPDYLAY_STREAM_ID_MASK;\n        uint8_t data_flags = session->iframe.headbuf[4];\n\n        if(session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) {\n          r = spdylay_session_update_recv_connection_window_size(session,\n                                                                 (int)readlen);\n          if(r < 0) {\n            /* FATAL */\n            assert(r < SPDYLAY_ERR_FATAL);\n            return r;\n          }\n\n          if(session->iframe.state == SPDYLAY_RECV_PAYLOAD_IGN &&\n             (session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2)) {\n            r = spdylay_session_update_connection_consumed_size\n              (session, (int)readlen);\n\n            if(r < 0) {\n              /* FATAL */\n              assert(r < SPDYLAY_ERR_FATAL);\n              return r;\n            }\n          }\n        }\n        if(session->flow_control &&\n           session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN &&\n           (session->iframe.payloadlen != session->iframe.off ||\n            (data_flags & SPDYLAY_DATA_FLAG_FIN) == 0)) {\n          r = spdylay_session_update_recv_window_size(session,\n                                                      data_stream_id,\n                                                      (int)readlen);\n          if(r < 0) {\n            /* FATAL */\n            assert(r < SPDYLAY_ERR_FATAL);\n            return r;\n          }\n        }\n        if(session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN) {\n          if(session->callbacks.on_data_chunk_recv_callback) {\n            session->callbacks.on_data_chunk_recv_callback(session,\n                                                           data_flags,\n                                                           data_stream_id,\n                                                           inmark - readlen,\n                                                           readlen,\n                                                           session->user_data);\n          }\n        }\n      }\n      if(session->iframe.payloadlen == session->iframe.off) {\n        if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {\n          r = spdylay_session_process_ctrl_frame(session);\n        } else {\n          r = spdylay_session_process_data_frame(session);\n        }\n        if(r < 0) {\n          /* FATAL */\n          assert(r < SPDYLAY_ERR_FATAL);\n          return r;\n        }\n        spdylay_inbound_frame_reset(&session->iframe);\n      }\n    }\n  }\n  return inmark-in;\n}\n\nint spdylay_session_recv(spdylay_session *session)\n{\n  uint8_t buf[SPDYLAY_INBOUND_BUFFER_LENGTH];\n  while(1) {\n    ssize_t readlen;\n    readlen = spdylay_recv(session, buf, sizeof(buf));\n    if(readlen > 0) {\n      ssize_t proclen = spdylay_session_mem_recv(session, buf, readlen);\n      if(proclen < 0) {\n        return (int)proclen;\n      }\n      assert(proclen == readlen);\n    } else if(readlen == 0 || readlen == SPDYLAY_ERR_WOULDBLOCK) {\n      return 0;\n    } else if(readlen == SPDYLAY_ERR_EOF) {\n      return (int)readlen;\n    } else if(readlen < 0) {\n      return SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  }\n}\n\nint spdylay_session_want_read(spdylay_session *session)\n{\n  /* If these flags are set, we don't want to read. The application\n     should drop the connection. */\n  if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) &&\n     (session->goaway_flags & SPDYLAY_GOAWAY_SEND)) {\n    return 0;\n  }\n  /* Unless GOAWAY is sent or received, we always want to read\n     incoming frames. After GOAWAY is sent or received, we are only\n     interested in active streams. */\n  return !session->goaway_flags || spdylay_map_size(&session->streams) > 0;\n}\n\nint spdylay_session_want_write(spdylay_session *session)\n{\n  /* If these flags are set, we don't want to write any data. The\n     application should drop the connection. */\n  if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) &&\n     (session->goaway_flags & SPDYLAY_GOAWAY_SEND)) {\n    return 0;\n  }\n  /*\n   * Unless GOAWAY is sent or received, we want to write frames if\n   * there is pending ones. If pending frame is SYN_STREAM and\n   * concurrent stream limit is reached, we don't want to write\n   * SYN_STREAM.  After GOAWAY is sent or received, we want to write\n   * frames if there is pending ones AND there are active frames.\n   */\n  return (session->aob.item != NULL || !spdylay_pq_empty(&session->ob_pq) ||\n          (!spdylay_pq_empty(&session->ob_ss_pq) &&\n           !spdylay_session_is_outgoing_concurrent_streams_max(session))) &&\n    (!session->goaway_flags || spdylay_map_size(&session->streams) > 0);\n}\n\nint spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id)\n{\n  int r;\n  spdylay_frame *frame;\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_ping_init(&frame->ping, session->version, unique_id);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_ping_free(&frame->ping);\n    free(frame);\n  }\n  return r;\n}\n\nint spdylay_session_add_goaway(spdylay_session *session,\n                               int32_t last_good_stream_id,\n                               uint32_t status_code)\n{\n  int r;\n  spdylay_frame *frame;\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_goaway_init(&frame->goaway, session->version,\n                            last_good_stream_id, status_code);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_goaway_free(&frame->goaway);\n    free(frame);\n  }\n  return r;\n}\n\nint spdylay_session_add_window_update(spdylay_session *session,\n                                      int32_t stream_id,\n                                      int32_t delta_window_size)\n{\n  int r;\n  spdylay_frame *frame;\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_window_update_init(&frame->window_update, session->version,\n                                   stream_id, delta_window_size);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_window_update_free(&frame->window_update);\n    free(frame);\n  }\n  return r;\n}\n\nssize_t spdylay_session_pack_data(spdylay_session *session,\n                                  uint8_t **buf_ptr, size_t *buflen_ptr,\n                                  size_t datamax,\n                                  spdylay_data *frame)\n{\n  ssize_t framelen = datamax+8, r;\n  int eof_flags;\n  uint8_t flags;\n  r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen);\n  if(r != 0) {\n    return r;\n  }\n  eof_flags = 0;\n  r = frame->data_prd.read_callback\n    (session, frame->stream_id, (*buf_ptr)+8, datamax,\n     &eof_flags, &frame->data_prd.source, session->user_data);\n  if(r == SPDYLAY_ERR_DEFERRED || r == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {\n    return r;\n  } else if(r < 0 || datamax < (size_t)r) {\n    /* This is the error code when callback is failed. */\n    return SPDYLAY_ERR_CALLBACK_FAILURE;\n  }\n  memset(*buf_ptr, 0, SPDYLAY_HEAD_LEN);\n  spdylay_put_uint32be(&(*buf_ptr)[0], frame->stream_id);\n  spdylay_put_uint32be(&(*buf_ptr)[4], (uint32_t)r);\n  flags = 0;\n  if(eof_flags) {\n    frame->eof = 1;\n    if(frame->flags & SPDYLAY_DATA_FLAG_FIN) {\n      flags |= SPDYLAY_DATA_FLAG_FIN;\n    }\n  }\n  (*buf_ptr)[4] = flags;\n  return r+8;\n}\n\nuint32_t spdylay_session_get_next_unique_id(spdylay_session *session)\n{\n  uint32_t ret_id;\n  if(session->next_unique_id > SPDYLAY_MAX_UNIQUE_ID) {\n    if(session->server) {\n      session->next_unique_id = 2;\n    } else {\n      session->next_unique_id = 1;\n    }\n  }\n  ret_id = session->next_unique_id;\n  session->next_unique_id += 2;\n  return ret_id;\n}\n\nvoid* spdylay_session_get_stream_user_data(spdylay_session *session,\n                                           int32_t stream_id)\n{\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream) {\n    return stream->stream_user_data;\n  } else {\n    return NULL;\n  }\n}\n\n\nint spdylay_session_set_stream_user_data(spdylay_session *session,\n                                         int32_t stream_id,\n                                         void *stream_user_data) {\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if (!stream) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  stream->stream_user_data = stream_user_data;\n  return 0;\n}\n\nint spdylay_session_resume_data(spdylay_session *session, int32_t stream_id)\n{\n  int r;\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream == NULL || stream->deferred_data == NULL ||\n     (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  r = spdylay_pq_push(&session->ob_pq, stream->deferred_data);\n  if(r == 0) {\n    spdylay_stream_detach_deferred_data(stream);\n  }\n  return r;\n}\n\nuint8_t spdylay_session_get_pri_lowest(spdylay_session *session)\n{\n  if(session->version == SPDYLAY_PROTO_SPDY2) {\n    return SPDYLAY_PRI_LOWEST_SPDY2;\n  } else if(session->version == SPDYLAY_PROTO_SPDY3) {\n    return SPDYLAY_PRI_LOWEST_SPDY3;\n  } else {\n    return 0;\n  }\n}\n\nsize_t spdylay_session_get_outbound_queue_size(spdylay_session *session)\n{\n  return spdylay_pq_size(&session->ob_pq)+spdylay_pq_size(&session->ob_ss_pq);\n}\n\nint spdylay_session_set_initial_client_cert_origin(spdylay_session *session,\n                                                   const char *scheme,\n                                                   const char *host,\n                                                   uint16_t port)\n{\n  return 0;\n}\n\nconst spdylay_origin* spdylay_session_get_client_cert_origin\n(spdylay_session *session,\n size_t slot)\n{\n  return NULL;\n}\n\n\nint spdylay_session_set_option(spdylay_session *session,\n                               int optname, void *optval, size_t optlen)\n{\n  switch(optname) {\n  case SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE:\n    if(optlen == sizeof(int)) {\n      int intval = *(int*)optval;\n      if(intval) {\n        session->opt_flags |= SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE;\n      } else {\n        session->opt_flags &= ~SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE;\n      }\n    } else {\n      return SPDYLAY_ERR_INVALID_ARGUMENT;\n    }\n    break;\n  case SPDYLAY_OPT_MAX_RECV_CTRL_FRAME_BUFFER:\n    if(optlen == sizeof(uint32_t)) {\n      uint32_t intval = *(uint32_t*)optval;\n      if((1 << 13) <= intval && intval < (1 << 24)) {\n        session->max_recv_ctrl_frame_buf = intval;\n      } else {\n        return SPDYLAY_ERR_INVALID_ARGUMENT;\n      }\n    } else {\n      return SPDYLAY_ERR_INVALID_ARGUMENT;\n    }\n    break;\n  case SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2:\n    if(optlen == sizeof(int)) {\n      int intval = *(int*)optval;\n      if(intval) {\n        session->opt_flags |= SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2;\n      } else {\n        session->opt_flags &= ~SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2;\n      }\n    } else {\n      return SPDYLAY_ERR_INVALID_ARGUMENT;\n    }\n    break;\n  default:\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  return 0;\n}\n\nint32_t spdylay_session_get_stream_recv_data_length\n(spdylay_session *session, int32_t stream_id)\n{\n  spdylay_stream *stream;\n  stream = spdylay_session_get_stream(session, stream_id);\n  if(stream == NULL) {\n    return -1;\n  }\n  if(session->flow_control == SPDYLAY_FLOW_CONTROL_NONE) {\n    return 0;\n  }\n  return stream->recv_window_size;\n}\n\nint32_t spdylay_session_get_recv_data_length(spdylay_session *session)\n{\n  if((session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) == 0) {\n    return 0;\n  }\n  return session->recv_window_size;\n}\n\nint spdylay_session_consume(spdylay_session *session, int32_t stream_id,\n                            size_t size)\n{\n  int rv;\n  spdylay_stream *stream;\n\n  if(stream_id == 0) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n\n  if(!(session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2)) {\n    return SPDYLAY_ERR_INVALID_STATE;\n  }\n\n  if(session->flow_control & SPDYLAY_FLOW_CONTROL_CONNECTION) {\n\n    rv = spdylay_session_update_connection_consumed_size(session, (int)size);\n\n    if(spdylay_is_fatal(rv)) {\n      return rv;\n    }\n  }\n\n  if(session->flow_control & SPDYLAY_FLOW_CONTROL_STREAM) {\n\n    stream = spdylay_session_get_stream(session, stream_id);\n\n    if(stream) {\n      rv = spdylay_session_update_stream_consumed_size(session, stream, (int)size);\n\n      if(spdylay_is_fatal(rv)) {\n        return rv;\n      }\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "lib/spdylay_session.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_SESSION_H\n#define SPDYLAY_SESSION_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_pq.h\"\n#include \"spdylay_map.h\"\n#include \"spdylay_frame.h\"\n#include \"spdylay_zlib.h\"\n#include \"spdylay_stream.h\"\n#include \"spdylay_buffer.h\"\n#include \"spdylay_outbound_item.h\"\n\n/**\n * @macro\n * Lowest priority value in SPDY/2, which is 3.\n */\n#define SPDYLAY_PRI_LOWEST_SPDY2 3\n/**\n * @macro\n * Lowest priority value in SPDY/3, which is 7.\n */\n#define SPDYLAY_PRI_LOWEST_SPDY3 7\n\n/*\n * Option flags.\n */\ntypedef enum {\n  SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0,\n  SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE2 = 1 << 1\n} spdylay_optmask;\n\ntypedef struct {\n  spdylay_outbound_item *item;\n  /* Buffer for outbound frames. Used to pack one frame. The memory\n     pointed by framebuf is initially allocated by\n     spdylay_session_{client,server}_new() and deallocated by\n     spdylay_session_del() */\n  uint8_t *framebuf;\n  /* The capacity of framebuf in bytes */\n  size_t framebufmax;\n  /* The length of the frame stored in framebuf */\n  size_t framebuflen;\n  /* The number of bytes has been sent */\n  size_t framebufoff;\n} spdylay_active_outbound_item;\n\n/* Buffer length for inbound raw byte stream. */\n#define SPDYLAY_INBOUND_BUFFER_LENGTH 16384\n\n#define SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH (SPDYLAY_DATA_PAYLOAD_LENGTH+8)\n#define SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH \\\n  SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH\n#define SPDYLAY_INITIAL_NV_BUFFER_LENGTH 4096\n\n/* Internal state when receiving incoming frame */\ntypedef enum {\n  /* Receiving frame header */\n  SPDYLAY_RECV_HEAD,\n  /* Receiving frame payload (comes after length field) */\n  SPDYLAY_RECV_PAYLOAD,\n  /* Receiving frame payload, but the received bytes are discarded. */\n  SPDYLAY_RECV_PAYLOAD_IGN,\n  /* Receiving frame payload that comes before name/value header\n     block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */\n  SPDYLAY_RECV_PAYLOAD_PRE_NV,\n  /* Receiving name/value header block in frame payload. Applied only\n     for SYN_STREAM, SYN_REPLY and HEADERS. */\n  SPDYLAY_RECV_PAYLOAD_NV\n} spdylay_inbound_state;\n\n#define SPDYLAY_HEAD_LEN 8\n\n/* Maximum unique ID in use for PING. If unique ID exeeds this number,\n   it wraps to 1 (client) or 2 (server) */\n#define SPDYLAY_MAX_UNIQUE_ID ((1u << 31)-1)\n\ntypedef struct {\n  /* Buffer used to store name/value pairs while inflating them using\n     zlib on unpack */\n  spdylay_buffer inflatebuf;\n  /* Payload for control frames. It is not used for DATA frames */\n  uint8_t *buf;\n  /* How many bytes are filled in headbuf */\n  size_t headbufoff;\n  /* Capacity of buf */\n  size_t bufmax;\n  /* For frames without name/value header block, this is how many\n     bytes are going to filled in buf. For frames with the block, buf\n     only contains bytes that come before ther block, but this value\n     includes the length of the block. buflen <= bufmax must be\n     fulfilled. */\n  size_t buflen;\n  /* length in Length field */\n  size_t payloadlen;\n  /* How many bytes are received for this frame. off <= payloadlen\n     must be fulfilled. */\n  size_t off;\n  spdylay_inbound_state state;\n  /* Error code */\n  int error_code;\n  uint8_t headbuf[SPDYLAY_HEAD_LEN];\n} spdylay_inbound_frame;\n\ntypedef enum {\n  SPDYLAY_GOAWAY_NONE = 0,\n  /* Flag means GOAWAY frame is sent to the remote peer. */\n  SPDYLAY_GOAWAY_SEND = 0x1,\n  /* Flag means GOAWAY frame is received from the remote peer. */\n  SPDYLAY_GOAWAY_RECV = 0x2,\n  /* Flag means connection should be dropped after sending GOAWAY. */\n  SPDYLAY_GOAWAY_FAIL_ON_SEND = 0x4\n} spdylay_goaway_flag;\n\ntypedef enum {\n  /* flow control disabled */\n  SPDYLAY_FLOW_CONTROL_NONE = 0,\n  /* stream-level flow control enabled */\n  SPDYLAY_FLOW_CONTROL_STREAM = 1,\n  /* connection-level flow control enabled */\n  SPDYLAY_FLOW_CONTROL_CONNECTION = 1 << 1\n} spdylay_flow_control_flag;\n\nstruct spdylay_session {\n  spdylay_map /* <spdylay_stream*> */ streams;\n  /* Queue for outbound frames other than SYN_STREAM */\n  spdylay_pq /* <spdylay_outbound_item*> */ ob_pq;\n  /* Queue for outbound SYN_STREAM frame */\n  spdylay_pq /* <spdylay_outbound_item*> */ ob_ss_pq;\n\n  spdylay_active_outbound_item aob;\n  spdylay_inbound_frame iframe;\n\n  spdylay_zlib hd_deflater;\n  spdylay_zlib hd_inflater;\n\n  spdylay_session_callbacks callbacks;\n\n  /* Sequence number of outbound frame to maintain the order of\n     enqueue if priority is equal. */\n  int64_t next_seq;\n\n  /* Buffer used to store inflated name/value pairs in wire format\n     temporarily on pack/unpack. */\n  uint8_t *nvbuf;\n\n  void *user_data;\n\n  /* The number of outgoing streams. This will be capped by\n     remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]. */\n  size_t num_outgoing_streams;\n  /* The number of incoming streams. This will be capped by\n     local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]. */\n  size_t num_incoming_streams;\n  /* The number of bytes allocated for nvbuf */\n  size_t nvbuflen;\n\n  /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */\n  uint32_t next_stream_id;\n  int32_t last_recv_stream_id;\n  /* Counter of unique ID of PING. Wraps when it exceeds\n     SPDYLAY_MAX_UNIQUE_ID */\n  uint32_t next_unique_id;\n\n  /* The last unique ID sent to the peer. */\n  uint32_t last_ping_unique_id;\n  /* This is the value in GOAWAY frame sent by remote endpoint. */\n  int32_t last_good_stream_id;\n  /* Current sender window size. This value is computed against the\n     current initial window size of remote endpoint. */\n  int32_t window_size;\n  /* Keep track of the number of bytes received without\n     WINDOW_UPDATE. */\n  int32_t recv_window_size;\n  /* The number of bytes consumed by the application and now is\n     subject to WINDOW_UPDATE.  This is only used when\n     SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2 is used. */\n  int32_t consumed_size;\n\n  /* Settings value received from the remote endpoint. We just use ID\n     as index. The index = 0 is unused. */\n  uint32_t remote_settings[SPDYLAY_SETTINGS_MAX+1];\n  /* Settings value of the local endpoint. */\n  uint32_t local_settings[SPDYLAY_SETTINGS_MAX+1];\n\n  /* Option flags. This is bitwise-OR of 0 or more of spdylay_optmask. */\n  uint32_t opt_flags;\n  /* Maxmum size of buffer to use when receving control frame. */\n  uint32_t max_recv_ctrl_frame_buf;\n\n\n  /* The protocol version: either SPDYLAY_PROTO_SPDY2 or\n     SPDYLAY_PROTO_SPDY3  */\n  uint16_t version;\n  uint8_t server;\n\n  /* Flags indicating GOAWAY is sent and/or recieved. The flags are\n     composed by bitwise OR-ing spdylay_goaway_flag. */\n  uint8_t goaway_flags;\n\n  /* Flag to indicate whether this session enforces flow\n     control. Bitwise-OR-ing spdylay_flow_control_flag. We assume\n     following combinations only:\n\n     - SPDYLAY_FLOW_CONTROL_NONE (spdy/2)\n     - SPDYLAY_FLOW_CONTROL_STREAM (spdy/3)\n     - SPDYLAY_FLOW_CONTROL_STREAM | SPDYLAY_FLOW_CONTROL_CONNECTION (spdy/3.1)\n  */\n  uint8_t flow_control;\n};\n\n/* Struct used when updating initial window size of each active\n   stream. */\ntypedef struct {\n  spdylay_session *session;\n  int32_t new_window_size, old_window_size;\n} spdylay_update_window_size_arg;\n\n/* TODO stream timeout etc */\n\n/*\n * Returns nonzero value if |stream_id| is initiated by local\n * endpoint.\n */\nint spdylay_session_is_my_stream_id(spdylay_session *session,\n                                    int32_t stream_id);\n\n/*\n * Adds frame |frame| to the outbound queue in |session|. The\n * |frame_cat| must be either SPDYLAY_CTRL or SPDYLAY_DATA. If the\n * |frame_cat| is SPDYLAY_CTRL, the |frame| must be a pointer to\n * spdylay_frame. If the |frame_cat| is SPDYLAY_DATA, it must be a\n * pointer to spdylay_data. |aux_data| is a pointer to the arbitrary\n * data. Its interpretation is defined per the type of the frame. When\n * this function succeeds, it takes ownership of |frame| and\n * |aux_data|, so caller must not free them on success.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_add_frame(spdylay_session *session,\n                              spdylay_frame_category frame_cat,\n                              void *abs_frame, void *aux_data);\n\n/*\n * Adds RST_STREAM frame for the stream |stream_id| with status code\n * |status_code|. This is a convenient function built on top of\n * spdylay_session_add_frame() to add RST_STREAM easily.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_add_rst_stream(spdylay_session *session,\n                                   int32_t stream_id, uint32_t status_code);\n\n/*\n * Adds PING frame with unique ID |unique_id|. This is a convenient\n * functin built on top of spdylay_session_add_frame() to add PING\n * easily.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id);\n\n/*\n * Adds GOAWAY frame with last-good-stream-ID |last_good_stream_id|\n * and the status code |status_code|. The |status_code| is ignored if\n * the protocol version is SPDYLAY_PROTO_SPDY2. This is a convenient\n * function built on top of spdylay_session_add_frame() to add GOAWAY\n * easily.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_add_goaway(spdylay_session *session,\n                               int32_t last_good_stream_id,\n                               uint32_t status_code);\n\n/*\n * Adds WINDOW_UPDATE frame with stream ID |stream_id| and\n * delta-window-size |delta_window_size|. This is a convenient\n * function built on top of spdylay_session_add_frame() to add\n * WINDOW_UPDATE easily.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_add_window_update(spdylay_session *session,\n                                      int32_t stream_id,\n                                      int32_t delta_window_size);\n\n/*\n * Creates new stream in |session| with stream ID |stream_id|,\n * priority |pri| and flags |flags|.  SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL\n * flag is set in |flags|, this stream is\n * unidirectional. SPDYLAY_CTRL_FLAG_FIN flag is set in |flags|, the\n * sender of SYN_STREAM will not send any further data in this\n * stream. Since this function is called when SYN_STREAM is sent or\n * received, these flags are taken from SYN_STREAM.  The state of\n * stream is set to |initial_state|.  |stream_user_data| is a pointer\n * to the arbitrary user supplied data to be associated to this\n * stream.\n *\n * This function returns a pointer to created new stream object, or\n * NULL.\n */\nspdylay_stream* spdylay_session_open_stream(spdylay_session *session,\n                                            int32_t stream_id,\n                                            uint8_t flags, uint8_t pri,\n                                            spdylay_stream_state initial_state,\n                                            void *stream_user_data);\n\n/*\n * Closes stream whose stream ID is |stream_id|. The reason of closure\n * is indicated by |status_code|. When closing the stream,\n * on_stream_close_callback will be called.\n *\n * This function returns 0 if it succeeds, or one the following\n * negative error codes:\n *\n * SPDYLAY_ERR_INVALID_ARGUMENT\n *     The specified stream does not exist.\n */\nint spdylay_session_close_stream(spdylay_session *session, int32_t stream_id,\n                                 spdylay_status_code status_code);\n\n/*\n * Closes all pushed streams which associate them to stream\n * |stream_id| with the status code |status_code|.\n */\nvoid spdylay_session_close_pushed_streams(spdylay_session *session,\n                                          int32_t stream_id,\n                                          spdylay_status_code status_code);\n\n/*\n * If further receptions and transmissions over the stream |stream_id|\n * are disallowed, close the stream with status code |status_code|.\n *\n * This function returns 0 if it\n * succeeds, or one of the following negative error codes:\n *\n * SPDYLAY_ERR_INVALID_ARGUMENT\n *     The specified stream does not exist.\n */\nint spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session,\n                                              spdylay_stream *stream);\n\n/*\n * Called when SYN_STREAM is received, assuming |frame.syn_stream| is\n * properly initialized.  This function does first validate received\n * frame and then open stream and call callback functions. This\n * function does not return error if frame is not valid.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_syn_stream_received(spdylay_session *session,\n                                           spdylay_frame *frame);\n\n/*\n * Called when SYN_REPLY is received, assuming |frame.syn_reply| is\n * properly initialized.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_syn_reply_received(spdylay_session *session,\n                                          spdylay_frame *frame);\n\n\n/*\n * Called when RST_STREAM is received, assuming |frame.rst_stream| is\n * properly initialized.\n *\n * This function returns 0 and never fail.\n */\nint spdylay_session_on_rst_stream_received(spdylay_session *session,\n                                           spdylay_frame *frame);\n\n/*\n * Called when SETTINGS is received, assuming |frame.settings| is\n * properly initialized.\n *\n * This function returns 0 and never fail.\n */\nint spdylay_session_on_settings_received(spdylay_session *session,\n                                         spdylay_frame *frame);\n\n/*\n * Called when PING is received, assuming |frame.ping| is properly\n * initialized.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_ping_received(spdylay_session *session,\n                                     spdylay_frame *frame);\n\n/*\n * Called when GOAWAY is received, assuming |frame.goaway| is properly\n * initialized.\n *\n * This function returns 0 and never fail.\n */\nint spdylay_session_on_goaway_received(spdylay_session *session,\n                                       spdylay_frame *frame);\n\n/*\n * Called when HEADERS is recieved, assuming |frame.headers| is\n * properly initialized.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_headers_received(spdylay_session *session,\n                                        spdylay_frame *frame);\n\n/*\n * Called when WINDOW_UPDATE is recieved, assuming\n * |frame.window_update| is properly initialized.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_window_update_received(spdylay_session *session,\n                                              spdylay_frame *frame);\n\n/*\n * Called when DATA is received.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_session_on_data_received(spdylay_session *session,\n                                     uint8_t flags, int32_t length,\n                                     int32_t stream_id);\n\n/*\n * Returns spdylay_stream* object whose stream ID is |stream_id|.  It\n * could be NULL if such stream does not exist.\n */\nspdylay_stream* spdylay_session_get_stream(spdylay_session *session,\n                                           int32_t stream_id);\n\n/*\n * Packs DATA frame |frame| in wire frame format and stores it in\n * |*buf_ptr|.  The capacity of |*buf_ptr| is |*buflen_ptr|\n * length. This function expands |*buf_ptr| as necessary to store\n * given |frame|. It packs header in first 8 bytes. Remaining bytes\n * are the DATA apyload and are filled using |frame->data_prd|. The\n * length of payload is at most |datamax| bytes.\n *\n * This function returns the size of packed frame if it succeeds, or\n * one of the following negative error codes:\n *\n * SPDYLAY_ERR_DEFERRED\n *     The DATA frame is postponed.\n * SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE\n *     The read_callback failed (stream error).\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n * SPDYLAY_ERR_CALLBACK_FAILURE\n *     The read_callback failed (session error).\n */\nssize_t spdylay_session_pack_data(spdylay_session *session,\n                                  uint8_t **buf_ptr, size_t *buflen_ptr,\n                                  size_t datamax,\n                                  spdylay_data *frame);\n\n/*\n * Returns next unique ID which can be used with PING.\n */\nuint32_t spdylay_session_get_next_unique_id(spdylay_session *session);\n\n/*\n * Returns top of outbound frame queue. This function returns NULL if\n * queue is empty.\n */\nspdylay_outbound_item* spdylay_session_get_ob_pq_top(spdylay_session *session);\n\n/*\n * Pops and returns next item to send. If there is no such item,\n * returns NULL.  This function takes into account max concurrent\n * streams. That means if session->ob_pq is empty but\n * session->ob_ss_pq has item and max concurrent streams is reached,\n * then this function returns NULL.\n */\nspdylay_outbound_item* spdylay_session_pop_next_ob_item\n(spdylay_session *session);\n\n/*\n * Returns next item to send. If there is no such item, this function\n * returns NULL.  This function takes into account max concurrent\n * streams. That means if session->ob_pq is empty but\n * session->ob_ss_pq has item and max concurrent streams is reached,\n * then this function returns NULL.\n */\nspdylay_outbound_item* spdylay_session_get_next_ob_item\n(spdylay_session *session);\n\n/*\n * Updates local settings with the |iv|. The number of elements in the\n * array pointed by the |iv| is given by the |niv|.  This function\n * assumes that the all settings_id member in |iv| are in range 1 to\n * SPDYLAY_SETTINGS_MAX, inclusive.\n */\nvoid spdylay_session_update_local_settings(spdylay_session *session,\n                                           spdylay_settings_entry *iv,\n                                           size_t niv);\n\n#endif /* SPDYLAY_SESSION_H */\n"
  },
  {
    "path": "lib/spdylay_stream.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_stream.h\"\n\n#include <assert.h>\n\nvoid spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,\n                         uint8_t flags, uint8_t pri,\n                         spdylay_stream_state initial_state,\n                         int32_t initial_window_size,\n                         void *stream_user_data)\n{\n  spdylay_map_entry_init(&stream->map_entry, stream_id);\n  stream->stream_id = stream_id;\n  stream->flags = flags;\n  stream->pri = pri;\n  stream->state = initial_state;\n  stream->shut_flags = SPDYLAY_SHUT_NONE;\n  stream->pushed_streams = NULL;\n  stream->pushed_streams_length = 0;\n  stream->pushed_streams_capacity = 0;\n  stream->stream_user_data = stream_user_data;\n  stream->deferred_data = NULL;\n  stream->deferred_flags = SPDYLAY_DEFERRED_NONE;\n  stream->window_size = initial_window_size;\n  stream->recv_window_size = 0;\n  stream->consumed_size = 0;\n}\n\nvoid spdylay_stream_free(spdylay_stream *stream)\n{\n  free(stream->pushed_streams);\n  spdylay_outbound_item_free(stream->deferred_data);\n  free(stream->deferred_data);\n}\n\nvoid spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag)\n{\n  stream->shut_flags |= flag;\n}\n\nint spdylay_stream_add_pushed_stream(spdylay_stream *stream, int32_t stream_id)\n{\n  if(stream->pushed_streams_capacity == stream->pushed_streams_length) {\n    int32_t *streams;\n    size_t capacity = stream->pushed_streams_capacity == 0 ?\n      5 : stream->pushed_streams_capacity*2;\n    streams = realloc(stream->pushed_streams, capacity*sizeof(int32_t));\n    if(streams == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    }\n    stream->pushed_streams = streams;\n    stream->pushed_streams_capacity = capacity;\n  }\n  stream->pushed_streams[stream->pushed_streams_length++] = stream_id;\n  return 0;\n}\n\nvoid spdylay_stream_defer_data(spdylay_stream *stream,\n                               spdylay_outbound_item *data,\n                               uint8_t flags)\n{\n  assert(stream->deferred_data == NULL);\n  stream->deferred_data = data;\n  stream->deferred_flags = flags;\n}\n\nvoid spdylay_stream_detach_deferred_data(spdylay_stream *stream)\n{\n  stream->deferred_data = NULL;\n  stream->deferred_flags = SPDYLAY_DEFERRED_NONE;\n}\n\nvoid spdylay_stream_update_initial_window_size(spdylay_stream *stream,\n                                               int32_t new_initial_window_size,\n                                               int32_t old_initial_window_size)\n{\n  stream->window_size =\n    new_initial_window_size-(old_initial_window_size-stream->window_size);\n}\n"
  },
  {
    "path": "lib/spdylay_stream.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_STREAM_H\n#define SPDYLAY_STREAM_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n#include \"spdylay_outbound_item.h\"\n#include \"spdylay_map.h\"\n\n/*\n * If local peer is stream initiator:\n * SPDYLAY_STREAM_OPENING : upon sending SYN_STREAM\n * SPDYLAY_STREAM_OPENED : upon receiving SYN_REPLY\n * SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM\n *\n * If remote peer is stream initiator:\n * SPDYLAY_STREAM_OPENING : upon receiving SYN_STREAM\n * SPDYLAY_STREAM_OPENED : upon sending SYN_REPLY\n * SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM\n */\ntypedef enum {\n  /* Initial state */\n  SPDYLAY_STREAM_INITIAL,\n  /* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is\n     not received yet.  For receiver: SYN_STREAM has been received,\n     but it does not send SYN_REPLY yet. */\n  SPDYLAY_STREAM_OPENING,\n  /* For stream initiator: SYN_REPLY is received. For receiver:\n     SYN_REPLY is sent. */\n  SPDYLAY_STREAM_OPENED,\n  /* RST_STREAM is received, but somehow we need to keep stream in\n     memory. */\n  SPDYLAY_STREAM_CLOSING\n} spdylay_stream_state;\n\ntypedef enum {\n  SPDYLAY_SHUT_NONE = 0,\n  /* Indicates further receptions will be disallowed. */\n  SPDYLAY_SHUT_RD = 0x01,\n  /* Indicates further transmissions will be disallowed. */\n  SPDYLAY_SHUT_WR = 0x02,\n  /* Indicates both further receptions and transmissions will be\n     disallowed. */\n  SPDYLAY_SHUT_RDWR = SPDYLAY_SHUT_RD | SPDYLAY_SHUT_WR\n} spdylay_shut_flag;\n\ntypedef enum {\n  SPDYLAY_DEFERRED_NONE = 0,\n  /* Indicates the DATA is deferred due to flow control. */\n  SPDYLAY_DEFERRED_FLOW_CONTROL = 0x01\n} spdylay_deferred_flag;\n\ntypedef struct {\n  /* Intrusive Map */\n  spdylay_map_entry map_entry;\n  /* The array of server-pushed stream IDs which associate them to\n     this stream. */\n  int32_t *pushed_streams;\n  /* The arbitrary data provided by user for this stream. */\n  void *stream_user_data;\n  /* Deferred DATA frame */\n  spdylay_outbound_item *deferred_data;\n  /* The number of stored pushed stream ID in |pushed_streams| */\n  size_t pushed_streams_length;\n  /* The maximum number of stream ID the |pushed_streams| can\n     store. */\n  size_t pushed_streams_capacity;\n  spdylay_stream_state state;\n  /* stream ID */\n  int32_t stream_id;\n  /* Current sender window size. This value is computed against the\n     current initial window size of remote endpoint. */\n  int32_t window_size;\n  /* Keep track of the number of bytes received without\n     WINDOW_UPDATE. */\n  int32_t recv_window_size;\n  /* The number of bytes consumed by the application and now is\n     subject to WINDOW_UPDATE.  This is only used when\n     SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2 is used. */\n  int32_t consumed_size;\n  /* Use same value in SYN_STREAM frame */\n  uint8_t flags;\n  /* Use same scheme in SYN_STREAM frame */\n  uint8_t pri;\n  /* Bitwise OR of zero or more spdylay_shut_flag values */\n  uint8_t shut_flags;\n  /* The flags for defered DATA. Bitwise OR of zero or more\n     spdylay_deferred_flag values */\n  uint8_t deferred_flags;\n} spdylay_stream;\n\nvoid spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,\n                         uint8_t flags, uint8_t pri,\n                         spdylay_stream_state initial_state,\n                         int32_t initial_window_size,\n                         void *stream_user_data);\n\nvoid spdylay_stream_free(spdylay_stream *stream);\n\n/*\n * Disallow either further receptions or transmissions, or both.\n * |flag| is bitwise OR of one or more of spdylay_shut_flag.\n */\nvoid spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag);\n\n/*\n * Add server-pushed |stream_id| to this stream. This happens when\n * server-pushed stream is associated to this stream. This function\n * returns 0 if it succeeds, or negative error code.\n *\n * RETURN VALUE\n * ------------\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nint spdylay_stream_add_pushed_stream(spdylay_stream *stream, int32_t stream_id);\n\n/*\n * Defer DATA frame |data|. We won't call this function in the\n * situation where stream->deferred_data != NULL.  If |flags| is\n * bitwise OR of zero or more spdylay_deferred_flag values.\n */\nvoid spdylay_stream_defer_data(spdylay_stream *stream,\n                               spdylay_outbound_item *data,\n                               uint8_t flags);\n\n/*\n * Detaches deferred data from this stream. This function does not\n * free deferred data.\n */\nvoid spdylay_stream_detach_deferred_data(spdylay_stream *stream);\n\n/*\n * Updates the initial window size with the new value\n * |new_initial_window_size|. The |old_initial_window_size| is used to\n * calculate the current window size.\n */\nvoid spdylay_stream_update_initial_window_size(spdylay_stream *stream,\n                                               int32_t new_initial_window_size,\n                                               int32_t old_initial_window_size);\n\n#endif /* SPDYLAY_STREAM */\n"
  },
  {
    "path": "lib/spdylay_submit.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_submit.h\"\n\n#include <string.h>\n\n#include \"spdylay_session.h\"\n#include \"spdylay_frame.h\"\n#include \"spdylay_helper.h\"\n\nstatic int spdylay_submit_syn_stream_shared\n(spdylay_session *session,\n uint8_t flags,\n int32_t assoc_stream_id,\n uint8_t pri,\n const char **nv,\n const spdylay_data_provider *data_prd,\n void *stream_user_data)\n{\n  int r;\n  spdylay_frame *frame;\n  char **nv_copy;\n  uint8_t flags_copy;\n  spdylay_data_provider *data_prd_copy = NULL;\n  spdylay_syn_stream_aux_data *aux_data;\n  int32_t stream_id;\n  if(pri > spdylay_session_get_pri_lowest(session)) {\n    pri = spdylay_session_get_pri_lowest(session);\n  }\n  if(assoc_stream_id != 0 && session->server == 0) {\n    assoc_stream_id = 0;\n  }\n  if(!spdylay_frame_nv_check_null(nv)) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n\n  if(session->next_stream_id > INT32_MAX) {\n    return SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE;\n  }\n  stream_id = session->next_stream_id;\n  session->next_stream_id += 2;\n\n  if(data_prd != NULL && data_prd->read_callback != NULL) {\n    data_prd_copy = malloc(sizeof(spdylay_data_provider));\n    if(data_prd_copy == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    }\n    *data_prd_copy = *data_prd;\n  }\n  aux_data = malloc(sizeof(spdylay_syn_stream_aux_data));\n  if(aux_data == NULL) {\n    free(data_prd_copy);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  aux_data->data_prd = data_prd_copy;\n  aux_data->stream_user_data = stream_user_data;\n\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    free(aux_data);\n    free(data_prd_copy);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  nv_copy = spdylay_frame_nv_norm_copy(nv);\n  if(nv_copy == NULL) {\n    free(frame);\n    free(aux_data);\n    free(data_prd_copy);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  flags_copy = 0;\n  if(flags & SPDYLAY_CTRL_FLAG_FIN) {\n    flags_copy |= SPDYLAY_CTRL_FLAG_FIN;\n  }\n  if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) {\n    flags_copy |= SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL;\n  }\n  spdylay_frame_syn_stream_init(&frame->syn_stream, session->version,\n                                flags_copy, stream_id, assoc_stream_id, pri,\n                                nv_copy);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame,\n                                aux_data);\n  if(r != 0) {\n    spdylay_frame_syn_stream_free(&frame->syn_stream);\n    free(frame);\n    free(aux_data);\n    free(data_prd_copy);\n  }\n  return r;\n}\n\nint spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags,\n                              int32_t assoc_stream_id, uint8_t pri,\n                              const char **nv, void *stream_user_data)\n{\n  return spdylay_submit_syn_stream_shared(session, flags, assoc_stream_id,\n                                          pri, nv, NULL, stream_user_data);\n}\n\nint spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags,\n                             int32_t stream_id, const char **nv)\n{\n  int r;\n  spdylay_frame *frame;\n  char **nv_copy;\n  uint8_t flags_copy;\n  if(!spdylay_frame_nv_check_null(nv)) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  nv_copy = spdylay_frame_nv_norm_copy(nv);\n  if(nv_copy == NULL) {\n    free(frame);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  flags_copy = 0;\n  if(flags & SPDYLAY_CTRL_FLAG_FIN) {\n    flags_copy |= SPDYLAY_CTRL_FLAG_FIN;\n  }\n  spdylay_frame_syn_reply_init(&frame->syn_reply, session->version, flags_copy,\n                               stream_id, nv_copy);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_syn_reply_free(&frame->syn_reply);\n    free(frame);\n  }\n  return r;\n}\n\nint spdylay_submit_headers(spdylay_session *session, uint8_t flags,\n                           int32_t stream_id, const char **nv)\n{\n  int r;\n  spdylay_frame *frame;\n  char **nv_copy;\n  uint8_t flags_copy;\n  if(!spdylay_frame_nv_check_null(nv)) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  nv_copy = spdylay_frame_nv_norm_copy(nv);\n  if(nv_copy == NULL) {\n    free(frame);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  flags_copy = 0;\n  if(flags & SPDYLAY_CTRL_FLAG_FIN) {\n    flags_copy |= SPDYLAY_CTRL_FLAG_FIN;\n  }\n  spdylay_frame_headers_init(&frame->headers, session->version, flags_copy,\n                             stream_id, nv_copy);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r != 0) {\n    spdylay_frame_headers_free(&frame->headers);\n    free(frame);\n  }\n  return r;\n}\n\nint spdylay_submit_ping(spdylay_session *session)\n{\n  return spdylay_session_add_ping(session,\n                                  spdylay_session_get_next_unique_id(session));\n}\n\nint spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id,\n                              uint32_t status_code)\n{\n  return spdylay_session_add_rst_stream(session, stream_id, status_code);\n}\n\nint spdylay_submit_goaway(spdylay_session *session, uint32_t status_code)\n{\n  return spdylay_session_add_goaway(session, session->last_recv_stream_id,\n                                    status_code);\n}\n\nint spdylay_submit_settings(spdylay_session *session, uint8_t flags,\n                            const spdylay_settings_entry *iv, size_t niv)\n{\n  spdylay_frame *frame;\n  spdylay_settings_entry *iv_copy;\n  int check[SPDYLAY_SETTINGS_MAX+1];\n  size_t i;\n  int r;\n  memset(check, 0, sizeof(check));\n  for(i = 0; i < niv; ++i) {\n    if(iv[i].settings_id > SPDYLAY_SETTINGS_MAX || iv[i].settings_id == 0 ||\n       check[iv[i].settings_id] == 1) {\n      return SPDYLAY_ERR_INVALID_ARGUMENT;\n    } else {\n      check[iv[i].settings_id] = 1;\n    }\n  }\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  iv_copy = spdylay_frame_iv_copy(iv, niv);\n  if(iv_copy == NULL) {\n    free(frame);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  spdylay_frame_iv_sort(iv_copy, niv);\n  spdylay_frame_settings_init(&frame->settings, session->version,\n                              flags, iv_copy, niv);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  if(r == 0) {\n    spdylay_session_update_local_settings(session, iv_copy, niv);\n  } else {\n    spdylay_frame_settings_free(&frame->settings);\n    free(frame);\n  }\n  return r;\n}\n\nint spdylay_submit_window_update(spdylay_session *session, int32_t stream_id,\n                                 int32_t delta_window_size)\n{\n  spdylay_stream *stream;\n  if(delta_window_size <= 0) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  if(stream_id == 0) {\n    session->recv_window_size -= spdylay_min(delta_window_size,\n                                             session->recv_window_size);\n    session->consumed_size -= spdylay_min(delta_window_size,\n                                          session->consumed_size);\n  } else {\n    stream = spdylay_session_get_stream(session, stream_id);\n    if(stream == NULL) {\n      return SPDYLAY_ERR_STREAM_CLOSED;\n    }\n    stream->recv_window_size -= spdylay_min(delta_window_size,\n                                            stream->recv_window_size);\n    stream->consumed_size -= spdylay_min(delta_window_size,\n                                         stream->consumed_size);\n  }\n  return spdylay_session_add_window_update(session, stream_id,\n                                           delta_window_size);\n}\n\nint spdylay_submit_request(spdylay_session *session, uint8_t pri,\n                           const char **nv,\n                           const spdylay_data_provider *data_prd,\n                           void *stream_user_data)\n{\n  int flags;\n  flags = 0;\n  if(data_prd == NULL || data_prd->read_callback == NULL) {\n    flags |= SPDYLAY_CTRL_FLAG_FIN;\n  }\n  return spdylay_submit_syn_stream_shared(session, flags, 0, pri, nv, data_prd,\n                                          stream_user_data);\n}\n\nint spdylay_submit_response(spdylay_session *session,\n                            int32_t stream_id, const char **nv,\n                            const spdylay_data_provider *data_prd)\n{\n  int r;\n  spdylay_frame *frame;\n  char **nv_copy;\n  uint8_t flags = 0;\n  spdylay_data_provider *data_prd_copy = NULL;\n  if(!spdylay_frame_nv_check_null(nv)) {\n    return SPDYLAY_ERR_INVALID_ARGUMENT;\n  }\n  if(data_prd != NULL && data_prd->read_callback != NULL) {\n    data_prd_copy = malloc(sizeof(spdylay_data_provider));\n    if(data_prd_copy == NULL) {\n      return SPDYLAY_ERR_NOMEM;\n    }\n    *data_prd_copy = *data_prd;\n  }\n  frame = malloc(sizeof(spdylay_frame));\n  if(frame == NULL) {\n    free(data_prd_copy);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  nv_copy = spdylay_frame_nv_norm_copy(nv);\n  if(nv_copy == NULL) {\n    free(frame);\n    free(data_prd_copy);\n    return SPDYLAY_ERR_NOMEM;\n  }\n  if(data_prd_copy == NULL) {\n    flags |= SPDYLAY_CTRL_FLAG_FIN;\n  }\n  spdylay_frame_syn_reply_init(&frame->syn_reply, session->version, flags,\n                               stream_id, nv_copy);\n  r = spdylay_session_add_frame(session, SPDYLAY_CTRL, frame,\n                                data_prd_copy);\n  if(r != 0) {\n    spdylay_frame_syn_reply_free(&frame->syn_reply);\n    free(frame);\n    free(data_prd_copy);\n  }\n  return r;\n}\n\nint spdylay_submit_data(spdylay_session *session, int32_t stream_id,\n                        uint8_t flags,\n                        const spdylay_data_provider *data_prd)\n{\n  int r;\n  spdylay_data *data_frame;\n  uint8_t nflags = 0;\n  data_frame = malloc(sizeof(spdylay_data));\n  if(data_frame == NULL) {\n    return SPDYLAY_ERR_NOMEM;\n  }\n  if(flags & SPDYLAY_DATA_FLAG_FIN) {\n    nflags |= SPDYLAY_DATA_FLAG_FIN;\n  }\n  spdylay_frame_data_init(data_frame, stream_id, nflags, data_prd);\n  r = spdylay_session_add_frame(session, SPDYLAY_DATA, data_frame, NULL);\n  if(r != 0) {\n    spdylay_frame_data_free(data_frame);\n    free(data_frame);\n  }\n  return r;\n}\n"
  },
  {
    "path": "lib/spdylay_submit.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_SUBMIT_H\n#define SPDYLAY_SUBMIT_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include <spdylay/spdylay.h>\n\n#endif /* SPDYLAY_SUBMIT_H */\n"
  },
  {
    "path": "lib/spdylay_zlib.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_zlib.h\"\n\n#include <assert.h>\n\n#define COMPRESSION_LEVEL 9\n#define WINDOW_BITS 11\n#define MEM_LEVEL 1\n\nstatic const char spdy2_hd_dict[] =\n  \"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-\"\n  \"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi\"\n  \"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser\"\n  \"-agent10010120020120220320420520630030130230330430530630740040140240340440\"\n  \"5406407408409410411412413414415416417500501502503504505accept-rangesageeta\"\n  \"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic\"\n  \"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran\"\n  \"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati\"\n  \"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo\"\n  \"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe\"\n  \"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic\"\n  \"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1\"\n  \".1statusversionurl\";\n\nstatic const uint8_t spdy3_hd_dict[] = {\n  0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   /*  - - - - o p t i */\n  0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   /*  o n s - - - - h */\n  0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   /*  e a d - - - - p */\n  0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   /*  o s t - - - - p */\n  0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   /*  u t - - - - d e */\n  0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   /*  l e t e - - - - */\n  0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   /*  t r a c e - - - */\n  0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   /*  - a c c e p t - */\n  0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   /*  - - - a c c e p */\n  0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /*  t - c h a r s e */\n  0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   /*  t - - - - a c c */\n  0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /*  e p t - e n c o */\n  0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   /*  d i n g - - - - */\n  0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   /*  a c c e p t - l */\n  0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   /*  a n g u a g e - */\n  0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   /*  - - - a c c e p */\n  0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   /*  t - r a n g e s */\n  0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   /*  - - - - a g e - */\n  0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   /*  - - - a l l o w */\n  0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   /*  - - - - a u t h */\n  0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   /*  o r i z a t i o */\n  0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   /*  n - - - - c a c */\n  0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   /*  h e - c o n t r */\n  0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   /*  o l - - - - c o */\n  0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   /*  n n e c t i o n */\n  0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   /*  - - - - c o n t */\n  0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   /*  e n t - b a s e */\n  0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   /*  - - - - c o n t */\n  0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   /*  e n t - e n c o */\n  0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   /*  d i n g - - - - */\n  0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   /*  c o n t e n t - */\n  0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   /*  l a n g u a g e */\n  0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   /*  - - - - c o n t */\n  0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   /*  e n t - l e n g */\n  0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   /*  t h - - - - c o */\n  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   /*  n t e n t - l o */\n  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /*  c a t i o n - - */\n  0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /*  - - c o n t e n */\n  0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   /*  t - m d 5 - - - */\n  0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   /*  - c o n t e n t */\n  0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   /*  - r a n g e - - */\n  0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   /*  - - c o n t e n */\n  0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   /*  t - t y p e - - */\n  0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   /*  - - d a t e - - */\n  0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   /*  - - e t a g - - */\n  0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   /*  - - e x p e c t */\n  0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   /*  - - - - e x p i */\n  0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   /*  r e s - - - - f */\n  0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   /*  r o m - - - - h */\n  0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   /*  o s t - - - - i */\n  0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   /*  f - m a t c h - */\n  0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   /*  - - - i f - m o */\n  0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   /*  d i f i e d - s */\n  0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   /*  i n c e - - - - */\n  0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   /*  i f - n o n e - */\n  0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   /*  m a t c h - - - */\n  0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   /*  - i f - r a n g */\n  0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   /*  e - - - - i f - */\n  0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   /*  u n m o d i f i */\n  0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   /*  e d - s i n c e */\n  0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   /*  - - - - l a s t */\n  0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   /*  - m o d i f i e */\n  0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   /*  d - - - - l o c */\n  0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   /*  a t i o n - - - */\n  0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   /*  - m a x - f o r */\n  0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   /*  w a r d s - - - */\n  0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   /*  - p r a g m a - */\n  0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   /*  - - - p r o x y */\n  0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   /*  - a u t h e n t */\n  0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   /*  i c a t e - - - */\n  0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   /*  - p r o x y - a */\n  0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   /*  u t h o r i z a */\n  0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   /*  t i o n - - - - */\n  0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   /*  r a n g e - - - */\n  0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   /*  - r e f e r e r */\n  0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   /*  - - - - r e t r */\n  0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   /*  y - a f t e r - */\n  0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   /*  - - - s e r v e */\n  0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   /*  r - - - - t e - */\n  0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   /*  - - - t r a i l */\n  0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   /*  e r - - - - t r */\n  0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   /*  a n s f e r - e */\n  0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   /*  n c o d i n g - */\n  0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   /*  - - - u p g r a */\n  0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   /*  d e - - - - u s */\n  0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   /*  e r - a g e n t */\n  0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   /*  - - - - v a r y */\n  0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   /*  - - - - v i a - */\n  0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   /*  - - - w a r n i */\n  0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   /*  n g - - - - w w */\n  0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   /*  w - a u t h e n */\n  0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   /*  t i c a t e - - */\n  0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   /*  - - m e t h o d */\n  0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   /*  - - - - g e t - */\n  0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   /*  - - - s t a t u */\n  0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   /*  s - - - - 2 0 0 */\n  0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   /*  - O K - - - - v */\n  0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   /*  e r s i o n - - */\n  0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   /*  - - H T T P - 1 */\n  0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   /*  - 1 - - - - u r */\n  0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   /*  l - - - - p u b */\n  0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   /*  l i c - - - - s */\n  0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   /*  e t - c o o k i */\n  0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   /*  e - - - - k e e */\n  0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   /*  p - a l i v e - */\n  0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   /*  - - - o r i g i */\n  0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   /*  n 1 0 0 1 0 1 2 */\n  0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   /*  0 1 2 0 2 2 0 5 */\n  0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   /*  2 0 6 3 0 0 3 0 */\n  0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   /*  2 3 0 3 3 0 4 3 */\n  0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   /*  0 5 3 0 6 3 0 7 */\n  0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   /*  4 0 2 4 0 5 4 0 */\n  0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   /*  6 4 0 7 4 0 8 4 */\n  0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   /*  0 9 4 1 0 4 1 1 */\n  0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   /*  4 1 2 4 1 3 4 1 */\n  0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   /*  4 4 1 5 4 1 6 4 */\n  0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   /*  1 7 5 0 2 5 0 4 */\n  0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   /*  5 0 5 2 0 3 - N */\n  0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   /*  o n - A u t h o */\n  0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   /*  r i t a t i v e */\n  0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   /*  - I n f o r m a */\n  0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   /*  t i o n 2 0 4 - */\n  0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   /*  N o - C o n t e */\n  0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   /*  n t 3 0 1 - M o */\n  0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   /*  v e d - P e r m */\n  0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   /*  a n e n t l y 4 */\n  0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   /*  0 0 - B a d - R */\n  0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   /*  e q u e s t 4 0 */\n  0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   /*  1 - U n a u t h */\n  0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   /*  o r i z e d 4 0 */\n  0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   /*  3 - F o r b i d */\n  0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   /*  d e n 4 0 4 - N */\n  0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   /*  o t - F o u n d */\n  0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   /*  5 0 0 - I n t e */\n  0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   /*  r n a l - S e r */\n  0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   /*  v e r - E r r o */\n  0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   /*  r 5 0 1 - N o t */\n  0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   /*  - I m p l e m e */\n  0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   /*  n t e d 5 0 3 - */\n  0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   /*  S e r v i c e - */\n  0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   /*  U n a v a i l a */\n  0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   /*  b l e J a n - F */\n  0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   /*  e b - M a r - A */\n  0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   /*  p r - M a y - J */\n  0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   /*  u n - J u l - A */\n  0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   /*  u g - S e p t - */\n  0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   /*  O c t - N o v - */\n  0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   /*  D e c - 0 0 - 0 */\n  0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   /*  0 - 0 0 - M o n */\n  0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   /*  - - T u e - - W */\n  0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   /*  e d - - T h u - */\n  0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   /*  - F r i - - S a */\n  0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   /*  t - - S u n - - */\n  0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   /*  G M T c h u n k */\n  0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   /*  e d - t e x t - */\n  0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   /*  h t m l - i m a */\n  0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   /*  g e - p n g - i */\n  0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   /*  m a g e - j p g */\n  0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   /*  - i m a g e - g */\n  0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /*  i f - a p p l i */\n  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /*  c a t i o n - x */\n  0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   /*  m l - a p p l i */\n  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   /*  c a t i o n - x */\n  0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   /*  h t m l - x m l */\n  0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   /*  - t e x t - p l */\n  0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   /*  a i n - t e x t */\n  0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   /*  - j a v a s c r */\n  0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   /*  i p t - p u b l */\n  0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   /*  i c p r i v a t */\n  0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   /*  e m a x - a g e */\n  0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   /*  - g z i p - d e */\n  0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   /*  f l a t e - s d */\n  0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   /*  c h c h a r s e */\n  0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   /*  t - u t f - 8 c */\n  0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   /*  h a r s e t - i */\n  0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   /*  s o - 8 8 5 9 - */\n  0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   /*  1 - u t f - - - */\n  0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          /*  - e n q - 0 - */\n};\n\nstatic const uint8_t* spdylay_select_hd_dict(size_t *len_ptr, uint16_t version)\n{\n  const uint8_t *hd_dict;\n  hd_dict = NULL;\n  if(version == SPDYLAY_PROTO_SPDY2) {\n    hd_dict = (const uint8_t*)spdy2_hd_dict;\n    *len_ptr = sizeof(spdy2_hd_dict);\n  } else if(version == SPDYLAY_PROTO_SPDY3) {\n    hd_dict = spdy3_hd_dict;\n    *len_ptr = sizeof(spdy3_hd_dict);\n  } else {\n    return NULL;\n  }\n\n  return hd_dict;\n}\n\nint spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater, int comp,\n                                 uint16_t version)\n{\n  const unsigned char *hd_dict;\n  size_t hd_dict_length;\n  deflater->zst.next_in = Z_NULL;\n  deflater->zst.zalloc = Z_NULL;\n  deflater->zst.zfree = Z_NULL;\n  deflater->zst.opaque = Z_NULL;\n  deflater->version = version;\n  hd_dict = spdylay_select_hd_dict(&hd_dict_length, version);\n  if(hd_dict == NULL) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  if(Z_OK != deflateInit2(&deflater->zst,\n                          comp ? COMPRESSION_LEVEL : Z_NO_COMPRESSION,\n                          Z_DEFLATED, WINDOW_BITS, MEM_LEVEL,\n                          Z_DEFAULT_STRATEGY)) {\n    return SPDYLAY_ERR_ZLIB;\n  }\n  if(Z_OK != deflateSetDictionary(&deflater->zst, (uint8_t*)hd_dict,\n                                  (uInt)hd_dict_length)) {\n    spdylay_zlib_deflate_free(deflater);\n    return SPDYLAY_ERR_ZLIB;\n  }\n  return 0;\n}\n\nint spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater, uint16_t version)\n{\n  const unsigned char *hd_dict;\n  size_t hd_dict_length;\n  inflater->zst.next_in = Z_NULL;\n  inflater->zst.avail_in = 0;\n  inflater->zst.zalloc = Z_NULL;\n  inflater->zst.zfree = Z_NULL;\n  inflater->zst.opaque = Z_NULL;\n  inflater->version = version;\n  hd_dict = spdylay_select_hd_dict(&hd_dict_length, version);\n  if(hd_dict == NULL) {\n    return SPDYLAY_ERR_UNSUPPORTED_VERSION;\n  }\n  if(Z_OK != inflateInit(&inflater->zst)) {\n    return SPDYLAY_ERR_ZLIB;\n  }\n  return 0;\n}\n\nvoid spdylay_zlib_deflate_free(spdylay_zlib *deflater)\n{\n  deflateEnd(&deflater->zst);\n}\n\nvoid spdylay_zlib_inflate_free(spdylay_zlib *inflater)\n{\n  inflateEnd(&inflater->zst);\n}\n\nssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater,\n                                uint8_t *out, size_t outlen,\n                                const uint8_t *in, size_t inlen)\n{\n  int r;\n  deflater->zst.avail_in = (uint)inlen;\n  deflater->zst.next_in = (uint8_t*)in;\n  deflater->zst.avail_out = (uint)outlen;\n  deflater->zst.next_out = out;\n  r = deflate(&deflater->zst, Z_SYNC_FLUSH);\n  if(r == Z_OK) {\n    return outlen-deflater->zst.avail_out;\n  } else {\n    return SPDYLAY_ERR_ZLIB;\n  }\n}\n\nsize_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len)\n{\n  return deflateBound(&deflater->zst, len);\n}\n\nssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater,\n                                spdylay_buffer* buf,\n                                const uint8_t *in, size_t inlen)\n{\n  int r;\n  inflater->zst.avail_in = (uint)inlen;\n  inflater->zst.next_in = (uint8_t*)in;\n  while(1) {\n    if(spdylay_buffer_avail(buf) == 0) {\n      if((r = spdylay_buffer_alloc(buf)) != 0) {\n        return r;\n      }\n    }\n    inflater->zst.avail_out = (uint)spdylay_buffer_avail(buf);\n    inflater->zst.next_out = spdylay_buffer_get(buf);\n    r = inflate(&inflater->zst, Z_NO_FLUSH);\n    if(r == Z_STREAM_ERROR || r == Z_STREAM_END || r == Z_DATA_ERROR) {\n      return SPDYLAY_ERR_ZLIB;\n    } else if(r == Z_NEED_DICT) {\n      const uint8_t *hd_dict;\n      size_t hd_dict_length;\n      hd_dict = spdylay_select_hd_dict(&hd_dict_length, inflater->version);\n      assert(hd_dict);\n      if(Z_OK != inflateSetDictionary(&inflater->zst, (uint8_t*)hd_dict,\n                                      (uint)hd_dict_length)) {\n        return SPDYLAY_ERR_ZLIB;\n      }\n    } else {\n      if(r == Z_OK) {\n        size_t adv = spdylay_buffer_avail(buf)-inflater->zst.avail_out;\n        spdylay_buffer_advance(buf, adv);\n      }\n      if(inflater->zst.avail_in == 0 && inflater->zst.avail_out > 0) {\n        break;\n      }\n    }\n  }\n  return spdylay_buffer_length(buf);\n}\n"
  },
  {
    "path": "lib/spdylay_zlib.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_ZLIB_H\n#define SPDYLAY_ZLIB_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n#include <zlib.h>\n\n#include \"spdylay_buffer.h\"\n\n/* This structure is used for both deflater and inflater. */\ntypedef struct {\n  z_stream zst;\n  /* The protocol version to select the dictionary later. */\n  uint16_t version;\n} spdylay_zlib;\n\n/*\n * Initializes |deflater| for deflating name/values pairs in the frame\n * of the protocol version |version|. If the |comp| is zero,\n * compression level becomes 0, which means no compression.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_ZLIB\n *     The z_stream initialization failed.\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n */\nint spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater, int comp,\n                                 uint16_t version);\n\n/*\n * Initializes |inflater| for inflating name/values pairs in the\n * frame of the protocol version |version|.\n *\n * This function returns 0 if it succeeds, or one of the following\n * negative error codes:\n *\n * SPDYLAY_ERR_ZLIB\n *     The z_stream initialization failed.\n * SPDYLAY_ERR_UNSUPPORTED_VERSION\n *     The version is not supported.\n */\nint spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater, uint16_t version);\n\n/*\n * Deallocates any resources allocated for |deflater|.\n */\nvoid spdylay_zlib_deflate_free(spdylay_zlib *deflater);\n\n/*\n * Deallocates any resources allocated for |inflater|.\n */\nvoid spdylay_zlib_inflate_free(spdylay_zlib *inflater);\n\n/*\n * Returns the maximum length when |len| bytes of data are deflated by\n * |deflater|.\n */\nsize_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len);\n\n/*\n * Deflates data stored in |in| with length |inlen|. The output is\n * written to |out| with length |outlen|. This is not a strict\n * requirement but |outlen| should have at least\n * spdylay_zlib_deflate_hd_bound(|inlen|) bytes for successful\n * operation.\n *\n * This function returns the number of bytes outputted if it succeeds,\n * or one of the following negative error codes:\n *\n * SPDYLAY_ERR_ZLIB\n *     The deflate operation failed.\n */\nssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater,\n                                uint8_t *out, size_t outlen,\n                                const uint8_t *in, size_t inlen);\n\n/*\n * Inflates data stored in |in| with length |inlen|.  The output is\n * added to |buf|.\n *\n * This function returns the number of bytes outputted if it succeeds,\n * or one of the following negative error codes:\n *\n * SPDYLAY_ERR_ZLIB\n *     The inflate operation failed.\n *\n * SPDYLAY_ERR_NOMEM\n *     Out of memory.\n */\nssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater,\n                                spdylay_buffer* buf,\n                                const uint8_t *in, size_t inlen);\n\n#endif /* SPDYLAY_ZLIB_H */\n"
  },
  {
    "path": "m4/README",
    "content": "Empty m4 directory to make `autoreconf -i` happy.\n"
  },
  {
    "path": "makerelease.sh",
    "content": "#!/bin/sh -e\n\nTAG=$1\nPREV_TAG=$2\n\ngit checkout refs/tags/$TAG\ngit log --pretty=fuller --date=short refs/tags/$PREV_TAG..HEAD > ChangeLog\n\n./configure && \\\n    make dist-bzip2 && make dist-gzip && make dist-xz || echo \"error\"\nmake distclean\n"
  },
  {
    "path": "proxy.pac.sample",
    "content": "function FindProxyForURL(url, host) {\n  // For SPDY proxy\n  return \"HTTPS localhost:3000\";\n  // For conventional HTTP proxy\n  // return \"PROXY localhost:3000\";\n}\n"
  },
  {
    "path": "python/MANIFEST.in",
    "content": "include spdylay.pyx cspdylay.pxd spdylay_tests.py spdyclient.py spdyserv.py README.rst\n"
  },
  {
    "path": "python/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nEXTRA_DIST = MANIFEST.in README.rst cspdylay.pxd setup.py \\\n\tspdyclient.py spdylay.pyx spdylay_tests.py spdyserv.py\n"
  },
  {
    "path": "python/README.rst",
    "content": "Python-spdylay is a Python SPDY library on top of Spdylay C\nlibrary. It supports SPDY/2 and SPDY/3 protocol.\n\nIt does not perform any I/O operations. When the library needs them,\nit calls the callback functions provided by the application. It also\ndoes not include any event polling mechanism, so the application can\nfreely choose the way of handling events.\n\nIt provides almost all API Spdylay provides with Pythonic fashion.\n\nThe core library API works with Python 2 and 3.  But\n``ThreadedSPDYServer`` requires Python 3.3 because it uses TLS NPN\nextension.\n\nInstallation\n============\n\nFirst install Spdylay library. You can grab a source distribution from\n`sf.net download page\n<http://sourceforge.net/projects/spdylay/files/stable/>`_\nor `clone git repository <https://github.com/tatsuhiro-t/spdylay>`_.\n\nSee `Spdylay documentation\n<http://spdylay.sourceforge.net/package_README.html>`_ for the\nrequired packages and how to build Spdylay from git repository.\n\nAfter Spdylay is installed, run ``build_ext`` command to build\nextension module::\n\n    $ python setup.py build_ext\n\nIf you installed Spdylay library in other than standard location, use\n``--include-dirs`` and ``--library-dirs`` to specify header file and\nlibrary locations respectively.\n\nDocumentation\n=============\n\nSee `python-spdylay documentation\n<http://spdylay.sourceforge.net/python.html>`_.\n\nSamples\n=======\n\nHere is a simple SPDY server::\n\n    #!/usr/bin/env python\n\n    # The example SPDY server. Python 3.3 or later is required because TLS\n    # NPN is used in spdylay.ThreadedSPDYServer. Put private key and\n    # certificate file in the current working directory.\n\n    import spdylay\n\n    # private key file\n    KEY_FILE='server.key'\n    # certificate file\n    CERT_FILE='server.crt'\n\n    class MySPDYRequestHandler(spdylay.BaseSPDYRequestHandler):\n\n        def do_GET(self):\n            self.send_response(200)\n            self.send_header('content-type', 'text/html; charset=UTF-8')\n\n            content = '''\\\n    <html>\n    <head><title>SPDY FTW</title></head>\n    <body>\n    <h1>SPDY FTW</h1>\n    <p>The age of HTTP/1.1 is over. The time of SPDY has come.</p>\n    </body>\n    </html>'''.encode('UTF-8')\n\n            self.wfile.write(content)\n\n    if __name__ == \"__main__\":\n        HOST, PORT = \"localhost\", 3000\n\n        server = spdylay.ThreadedSPDYServer((HOST, PORT),\n                                            MySPDYRequestHandler,\n                                            cert_file=CERT_FILE,\n                                            key_file=KEY_FILE)\n        server.start()\n\nHere is a simple SPDY client::\n\n    #!/usr/bin/env python\n\n    # The example SPDY client.  You need Python 3.3 or later because we\n    # use TLS NPN.\n    #\n    # Usage: spdyclient.py URL...\n    #\n    import sys\n    import spdylay\n\n    class MyStreamHandler(spdylay.BaseSPDYStreamHandler):\n        def on_header(self, nv):\n            sys.stdout.write('Stream#{}\\n'.format(self.stream_id))\n            for k, v in nv:\n                sys.stdout.write('{}: {}\\n'.format(k, v))\n\n        def on_data(self, data):\n            sys.stdout.write('Stream#{}\\n'.format(self.stream_id))\n            sys.stdout.buffer.write(data)\n\n        def on_close(self, status_code):\n            sys.stdout.write('Stream#{} closed\\n'.format(self.stream_id))\n\n    if __name__ == '__main__':\n        uris = sys.argv[1:]\n        spdylay.urlfetch(uris, MyStreamHandler)\n"
  },
  {
    "path": "python/cspdylay.pxd",
    "content": "from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t\n\ncdef extern from 'spdylay/spdylay.h':\n\n    ctypedef enum spdylay_proto_version:\n        SPDYLAY_PROTO_SPDY2\n        SPDYLAY_PROTO_SPDY3\n\n    ctypedef enum spdylay_error:\n        SPDYLAY_ERR_INVALID_ARGUMENT\n        SPDYLAY_ERR_ZLIB\n        SPDYLAY_ERR_UNSUPPORTED_VERSION\n        SPDYLAY_ERR_WOULDBLOCK\n        SPDYLAY_ERR_PROTO\n        SPDYLAY_ERR_INVALID_FRAME\n        SPDYLAY_ERR_EOF\n        SPDYLAY_ERR_DEFERRED\n        SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE\n        SPDYLAY_ERR_STREAM_CLOSED\n        SPDYLAY_ERR_STREAM_CLOSING\n        SPDYLAY_ERR_STREAM_SHUT_WR\n        SPDYLAY_ERR_INVALID_STREAM_ID\n        SPDYLAY_ERR_INVALID_STREAM_STATE\n        SPDYLAY_ERR_DEFERRED_DATA_EXIST\n        SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED\n        SPDYLAY_ERR_GOAWAY_ALREADY_SENT\n        SPDYLAY_ERR_INVALID_HEADER_BLOCK\n        SPDYLAY_ERR_INVALID_STATE\n        SPDYLAY_ERR_GZIP\n        SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE\n        SPDYLAY_ERR_FATAL\n        SPDYLAY_ERR_NOMEM\n        SPDYLAY_ERR_CALLBACK_FAILURE\n\n    ctypedef enum spdylay_ctrl_flag:\n        SPDYLAY_CTRL_FLAG_NONE\n        SPDYLAY_CTRL_FLAG_FIN\n        SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL\n\n    ctypedef enum spdylay_data_flag:\n        SPDYLAY_DATA_FLAG_NONE\n        SPDYLAY_DATA_FLAG_FIN\n\n    ctypedef enum spdylay_frame_type:\n        SPDYLAY_SYN_STREAM\n        SPDYLAY_SYN_REPLY\n        SPDYLAY_RST_STREAM\n        SPDYLAY_SETTINGS\n        SPDYLAY_NOOP\n        SPDYLAY_PING\n        SPDYLAY_GOAWAY\n        SPDYLAY_HEADERS\n        SPDYLAY_WINDOW_UPDATE\n        SPDYLAY_CREDENTIAL\n\n    ctypedef enum spdylay_status_code:\n        SPDYLAY_OK\n        SPDYLAY_PROTOCOL_ERROR\n        SPDYLAY_INVALID_STREAM\n        SPDYLAY_REFUSED_STREAM\n        SPDYLAY_UNSUPPORTED_VERSION\n        SPDYLAY_CANCEL\n        SPDYLAY_INTERNAL_ERROR\n        SPDYLAY_FLOW_CONTROL_ERROR\n        # Following status codes were introduced in SPDY/3\n        SPDYLAY_STREAM_IN_USE\n        SPDYLAY_STREAM_ALREADY_CLOSED\n        SPDYLAY_INVALID_CREDENTIALS\n        SPDYLAY_FRAME_TOO_LARGE\n\n    # The status codes for GOAWAY, introduced in SPDY/3.\n    ctypedef enum spdylay_goaway_status_code:\n        SPDYLAY_GOAWAY_OK\n        SPDYLAY_GOAWAY_PROTOCOL_ERROR\n        SPDYLAY_GOAWAY_INTERNAL_ERROR\n\n    ctypedef enum spdylay_settings_flag:\n        SPDYLAY_FLAG_SETTINGS_NONE\n        SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS\n\n    ctypedef enum spdylay_settings_id_flag:\n        SPDYLAY_ID_FLAG_SETTINGS_NONE\n        SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE\n        SPDYLAY_ID_FLAG_SETTINGS_PERSISTED\n\n    ctypedef enum spdylay_settings_id:\n        SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH\n        SPDYLAY_SETTINGS_DOWNLOAD_BANDWIDTH\n        SPDYLAY_SETTINGS_ROUND_TRIP_TIME\n        SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS\n        SPDYLAY_SETTINGS_CURRENT_CWND\n        SPDYLAY_SETTINGS_DOWNLOAD_RETRANS_RATE\n        SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE\n        SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE\n        SPDYLAY_SETTINGS_MAX\n\n    ctypedef struct spdylay_ctrl_hd:\n        uint16_t version\n        uint16_t type\n        uint8_t flags\n        int32_t length\n\n    ctypedef struct spdylay_syn_stream:\n        spdylay_ctrl_hd hd\n        int32_t stream_id\n        int32_t assoc_stream_id\n        uint8_t pri\n        uint8_t slot\n        char **nv\n\n    ctypedef struct spdylay_syn_reply:\n        spdylay_ctrl_hd hd\n        int32_t stream_id\n        char **nv\n\n    ctypedef struct spdylay_headers:\n        spdylay_ctrl_hd hd\n        int32_t stream_id\n        char **nv\n\n    ctypedef struct spdylay_rst_stream:\n        spdylay_ctrl_hd hd\n        int32_t stream_id\n        uint32_t status_code\n\n    ctypedef struct spdylay_settings_entry:\n        int32_t settings_id\n        uint8_t flags\n        uint32_t value\n\n    ctypedef struct spdylay_settings:\n        spdylay_ctrl_hd hd\n        size_t niv\n        spdylay_settings_entry *iv\n\n    ctypedef struct spdylay_ping:\n        spdylay_ctrl_hd hd\n        uint32_t unique_id\n\n    ctypedef struct spdylay_goaway:\n        spdylay_ctrl_hd hd\n        int32_t last_good_stream_id\n        uint32_t status_code\n\n    ctypedef struct spdylay_window_update:\n        spdylay_ctrl_hd hd\n        int32_t stream_id\n        int32_t delta_window_size\n\n    ctypedef union spdylay_frame:\n        spdylay_syn_stream syn_stream\n        spdylay_syn_reply syn_reply\n        spdylay_rst_stream rst_stream\n        spdylay_settings settings\n        spdylay_ping ping\n        spdylay_goaway goaway\n        spdylay_headers headers\n        spdylay_window_update window_update\n        #spdylay_credential credential\n\n    ctypedef union spdylay_data_source:\n        int fd\n        void *ptr\n\n    ctypedef ssize_t (*spdylay_data_source_read_callback)\\\n        (spdylay_session *session, int32_t stream_id,\n         uint8_t *buf, size_t length, int *eof,\n         spdylay_data_source *source, void *user_data)\n\n    ctypedef struct spdylay_data_provider:\n        spdylay_data_source source\n        spdylay_data_source_read_callback read_callback\n\n    ctypedef struct spdylay_session:\n        pass\n\n\n    ctypedef ssize_t (*spdylay_send_callback)\\\n        (spdylay_session *session,\n         uint8_t *data, size_t length, int flags, void *user_data)\n\n    ctypedef ssize_t (*spdylay_recv_callback)\\\n        (spdylay_session *session,\n         uint8_t *buf, size_t length, int flags, void *user_data)\n\n    ctypedef void (*spdylay_on_ctrl_recv_callback)\\\n        (spdylay_session *session, spdylay_frame_type frame_type,\n         spdylay_frame *frame, void *user_data)\n\n    ctypedef void (*spdylay_on_invalid_ctrl_recv_callback)\\\n        (spdylay_session *session, spdylay_frame_type type,\n         spdylay_frame *frame, uint32_t status_code, void *user_data)\n\n    ctypedef void (*spdylay_on_data_chunk_recv_callback)\\\n        (spdylay_session *session, uint8_t flags, int32_t stream_id,\n         uint8_t *data, size_t len, void *user_data)\n\n    ctypedef void (*spdylay_on_data_recv_callback)\\\n        (spdylay_session *session, uint8_t flags, int32_t stream_id,\n         int32_t length, void *user_data)\n\n    ctypedef void (*spdylay_before_ctrl_send_callback)\\\n        (spdylay_session *session, spdylay_frame_type type,\n         spdylay_frame *frame, void *user_data)\n\n    ctypedef void (*spdylay_on_ctrl_send_callback)\\\n        (spdylay_session *session, spdylay_frame_type type,\n         spdylay_frame *frame, void *user_data)\n\n    ctypedef void (*spdylay_on_ctrl_not_send_callback)\\\n        (spdylay_session *session, spdylay_frame_type type,\n         spdylay_frame *frame, int error_code, void *user_data)\n\n    ctypedef void (*spdylay_on_data_send_callback)\\\n        (spdylay_session *session, uint8_t flags, int32_t stream_id,\n         int32_t length, void *user_data)\n\n    ctypedef void (*spdylay_on_stream_close_callback)\\\n        (spdylay_session *session, int32_t stream_id,\n         spdylay_status_code status_code, void *user_data)\n\n    ctypedef void (*spdylay_on_request_recv_callback)\\\n        (spdylay_session *session, int32_t stream_id, void *user_data)\n\n    ctypedef void (*spdylay_on_ctrl_recv_parse_error_callback)\\\n        (spdylay_session *session, spdylay_frame_type type,\n         uint8_t *head, size_t headlen, uint8_t *payload, size_t payloadlen,\n         int error_code, void *user_data)\n\n    ctypedef void (*spdylay_on_unknown_ctrl_recv_callback)\\\n        (spdylay_session *session, uint8_t *head, size_t headlen,\n         uint8_t *payload, size_t payloadlen, void *user_data)\n\n    ctypedef struct spdylay_session_callbacks:\n        spdylay_send_callback send_callback\n        spdylay_recv_callback recv_callback\n        spdylay_on_ctrl_recv_callback on_ctrl_recv_callback\n        spdylay_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback\n        spdylay_on_data_chunk_recv_callback on_data_chunk_recv_callback\n        spdylay_on_data_recv_callback on_data_recv_callback\n        spdylay_before_ctrl_send_callback before_ctrl_send_callback\n        spdylay_on_ctrl_send_callback on_ctrl_send_callback\n        spdylay_on_ctrl_not_send_callback on_ctrl_not_send_callback\n        spdylay_on_data_send_callback on_data_send_callback\n        spdylay_on_stream_close_callback on_stream_close_callback\n        spdylay_on_request_recv_callback on_request_recv_callback\n        spdylay_on_ctrl_recv_parse_error_callback \\\n            on_ctrl_recv_parse_error_callback\n        spdylay_on_unknown_ctrl_recv_callback on_unknown_ctrl_recv_callback\n\n    int spdylay_session_client_new(spdylay_session **session_ptr,\n                                   int version,\n                                   spdylay_session_callbacks *callbacks,\n                                   void *user_data)\n\n    int spdylay_session_server_new(spdylay_session **session_ptr,\n                                   int version,\n                                   spdylay_session_callbacks *callbacks,\n                                   void *user_data)\n\n    void spdylay_session_del(spdylay_session *session)\n\n\n    int spdylay_session_recv(spdylay_session *session)\n\n    ssize_t spdylay_session_mem_recv(spdylay_session *session,\n                                     uint8_t *data, size_t length)\n\n    int spdylay_session_send(spdylay_session *session)\n\n    int spdylay_session_resume_data(spdylay_session *session,\n                                    int32_t stream_id)\n\n    bint spdylay_session_want_read(spdylay_session *session)\n\n    bint spdylay_session_want_write(spdylay_session *session)\n\n    void* spdylay_session_get_stream_user_data(spdylay_session *session,\n                                               int32_t stream_id)\n\n    size_t spdylay_session_get_outbound_queue_size(spdylay_session *session)\n\n    uint8_t spdylay_session_get_pri_lowest(spdylay_session *session)\n\n    int spdylay_session_fail_session(spdylay_session *session,\n                                     uint32_t status_code)\n\n    char* spdylay_strerror(int error_code)\n\n    int spdylay_submit_request(spdylay_session *session, uint8_t pri,\n                               char **nv,\n                               spdylay_data_provider *data_prd,\n                               void *stream_user_data)\n\n    int spdylay_submit_response(spdylay_session *session,\n                                int32_t stream_id, char **nv,\n                                spdylay_data_provider *data_prd)\n\n    int spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags,\n                                  int32_t assoc_stream_id, uint8_t pri,\n                                  char **nv, void *stream_user_data)\n\n    int spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags,\n                                 int32_t stream_id, char **nv)\n\n    int spdylay_submit_headers(spdylay_session *session, uint8_t flags,\n                               int32_t stream_id, char **nv)\n\n    int spdylay_submit_data(spdylay_session *session, int32_t stream_id,\n                            uint8_t flags, spdylay_data_provider *data_prd)\n\n    int spdylay_submit_rst_stream(spdylay_session *session,\n                                  int32_t stream_id, uint32_t status_code)\n\n    int spdylay_submit_ping(spdylay_session *session)\n\n    int spdylay_submit_goaway(spdylay_session *session, uint32_t status_code)\n\n    int spdylay_submit_settings(spdylay_session *session, uint8_t flags,\n                                spdylay_settings_entry *iv, size_t niv)\n\n    int spdylay_submit_window_update(spdylay_session *session,\n                                     int32_t stream_id,\n                                     int32_t delta_window_size)\n\n    ctypedef struct spdylay_npn_proto:\n        unsigned char *proto\n        uint8_t len\n        uint16_t version\n\n    spdylay_npn_proto* spdylay_npn_get_proto_list(size_t *len_ptr)\n\n    uint16_t spdylay_npn_get_version(unsigned char *proto,\n                                     size_t protolen)\n"
  },
  {
    "path": "python/setup.py",
    "content": "from distutils.core import setup\nfrom distutils.extension import Extension\n\nsetup(\n    name = 'python-spdylay',\n    # Also update __version__ in spdylay.pyx\n    version = '0.1.2',\n    description = 'Python SPDY library on top of Spdylay C library',\n    author = 'Tatsuhiro Tsujikawa',\n    author_email = 'tatsuhiro.t@gmail.com',\n    url = 'http://spdylay.sourceforge.net/',\n    keywords = [],\n    ext_modules = [Extension(\"spdylay\",\n                             [\"spdylay.c\"],\n                             libraries=['spdylay'])],\n    long_description=\"\"\"\\\nPython-spdylay is a Python SPDY library on top of Spdylay C\nlibrary. It supports SPDY/2 and SPDY/3 protocol.\n\nIt does not perform any I/O operations. When the library needs them,\nit calls the callback functions provided by the application. It also\ndoes not include any event polling mechanism, so the application can\nfreely choose the way of handling events.\n\nIt provides almost all API Spdylay provides with Pythonic fashion.\n\nThe core library API works with Python 2 and 3.  But\n``ThreadedSPDYServer`` requires Python 3.3 because it uses TLS NPN\nextension.\n\nInstallation\n============\n\nFirst install Spdylay library. You can grab a source distribution from\n`sf.net download page\n<http://sourceforge.net/projects/spdylay/files/stable/>`_\nor `clone git repository <https://github.com/tatsuhiro-t/spdylay>`_.\n\nSee `Spdylay documentation\n<http://spdylay.sourceforge.net/package_README.html>`_ for the\nrequired packages and how to build Spdylay from git repository.\n\nAfter Spdylay is installed, run ``build_ext`` command to build\nextension module::\n\n    $ python setup.py build_ext\n\nIf you installed Spdylay library in other than standard location, use\n``--include-dirs`` and ``--library-dirs`` to specify header file and\nlibrary locations respectively.\n\nDocumentation\n=============\n\nSee `python-spdylay documentation\n<http://spdylay.sourceforge.net/python.html>`_.\n\nSamples\n=======\n\nHere is a simple SPDY server::\n\n    #!/usr/bin/env python\n\n    # The example SPDY server. Python 3.3 or later is required because TLS\n    # NPN is used in spdylay.ThreadedSPDYServer. Put private key and\n    # certificate file in the current working directory.\n\n    import spdylay\n\n    # private key file\n    KEY_FILE='server.key'\n    # certificate file\n    CERT_FILE='server.crt'\n\n    class MySPDYRequestHandler(spdylay.BaseSPDYRequestHandler):\n\n        def do_GET(self):\n            self.send_response(200)\n            self.send_header('content-type', 'text/html; charset=UTF-8')\n\n            content = '''\\\\\n    <html>\n    <head><title>SPDY FTW</title></head>\n    <body>\n    <h1>SPDY FTW</h1>\n    <p>The age of HTTP/1.1 is over. The time of SPDY has come.</p>\n    </body>\n    </html>'''.encode('UTF-8')\n\n            self.wfile.write(content)\n\n    if __name__ == \"__main__\":\n        HOST, PORT = \"localhost\", 3000\n\n        server = spdylay.ThreadedSPDYServer((HOST, PORT),\n                                            MySPDYRequestHandler,\n                                            cert_file=CERT_FILE,\n                                            key_file=KEY_FILE)\n        server.start()\n\nHere is a simple SPDY client::\n\n    #!/usr/bin/env python\n\n    # The example SPDY client.  You need Python 3.3 or later because we\n    # use TLS NPN.\n    #\n    # Usage: spdyclient.py URL...\n    #\n    import sys\n    import spdylay\n\n    class MyStreamHandler(spdylay.BaseSPDYStreamHandler):\n        def on_header(self, nv):\n            sys.stdout.write('Stream#{}\\\\n'.format(self.stream_id))\n            for k, v in nv:\n                sys.stdout.write('{}: {}\\\\n'.format(k, v))\n\n        def on_data(self, data):\n            sys.stdout.write('Stream#{}\\\\n'.format(self.stream_id))\n            sys.stdout.buffer.write(data)\n\n        def on_close(self, status_code):\n            sys.stdout.write('Stream#{} closed\\\\n'.format(self.stream_id))\n\n    if __name__ == '__main__':\n        uris = sys.argv[1:]\n        spdylay.urlfetch(uris, MyStreamHandler)\n\"\"\",\n    classifiers = [\n        'Development Status :: 4 - Beta',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Operating System :: OS Independent',\n        'Programming Language :: Cython',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: 3',\n        'Topic :: Internet :: WWW/HTTP',\n        'Topic :: Software Development :: Libraries :: Python Modules'\n        ]\n    )\n"
  },
  {
    "path": "python/spdyclient.py",
    "content": "#!/usr/bin/env python\n\n# The example SPDY client.  You need Python 3.3 or later because we\n# use TLS NPN.\n#\n# Usage: spdyclient.py URL...\n#\nimport sys\nimport spdylay\n\nclass MyStreamHandler(spdylay.BaseSPDYStreamHandler):\n    def on_header(self, nv):\n        sys.stdout.write('Stream#{}\\n'.format(self.stream_id))\n        for k, v in nv:\n            sys.stdout.write('{}: {}\\n'.format(k, v))\n\n    def on_data(self, data):\n        sys.stdout.write('Stream#{}\\n'.format(self.stream_id))\n        sys.stdout.buffer.write(data)\n\n    def on_close(self, status_code):\n        sys.stdout.write('Stream#{} closed\\n'.format(self.stream_id))\n\nif __name__ == '__main__':\n    uris = sys.argv[1:]\n    spdylay.urlfetch(uris, MyStreamHandler)\n"
  },
  {
    "path": "python/spdylay.pyx",
    "content": "cimport cspdylay\n\nfrom libc.stdlib cimport malloc, free\nfrom libc.string cimport memcpy, memset\nfrom libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t\n\n# Also update version in setup.py\n__version__ = '0.1.2'\n\nclass EOFError(Exception):\n    pass\n\nclass CallbackFailureError(Exception):\n    pass\n\nclass TemporalCallbackFailureError(Exception):\n    pass\n\nclass InvalidArgumentError(Exception):\n    pass\n\nclass ZlibError(Exception):\n    pass\n\nclass UnsupportedVersionError(Exception):\n    pass\n\nclass StreamClosedError(Exception):\n    pass\n\nclass DataProvider:\n    def __init__(self, source, read_cb):\n        self.source = source\n        self.read_cb = read_cb\n\ncdef class CtrlFrame:\n    cdef uint16_t version\n    cdef uint16_t frame_type\n    cdef uint8_t flags\n    cdef int32_t length\n\n    cdef void fillhd(self, cspdylay.spdylay_ctrl_hd *hd):\n        self.version = hd.version\n        self.frame_type = hd.type\n        self.flags = hd.flags\n        self.length = hd.length\n\n    property version:\n        def __get__(self):\n            return self.version\n\n    property frame_type:\n        def __get__(self):\n            return self.frame_type\n\n    property flags:\n        def __get__(self):\n            return self.flags\n\n    property length:\n        def __get__(self):\n            return self.length\n\ncdef class SynStreamFrame(CtrlFrame):\n    cdef int32_t stream_id\n    cdef int32_t assoc_stream_id\n    cdef uint8_t pri\n    cdef uint8_t slot\n    cdef object nv\n\n    cdef fill(self, cspdylay.spdylay_syn_stream *frame):\n        self.fillhd(&frame.hd)\n\n        self.stream_id = frame.stream_id\n        self.assoc_stream_id = frame.assoc_stream_id\n        self.pri = frame.pri\n        self.slot = frame.slot\n        self.nv = cnv2pynv(frame.nv)\n\n    property stream_id:\n        def __get__(self):\n            return self.stream_id\n\n    property assoc_stream_id:\n        def __get__(self):\n            return self.assoc_stream_id\n\n    property pri:\n        def __get__(self):\n            return self.pri\n\n    property nv:\n        def __get__(self):\n            return self.nv\n\ncdef class SynReplyFrame(CtrlFrame):\n    cdef int32_t stream_id\n    cdef object nv\n\n    cdef fill(self, cspdylay.spdylay_syn_reply *frame):\n        self.fillhd(&frame.hd)\n\n        self.stream_id = frame.stream_id\n        self.nv = cnv2pynv(frame.nv)\n\n    property stream_id:\n        def __get__(self):\n            return self.stream_id\n    property nv:\n        def __get__(self):\n            return self.nv\n\ncdef class HeadersFrame(CtrlFrame):\n    cdef int32_t stream_id\n    cdef object nv\n\n    cdef fill(self, cspdylay.spdylay_headers *frame):\n        self.fillhd(&frame.hd)\n\n        self.stream_id = frame.stream_id\n        self.nv = cnv2pynv(frame.nv)\n\n    property stream_id:\n        def __get__(self):\n            return self.stream_id\n\n    property nv:\n        def __get__(self):\n            return self.nv\n\ncdef class RstStreamFrame(CtrlFrame):\n    cdef int32_t stream_id\n    cdef uint32_t status_code\n\n    cdef fill(self, cspdylay.spdylay_rst_stream *frame):\n        self.fillhd(&frame.hd)\n\n        self.stream_id = frame.stream_id\n        self.status_code = frame.status_code\n\n    property stream_id:\n        def __get__(self):\n            return self.stream_id\n\n    property status_code:\n        def __get__(self):\n            return self.status_code\n\ncdef class SettingsFrame(CtrlFrame):\n    cdef object iv\n\n    cdef fill(self, cspdylay.spdylay_settings *frame):\n        self.fillhd(&frame.hd)\n\n        self.iv = csettings2pysettings(frame.niv, frame.iv)\n\n\n    property iv:\n        def __get__(self):\n            return self.iv\n\ncdef class PingFrame(CtrlFrame):\n    cdef uint32_t unique_id\n\n    cdef fill(self, cspdylay.spdylay_ping *frame):\n        self.fillhd(&frame.hd)\n\n        self.unique_id = frame.unique_id\n\n    property unique_id:\n        def __get__(self):\n            return self.unique_id\n\ncdef class GoawayFrame(CtrlFrame):\n    cdef int32_t last_good_stream_id\n    cdef uint32_t status_code\n\n    cdef fill(self, cspdylay.spdylay_goaway *frame):\n        self.fillhd(&frame.hd)\n\n        self.last_good_stream_id = frame.last_good_stream_id\n        self.status_code = frame.status_code\n\n    property last_good_stream_id:\n        def __get__(self):\n            return self.last_good_stream_id\n\n    property status_code:\n        def __get__(self):\n            return self.status_code\n\ncdef class WindowUpdateFrame(CtrlFrame):\n    cdef int32_t stream_id\n    cdef int32_t delta_window_size\n\n    cdef fill(self, cspdylay.spdylay_window_update *frame):\n        self.fillhd(&frame.hd)\n\n        self.stream_id = frame.stream_id\n        self.delta_window_size = frame.delta_window_size\n\n    property stream_id:\n        def __get__(self):\n            return self.stream_id\n\n    property delta_window_size:\n        def __get__(self):\n            return self.delta_window_size\n\ncdef cnv2pynv(char **nv):\n    ''' Convert C-style name/value pairs ``nv`` to Python style\n    pairs. We assume that strings in nv is UTF-8 encoded as per SPDY\n    spec. In Python pairs, we use unicode string.'''\n    cdef size_t i\n    pynv = []\n    i = 0\n    while nv[i] != NULL:\n        pynv.append((nv[i].decode('UTF-8'), nv[i+1].decode('UTF-8')))\n        i += 2\n    return pynv\n\ncdef char** pynv2cnv(object nv) except *:\n    ''' Convert Python style UTF-8 name/value pairs ``nv`` to C-style\n    pairs. Python style name/value pairs are list of tuple (key,\n    value).'''\n    cdef char **cnv = <char**>malloc((len(nv)*2+1)*sizeof(char*))\n    cdef size_t i\n    if cnv == NULL:\n        raise MemoryError()\n    i = 0\n    for n, v in nv:\n        cnv[i] = n\n        i += 1\n        cnv[i] = v\n        i += 1\n    cnv[i] = NULL\n    return cnv\n\ncdef pynv_encode(nv):\n    res = []\n    for k, v in nv:\n        res.append((k.encode('UTF-8'), v.encode('UTF-8')))\n    return res\n\ncdef object csettings2pysettings(size_t niv,\n                                 cspdylay.spdylay_settings_entry *iv):\n    cdef size_t i = 0\n    cdef cspdylay.spdylay_settings_entry *ent\n    res = []\n    while i < niv:\n        ent = &iv[i]\n        res.append((ent.settings_id, ent.flags, ent.value))\n        i += 1\n    return res\n\ncdef cspdylay.spdylay_settings_entry* pysettings2csettings(object iv) except *:\n    cdef size_t i\n    cdef cspdylay.spdylay_settings_entry *civ =\\\n        <cspdylay.spdylay_settings_entry*>malloc(\\\n        len(iv)*sizeof(cspdylay.spdylay_settings_entry))\n    if civ == NULL:\n        raise MemoryError()\n    i = 0\n    for settings_id, flags, value in iv:\n        civ[i].settings_id = settings_id\n        civ[i].flags = flags\n        civ[i].value = value\n        i += 1\n    return civ\n\ncdef cspdylay.spdylay_data_provider create_c_data_prd\\\n(cspdylay.spdylay_data_provider *cdata_prd, object pydata_prd):\n    cdata_prd.source.ptr = <void*>pydata_prd\n    cdata_prd.read_callback = read_callback\n\n\ncdef object cframe2pyframe(cspdylay.spdylay_frame_type frame_type,\n                           cspdylay.spdylay_frame *frame):\n    cdef SynStreamFrame syn_stream\n    cdef SynReplyFrame syn_reply\n    cdef HeadersFrame headers\n    cdef RstStreamFrame rst_stream\n    cdef SettingsFrame settings\n    cdef PingFrame ping\n    cdef GoawayFrame goaway\n    cdef WindowUpdateFrame window_update\n    cdef object pyframe = None\n    if frame_type == cspdylay.SPDYLAY_SYN_STREAM:\n        syn_stream = SynStreamFrame()\n        syn_stream.fill(&frame.syn_stream)\n        pyframe = syn_stream\n    elif frame_type == cspdylay.SPDYLAY_SYN_REPLY:\n        syn_reply = SynReplyFrame()\n        syn_reply.fill(&frame.syn_reply)\n        pyframe = syn_reply\n    elif frame_type == cspdylay.SPDYLAY_HEADERS:\n        headers = HeadersFrame()\n        headers.fill(&frame.headers)\n        pyframe = headers\n    elif frame_type == cspdylay.SPDYLAY_RST_STREAM:\n        rst_stream = RstStreamFrame()\n        rst_stream.fill(&frame.rst_stream)\n        pyframe = rst_stream\n    elif frame_type == cspdylay.SPDYLAY_SETTINGS:\n        settings = SettingsFrame()\n        settings.fill(&frame.settings)\n        pyframe = settings\n    elif frame_type == cspdylay.SPDYLAY_PING:\n        ping = PingFrame()\n        ping.fill(&frame.ping)\n        pyframe = ping\n    elif frame_type == cspdylay.SPDYLAY_GOAWAY:\n        goaway = GoawayFrame()\n        goaway.fill(&frame.goaway)\n        pyframe = goaway\n    elif frame_type == cspdylay.SPDYLAY_WINDOW_UPDATE:\n        window_update = WindowUpdateFrame()\n        window_update.fill(&frame.window_update)\n        pyframe = window_update\n    return pyframe\n\ncdef void _call_frame_callback(Session pysession,\n                               cspdylay.spdylay_frame_type frame_type,\n                               cspdylay.spdylay_frame *frame,\n                               object callback):\n    if not callback:\n        return\n    try:\n        pyframe = cframe2pyframe(frame_type, frame)\n        if pyframe:\n            callback(pysession, pyframe)\n    except Exception as e:\n        pysession.error = e\n    except BaseException as e:\n        pysession.base_error = e\n\ncdef void on_ctrl_recv_callback(cspdylay.spdylay_session *session,\n                                cspdylay.spdylay_frame_type frame_type,\n                                cspdylay.spdylay_frame *frame,\n                                void *user_data):\n    cdef Session pysession = <Session>user_data\n    _call_frame_callback(pysession, frame_type, frame,\n                         pysession.on_ctrl_recv_cb)\n\ncdef void on_invalid_ctrl_recv_callback(cspdylay.spdylay_session *session,\n                                        cspdylay.spdylay_frame_type frame_type,\n                                        cspdylay.spdylay_frame *frame,\n                                        uint32_t status_code,\n                                        void *user_data):\n    cdef Session pysession = <Session>user_data\n\n    if not pysession.on_invalid_ctrl_recv_cb:\n        return\n    try:\n        pyframe = cframe2pyframe(frame_type, frame)\n        if pyframe:\n            pysession.on_invalid_ctrl_recv_cb(pysession, pyframe, status_code)\n    except Exception as e:\n        pysession.error = e\n    except BaseException as e:\n        pysession.base_error = e\n\ncdef void before_ctrl_send_callback(cspdylay.spdylay_session *session,\n                                    cspdylay.spdylay_frame_type frame_type,\n                                    cspdylay.spdylay_frame *frame,\n                                    void *user_data):\n    cdef Session pysession = <Session>user_data\n    _call_frame_callback(pysession, frame_type, frame,\n                         pysession.before_ctrl_send_cb)\n\ncdef void on_ctrl_send_callback(cspdylay.spdylay_session *session,\n                                cspdylay.spdylay_frame_type frame_type,\n                                cspdylay.spdylay_frame *frame,\n                                void *user_data):\n    cdef Session pysession = <Session>user_data\n    _call_frame_callback(pysession, frame_type, frame,\n                         pysession.on_ctrl_send_cb)\n\ncdef void on_ctrl_not_send_callback(cspdylay.spdylay_session *session,\n                                    cspdylay.spdylay_frame_type frame_type,\n                                    cspdylay.spdylay_frame *frame,\n                                    int error_code,\n                                    void *user_data):\n    cdef Session pysession = <Session>user_data\n\n    if not pysession.on_ctrl_not_send_cb:\n        return\n    try:\n        pyframe = cframe2pyframe(frame_type, frame)\n        if pyframe:\n            pysession.on_ctrl_not_send_cb(pysession, pyframe, error_code)\n    except Exception as e:\n        pysession.error = e\n    except BaseException as e:\n        pysession.base_error = e\n\ncdef void on_ctrl_recv_parse_error_callback(\\\n    cspdylay.spdylay_session *session,\n    cspdylay.spdylay_frame_type frame_type,\n    uint8_t *head, size_t headlen,\n    uint8_t *payload, size_t payloadlen,\n    int error_code, void *user_data):\n    cdef Session pysession = <Session>user_data\n\n    if not pysession.on_ctrl_recv_parse_error_cb:\n        return\n    try:\n        pysession.on_ctrl_recv_parse_error_cb(pysession, frame_type,\n                                              (<char*>head)[:headlen],\n                                              (<char*>payload)[:payloadlen],\n                                              error_code)\n    except Exception as e:\n        pysession.error = e\n    except BaseException as e:\n        pysession.base_error = e\n\ncdef void on_unknown_ctrl_recv_callback(cspdylay.spdylay_session *session,\n                                        uint8_t *head, size_t headlen,\n                                        uint8_t *payload, size_t payloadlen,\n                                        void *user_data):\n    cdef Session pysession = <Session>user_data\n\n    if not pysession.on_unknown_ctrl_recv_cb:\n        return\n    try:\n        pysession.on_unknown_ctrl_recv_cb(pysession,\n                                          (<char*>head)[:headlen],\n                                          (<char*>payload)[:payloadlen])\n    except Exception as e:\n        pysession.error = e\n    except BaseException as e:\n        pysession.base_error = e\n\ncdef ssize_t recv_callback(cspdylay.spdylay_session *session,\n                           uint8_t *buf, size_t length,\n                           int flags, void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.recv_callback:\n        try:\n            data = pysession.recv_callback(pysession, length)\n        except EOFError as e:\n            pysession.error = e\n            return cspdylay.SPDYLAY_ERR_EOF\n        except CallbackFailureError as e:\n            pysession.error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        except Exception as e:\n            pysession.error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        except BaseException as e:\n            pysession.base_error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        if data:\n            if len(data) > length:\n                return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n            memcpy(buf, <char*>data, len(data))\n            return len(data)\n        else:\n            return cspdylay.SPDYLAY_ERR_WOULDBLOCK\n    else:\n        return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n\ncdef ssize_t send_callback(cspdylay.spdylay_session *session,\n                           uint8_t *data, size_t length, int flags,\n                           void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.send_callback:\n        try:\n            rv = pysession.send_callback(pysession, (<char*>data)[:length])\n        except CallbackFailureError as e:\n            pysession.error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        except Exception as e:\n            pysession.error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        except BaseException as e:\n            pysession.base_error = e\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n\n        if rv:\n            return rv\n        else:\n            return cspdylay.SPDYLAY_ERR_WOULDBLOCK\n    else:\n        # If no send_callback is given, pretend all data were sent and\n        # just return length\n        return length\n\ncdef void on_data_chunk_recv_callback(cspdylay.spdylay_session *session,\n                                      uint8_t flags, int32_t stream_id,\n                                      uint8_t *data, size_t length,\n                                      void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.on_data_chunk_recv_cb:\n        try:\n            pysession.on_data_chunk_recv_cb(pysession, flags, stream_id,\n                                            (<char*>data)[:length])\n        except Exception as e:\n            pysession.error = e\n        except BaseException as e:\n            pysession.base_error = e\n\ncdef void on_data_recv_callback(cspdylay.spdylay_session *session,\n                                uint8_t flags, int32_t stream_id,\n                                int32_t length, void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.on_data_recv_cb:\n        try:\n            pysession.on_data_recv_cb(pysession, flags, stream_id, length)\n        except Exception as e:\n            pysession.error = e\n        except BaseException as e:\n            pysession.base_error = e\n\ncdef void on_data_send_callback(cspdylay.spdylay_session *session,\n                                uint8_t flags, int32_t stream_id,\n                                int32_t length, void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.on_data_send_cb:\n        try:\n            pysession.on_data_send_cb(pysession, flags, stream_id, length)\n        except Exception as e:\n            pysession.error = e\n        except BaseException as e:\n            pysession.base_error = e\n\ncdef void on_stream_close_callback(cspdylay.spdylay_session *session,\n                                   int32_t stream_id,\n                                   cspdylay.spdylay_status_code status_code,\n                                   void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.on_stream_close_cb:\n        try:\n            pysession.on_stream_close_cb(pysession, stream_id, status_code)\n        except Exception as e:\n            pysession.error = e\n        except BaseException as e:\n            pysession.base_error = e\n\ncdef void on_request_recv_callback(cspdylay.spdylay_session *session,\n                                   int32_t stream_id,\n                                   void *user_data):\n    cdef Session pysession = <Session>user_data\n    if pysession.on_request_recv_cb:\n        try:\n            pysession.on_request_recv_cb(pysession, stream_id)\n        except Exception as e:\n            pysession.error = e\n        except BaseException as e:\n            pysession.base_error = e\n\ncdef class ReadCtrl:\n    cdef int flags\n\n    def __cinit__(self):\n        self.flags = 0\n\n    property flags:\n        def __set__(self, value):\n            self.flags = value\n\ncdef ssize_t read_callback(cspdylay.spdylay_session *session,\n                           int32_t stream_id, uint8_t *buf, size_t length,\n                           int *eof, cspdylay.spdylay_data_source *source,\n                           void *user_data):\n    cdef Session pysession = <Session>user_data\n    cdef ReadCtrl read_ctrl = ReadCtrl()\n    data_prd = <object>source.ptr\n\n    try:\n        res = data_prd.read_cb(pysession, stream_id, length, read_ctrl,\n                               data_prd.source)\n    except TemporalCallbackFailureError as e:\n        return cspdylay.SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE\n    except CallbackFailureError as e:\n        pysession.error = e\n        return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n    except Exception as e:\n        pysession.error = e\n        return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n    except BaseException as e:\n        pysession.base_error = e\n        return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n\n    if read_ctrl.flags & READ_EOF:\n        eof[0] = 1\n    if res == cspdylay.SPDYLAY_ERR_DEFERRED:\n        return res\n    elif res:\n        if len(res) > length:\n            return cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n        memcpy(buf, <char*>res, len(res))\n        return len(res)\n    else:\n        return 0\n\ncdef class Session:\n    cdef cspdylay.spdylay_session *_c_session\n    cdef object recv_callback\n    cdef object send_callback\n    cdef object on_ctrl_recv_cb\n    cdef object on_invalid_ctrl_recv_cb\n    cdef object on_data_chunk_recv_cb\n    cdef object on_data_recv_cb\n    cdef object before_ctrl_send_cb\n    cdef object on_ctrl_send_cb\n    cdef object on_ctrl_not_send_cb\n    cdef object on_data_send_cb\n    cdef object on_stream_close_cb\n    cdef object on_request_recv_cb\n    cdef object on_ctrl_recv_parse_error_cb\n    cdef object on_unknown_ctrl_recv_cb\n    cdef object user_data\n\n    cdef object error\n    cdef object base_error\n\n    property user_data:\n        def __get__(self):\n            return self.user_data\n\n    def __cinit__(self, side, version, config=None,\n                  send_cb=None, recv_cb=None,\n                  on_ctrl_recv_cb=None,\n                  on_invalid_ctrl_recv_cb=None,\n                  on_data_chunk_recv_cb=None,\n                  on_data_recv_cb=None,\n                  before_ctrl_send_cb=None,\n                  on_ctrl_send_cb=None,\n                  on_ctrl_not_send_cb=None,\n                  on_data_send_cb=None,\n                  on_stream_close_cb=None,\n                  on_request_recv_cb=None,\n                  on_ctrl_recv_parse_error_cb=None,\n                  on_unknown_ctrl_recv_cb=None,\n                  user_data=None):\n        cdef cspdylay.spdylay_session_callbacks c_session_callbacks\n        cdef int rv\n        self._c_session = NULL\n        memset(&c_session_callbacks, 0, sizeof(c_session_callbacks))\n        c_session_callbacks.recv_callback = \\\n            <cspdylay.spdylay_recv_callback>recv_callback\n        c_session_callbacks.send_callback = \\\n            <cspdylay.spdylay_send_callback>send_callback\n        c_session_callbacks.on_ctrl_recv_callback = \\\n            <cspdylay.spdylay_on_ctrl_recv_callback>on_ctrl_recv_callback\n        c_session_callbacks.on_invalid_ctrl_recv_callback = \\\n            <cspdylay.spdylay_on_invalid_ctrl_recv_callback>\\\n            on_invalid_ctrl_recv_callback\n        c_session_callbacks.on_data_chunk_recv_callback = \\\n            <cspdylay.spdylay_on_data_chunk_recv_callback>\\\n            on_data_chunk_recv_callback\n        c_session_callbacks.on_data_recv_callback = \\\n            <cspdylay.spdylay_on_data_recv_callback>on_data_recv_callback\n        c_session_callbacks.before_ctrl_send_callback = \\\n            <cspdylay.spdylay_before_ctrl_send_callback>\\\n            before_ctrl_send_callback\n        c_session_callbacks.on_ctrl_send_callback = \\\n            <cspdylay.spdylay_on_ctrl_send_callback>on_ctrl_send_callback\n        c_session_callbacks.on_ctrl_not_send_callback = \\\n            <cspdylay.spdylay_on_ctrl_not_send_callback>\\\n            on_ctrl_not_send_callback\n        c_session_callbacks.on_data_send_callback = \\\n            <cspdylay.spdylay_on_data_send_callback>on_data_send_callback\n        c_session_callbacks.on_stream_close_callback = \\\n            <cspdylay.spdylay_on_stream_close_callback>on_stream_close_callback\n        c_session_callbacks.on_request_recv_callback = \\\n            <cspdylay.spdylay_on_request_recv_callback>on_request_recv_callback\n        # c_session_callbacks.get_credential_proof = NULL\n        # c_session_callbacks.get_credential_ncerts = NULL\n        # c_session_callbacks.get_credential_cert = NULL\n        c_session_callbacks.on_ctrl_recv_parse_error_callback = \\\n            <cspdylay.spdylay_on_ctrl_recv_parse_error_callback>\\\n            on_ctrl_recv_parse_error_callback\n        c_session_callbacks.on_unknown_ctrl_recv_callback = \\\n            <cspdylay.spdylay_on_unknown_ctrl_recv_callback>\\\n            on_unknown_ctrl_recv_callback\n\n        self.recv_callback = recv_cb\n        self.send_callback = send_cb\n        self.on_ctrl_recv_cb = on_ctrl_recv_cb\n        self.on_invalid_ctrl_recv_cb = on_invalid_ctrl_recv_cb\n        self.on_data_chunk_recv_cb = on_data_chunk_recv_cb\n        self.on_data_recv_cb = on_data_recv_cb\n        self.before_ctrl_send_cb = before_ctrl_send_cb\n        self.on_ctrl_send_cb = on_ctrl_send_cb\n        self.on_ctrl_not_send_cb = on_ctrl_not_send_cb\n        self.on_data_send_cb = on_data_send_cb\n        self.on_stream_close_cb = on_stream_close_cb\n        self.on_request_recv_cb = on_request_recv_cb\n        self.on_ctrl_recv_parse_error_cb = on_ctrl_recv_parse_error_cb\n        self.on_unknown_ctrl_recv_cb = on_unknown_ctrl_recv_cb\n\n        self.user_data = user_data\n\n        if side == CLIENT:\n            rv = cspdylay.spdylay_session_client_new(&self._c_session,\n                                                      version,\n                                                      &c_session_callbacks,\n                                                      <void*>self)\n        elif side == SERVER:\n            rv = cspdylay.spdylay_session_server_new(&self._c_session,\n                                                      version,\n                                                      &c_session_callbacks,\n                                                      <void*>self)\n        else:\n            raise InvalidArgumentError('side must be either CLIENT or SERVER')\n\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n        elif rv == cspdylay.SPDYLAY_ERR_ZLIB:\n            raise ZlibError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_UNSUPPORTED_VERSION:\n            raise UnsupportedVersionError(_strerror(rv))\n\n    def __init__(self, side, version, config=None,\n                 send_cb=None, recv_cb=None,\n                 on_ctrl_recv_cb=None,\n                 on_invalid_ctrl_recv_cb=None,\n                 on_data_chunk_recv_cb=None,\n                 on_data_recv_cb=None,\n                 before_ctrl_send_cb=None,\n                 on_ctrl_send_cb=None,\n                 on_ctrl_not_send_cb=None,\n                 on_data_send_cb=None,\n                 on_stream_close_cb=None,\n                 on_request_recv_cb=None,\n                 on_ctrl_recv_parse_error_cb=None,\n                 user_data=None):\n        pass\n\n    def __dealloc__(self):\n        cspdylay.spdylay_session_del(self._c_session)\n\n    cpdef recv(self, data=None):\n        cdef int rv\n        cdef char *c_data\n        self.error = self.base_error = None\n        if data is None:\n            rv = cspdylay.spdylay_session_recv(self._c_session)\n        else:\n            c_data = data\n            rv = cspdylay.spdylay_session_mem_recv(self._c_session,\n                                                   <uint8_t*>c_data, len(data))\n        if self.base_error:\n            raise self.base_error\n        if self.error:\n            raise self.error\n\n        if rv >= 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_EOF:\n            raise EOFError()\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n        elif rv == cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE:\n            raise CallbackFailureError()\n\n    cpdef send(self):\n        cdef int rv\n        self.error = self.base_error = None\n        rv = cspdylay.spdylay_session_send(self._c_session)\n        if self.base_error:\n            raise self.base_error\n        elif self.error:\n            raise self.error\n\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n        elif rv == cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE:\n            raise CallbackFailureError()\n\n    cpdef resume_data(self, stream_id):\n        cpdef int rv\n        rv = cspdylay.spdylay_session_resume_data(self._c_session, stream_id)\n        if rv == 0:\n            return True\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            return False\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef want_read(self):\n        return cspdylay.spdylay_session_want_read(self._c_session)\n\n    cpdef want_write(self):\n        return cspdylay.spdylay_session_want_write(self._c_session)\n\n    cpdef get_stream_user_data(self, stream_id):\n        return <object>cspdylay.spdylay_session_get_stream_user_data(\\\n            self._c_session, stream_id)\n\n    cpdef get_outbound_queue_size(self):\n        return cspdylay.spdylay_session_get_outbound_queue_size(\\\n            self._c_session)\n\n    cpdef get_pri_lowest(self):\n        return cspdylay.spdylay_session_get_pri_lowest(self._c_session)\n\n\n    cpdef fail_session(self, status_code):\n        cdef int rv\n        rv = cspdylay.spdylay_session_fail_session(self._c_session,\n                                                   status_code)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_request(self, pri, nv, data_prd=None, stream_user_data=None):\n        cdef cspdylay.spdylay_data_provider c_data_prd\n        cdef cspdylay.spdylay_data_provider *c_data_prd_ptr\n        cpdef int rv\n        cdef char **cnv\n        nv = pynv_encode(nv)\n        cnv = pynv2cnv(nv)\n        if data_prd:\n            create_c_data_prd(&c_data_prd, data_prd)\n            c_data_prd_ptr = &c_data_prd\n        else:\n            c_data_prd_ptr = NULL\n\n        rv = cspdylay.spdylay_submit_request(self._c_session, pri, cnv,\n                                             c_data_prd_ptr,\n                                             <void*>stream_user_data)\n        free(cnv)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_response(self, stream_id, nv, data_prd=None):\n        cdef cspdylay.spdylay_data_provider c_data_prd\n        cdef cspdylay.spdylay_data_provider *c_data_prd_ptr\n        cpdef int rv\n        cdef char **cnv\n        nv = pynv_encode(nv)\n        cnv = pynv2cnv(nv)\n        if data_prd:\n            create_c_data_prd(&c_data_prd, data_prd)\n            c_data_prd_ptr = &c_data_prd\n        else:\n            c_data_prd_ptr = NULL\n\n        rv = cspdylay.spdylay_submit_response(self._c_session, stream_id,\n                                              cnv, c_data_prd_ptr)\n        free(cnv)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_syn_stream(self, flags, pri, nv, assoc_stream_id=0,\n                            stream_user_data=None):\n        cdef int rv\n        cdef char **cnv\n        nv = pynv_encode(nv)\n        cnv = pynv2cnv(nv)\n        rv = cspdylay.spdylay_submit_syn_stream(self._c_session,\n                                                flags,\n                                                assoc_stream_id,\n                                                pri,\n                                                cnv,\n                                                <void*>stream_user_data)\n        free(cnv)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_syn_reply(self, flags, stream_id, nv):\n        cdef int rv\n        cdef char **cnv\n        nv = pynv_encode(nv)\n        cnv = pynv2cnv(nv)\n        rv = cspdylay.spdylay_submit_syn_reply(self._c_session,\n                                               flags, stream_id, cnv)\n        free(cnv)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_headers(self, flags, stream_id, nv):\n        cdef int rv\n        cdef char **cnv\n        nv = pynv_encode(nv)\n        cnv = pynv2cnv(nv)\n        rv = cspdylay.spdylay_submit_headers(self._c_session,\n                                             flags, stream_id, cnv)\n        free(cnv)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_data(self, stream_id, flags, data_prd):\n        cdef cspdylay.spdylay_data_provider c_data_prd\n        cdef cspdylay.spdylay_data_provider *c_data_prd_ptr\n        cpdef int rv\n        if data_prd:\n            create_c_data_prd(&c_data_prd, data_prd)\n            c_data_prd_ptr = &c_data_prd\n        else:\n            c_data_prd_ptr = NULL\n\n        rv = cspdylay.spdylay_submit_data(self._c_session, stream_id,\n                                          flags, c_data_prd_ptr)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_rst_stream(self, stream_id, status_code):\n        cdef int rv\n        rv = cspdylay.spdylay_submit_rst_stream(self._c_session, stream_id,\n                                                status_code)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_ping(self):\n        cdef int rv\n        rv = cspdylay.spdylay_submit_ping(self._c_session)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_goaway(self, status_code):\n        cdef int rv\n        rv = cspdylay.spdylay_submit_goaway(self._c_session, status_code)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_window_update(self, stream_id, delta_window_size):\n        cdef int rv\n        rv = cspdylay.spdylay_submit_window_update(self._c_session, stream_id,\n                                                   delta_window_size)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError()\n        elif rv == cspdylay.SPDYLAY_ERR_STREAM_CLOSED:\n            raise StreamClosedError()\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\n    cpdef submit_settings(self, flags, iv):\n        ''' Submit SETTINGS frame. iv is list of tuple (settings_id,\n        flag, value)\n        '''\n        cdef int rv\n        cdef cspdylay.spdylay_settings_entry *civ = pysettings2csettings(iv)\n        rv = cspdylay.spdylay_submit_settings(self._c_session, flags,\n                                              civ, len(iv))\n        free(civ)\n        if rv == 0:\n            return\n        elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT:\n            raise InvalidArgumentError(_strerror(rv))\n        elif rv == cspdylay.SPDYLAY_ERR_NOMEM:\n            raise MemoryError()\n\ncdef _strerror(int error_code):\n    return cspdylay.spdylay_strerror(error_code).decode('UTF-8')\n\ncpdef get_npn_protocols():\n    cdef size_t proto_list_len\n    cdef cspdylay.spdylay_npn_proto *proto_list\n    proto_list = cspdylay.spdylay_npn_get_proto_list(&proto_list_len)\n    res = []\n    for i in range(proto_list_len):\n        res.append((<char*>proto_list[i].proto)[:proto_list[i].len]\\\n                       .decode('UTF-8'))\n    return res\n\ncpdef int npn_get_version(proto):\n    cdef char *cproto\n    if proto == None:\n        return 0\n    proto = proto.encode('UTF-8')\n    cproto = proto\n    return cspdylay.spdylay_npn_get_version(<unsigned char*>cproto, len(proto))\n\n# Side\nCLIENT = 1\nSERVER = 2\n\n# SPDY protocol version\nPROTO_SPDY2 = cspdylay.SPDYLAY_PROTO_SPDY2\nPROTO_SPDY3 = cspdylay.SPDYLAY_PROTO_SPDY3\n\n# Control frame flags\nCTRL_FLAG_NONE = cspdylay.SPDYLAY_CTRL_FLAG_NONE\nCTRL_FLAG_FIN = cspdylay.SPDYLAY_CTRL_FLAG_FIN\nCTRL_FLAG_UNIDIRECTIONAL = cspdylay.SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL\n\n# Data frame flags\nDATA_FLAG_NONE = cspdylay.SPDYLAY_DATA_FLAG_NONE\nDATA_FLAG_FIN = cspdylay.SPDYLAY_DATA_FLAG_FIN\n\n# Error codes\nERR_INVALID_ARGUMENT = cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT\nERR_ZLIB = cspdylay.SPDYLAY_ERR_ZLIB\nERR_UNSUPPORTED_VERSION = cspdylay.SPDYLAY_ERR_UNSUPPORTED_VERSION\nERR_WOULDBLOCK = cspdylay.SPDYLAY_ERR_WOULDBLOCK\nERR_PROTO = cspdylay.SPDYLAY_ERR_PROTO\nERR_INVALID_FRAME = cspdylay.SPDYLAY_ERR_INVALID_FRAME\nERR_EOF = cspdylay.SPDYLAY_ERR_EOF\nERR_DEFERRED = cspdylay.SPDYLAY_ERR_DEFERRED\nERR_STREAM_ID_NOT_AVAILABLE = cspdylay.SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE\nERR_STREAM_CLOSED = cspdylay.SPDYLAY_ERR_STREAM_CLOSED\nERR_STREAM_CLOSING = cspdylay.SPDYLAY_ERR_STREAM_CLOSING\nERR_STREAM_SHUT_WR = cspdylay.SPDYLAY_ERR_STREAM_SHUT_WR\nERR_INVALID_STREAM_ID = cspdylay.SPDYLAY_ERR_INVALID_STREAM_ID\nERR_INVALID_STREAM_STATE = cspdylay.SPDYLAY_ERR_INVALID_STREAM_STATE\nERR_DEFERRED_DATA_EXIST = cspdylay.SPDYLAY_ERR_DEFERRED_DATA_EXIST\nERR_SYN_STREAM_NOT_ALLOWED = cspdylay.SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED\nERR_GOAWAY_ALREADY_SENT = cspdylay.SPDYLAY_ERR_GOAWAY_ALREADY_SENT\nERR_INVALID_HEADER_BLOCK = cspdylay.SPDYLAY_ERR_INVALID_HEADER_BLOCK\nERR_INVALID_STATE = cspdylay.SPDYLAY_ERR_INVALID_STATE\nERR_GZIP = cspdylay.SPDYLAY_ERR_GZIP\nERR_TEMPORAL_CALLBACK_FAILURE = cspdylay.SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE\nERR_FATAL = cspdylay.SPDYLAY_ERR_FATAL\nERR_NOMEM = cspdylay.SPDYLAY_ERR_NOMEM\nERR_CALLBACK_FAILURE = cspdylay.SPDYLAY_ERR_CALLBACK_FAILURE\n\n# Read Callback Flags\nREAD_EOF = 1\n\n# The status code for RST_STREAM\nOK = cspdylay.SPDYLAY_OK\nPROTOCOL_ERROR = cspdylay.SPDYLAY_PROTOCOL_ERROR\nINVALID_STREAM = cspdylay.SPDYLAY_INVALID_STREAM\nREFUSED_STREAM = cspdylay.SPDYLAY_REFUSED_STREAM\nUNSUPPORTED_VERSION = cspdylay.SPDYLAY_UNSUPPORTED_VERSION\nCANCEL = cspdylay.SPDYLAY_CANCEL\nINTERNAL_ERROR = cspdylay.SPDYLAY_INTERNAL_ERROR\nFLOW_CONTROL_ERROR = cspdylay.SPDYLAY_FLOW_CONTROL_ERROR\n# Following status codes were introduced in SPDY/3\nSTREAM_IN_USE = cspdylay.SPDYLAY_STREAM_IN_USE\nSTREAM_ALREADY_CLOSED = cspdylay.SPDYLAY_STREAM_ALREADY_CLOSED\nINVALID_CREDENTIALS = cspdylay.SPDYLAY_INVALID_CREDENTIALS\nFRAME_TOO_LARGE = cspdylay.SPDYLAY_FRAME_TOO_LARGE\n\n# The status codes for GOAWAY, introduced in SPDY/3.\nGOAWAY_OK = cspdylay.SPDYLAY_GOAWAY_OK\nGOAWAY_PROTOCOL_ERROR = cspdylay.SPDYLAY_GOAWAY_PROTOCOL_ERROR\nGOAWAY_INTERNAL_ERROR = cspdylay.SPDYLAY_GOAWAY_INTERNAL_ERROR\n\n# Frame types\nSYN_STREAM = cspdylay.SPDYLAY_SYN_STREAM\nSYN_REPLY = cspdylay.SPDYLAY_SYN_REPLY\nRST_STREAM = cspdylay.SPDYLAY_RST_STREAM\nSETTINGS = cspdylay.SPDYLAY_SETTINGS\nNOOP = cspdylay.SPDYLAY_NOOP\nPING = cspdylay.SPDYLAY_PING\nGOAWAY = cspdylay.SPDYLAY_GOAWAY\nHEADERS = cspdylay.SPDYLAY_HEADERS\nWINDOW_UPDATE = cspdylay.SPDYLAY_WINDOW_UPDATE\nCREDENTIAL = cspdylay.SPDYLAY_CREDENTIAL\n\n# The flags for the SETTINGS control frame.\nFLAG_SETTINGS_NONE = cspdylay.SPDYLAY_FLAG_SETTINGS_NONE\nFLAG_SETTINGS_CLEAR_SETTINGS = cspdylay.SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS\n\n# The flags for SETTINGS ID/value pair.\nID_FLAG_SETTINGS_NONE = cspdylay.SPDYLAY_ID_FLAG_SETTINGS_NONE\nID_FLAG_SETTINGS_PERSIST_VALUE = cspdylay.SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE\nID_FLAG_SETTINGS_PERSISTED = cspdylay.SPDYLAY_ID_FLAG_SETTINGS_PERSISTED\n\n# The SETTINGS ID.\nSETTINGS_UPLOAD_BANDWIDTH = cspdylay.SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH\nSETTINGS_DOWNLOAD_BANDWIDTH = cspdylay.SPDYLAY_SETTINGS_DOWNLOAD_BANDWIDTH\nSETTINGS_ROUND_TRIP_TIME = cspdylay.SPDYLAY_SETTINGS_ROUND_TRIP_TIME\nSETTINGS_MAX_CONCURRENT_STREAMS = \\\n    cspdylay.SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS\nSETTINGS_CURRENT_CWND = cspdylay.SPDYLAY_SETTINGS_CURRENT_CWND\nSETTINGS_DOWNLOAD_RETRANS_RATE = \\\n    cspdylay.SPDYLAY_SETTINGS_DOWNLOAD_RETRANS_RATE\nSETTINGS_INITIAL_WINDOW_SIZE = cspdylay.SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE\nSETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = \\\n    cspdylay.SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE\nSETTINGS_MAX = cspdylay.SPDYLAY_SETTINGS_MAX\n\ntry:\n    # Simple SPDY Server implementation. We mimics the methods and\n    # attributes of http.server.BaseHTTPRequestHandler. Since this\n    # implementation uses TLS NPN, Python 3.3.0 or later is required.\n\n    import socket\n    import threading\n    import socketserver\n    import ssl\n    import io\n    import select\n    import sys\n    import time\n    from xml.sax.saxutils import escape\n\n    class Stream:\n        def __init__(self, stream_id):\n            self.stream_id = stream_id\n            self.data_prd = None\n\n            self.method = None\n            self.path = None\n            self.version = None\n            self.scheme = None\n            self.host = None\n            self.headers = []\n\n            self.rfile = None\n            self.wfile = None\n\n        def process_headers(self, headers):\n            for k, v in headers:\n                if k == ':method':\n                    self.method = v\n                elif k == ':scheme':\n                    self.scheme = v\n                elif k == ':path':\n                    self.path = v\n                elif k == ':version':\n                    self.version = v\n                elif k == ':host':\n                    self.host = v\n                else:\n                    self.headers.append((k, v))\n\n    class SessionCtrl:\n        def __init__(self):\n            self.streams = {}\n\n    class BaseSPDYRequestHandler(socketserver.BaseRequestHandler):\n\n        server_version = 'Python-spdylay'\n\n        error_content_type = 'text/html; charset=UTF-8'\n\n        # Same HTML from Apache error page\n        error_message_format = '''\\\n<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>{code} {reason}</title>\n</head><body>\n<h1>{reason}</h1>\n<p>{explain}</p>\n<hr>\n<address>{server} at {hostname} Port {port}</address>\n</body></html>\n'''\n\n        def send_error(self, code, message=None):\n            # Make sure that code is really int\n            code = int(code)\n            try:\n                shortmsg, longmsg = self.responses[code]\n            except KeyError:\n                shortmsg, longmsg = '???', '???'\n            if message is None:\n                message = shortmsg\n            explain = longmsg\n\n            content = self.error_message_format.format(\\\n                code=code,\n                reason = escape(message),\n                explain=escape(explain),\n                server=escape(self.server_version),\n                hostname=escape(socket.getfqdn()),\n                port=self.server.server_address[1]).encode('UTF-8')\n\n            self.send_response(code, message)\n            self.send_header('content-type', self.error_content_type)\n            self.send_header('content-length', str(len(content)))\n\n            self.wfile.write(content)\n\n        def send_response(self, code, message=None):\n            if message is None:\n                try:\n                    shortmsg, _ = self.responses[code]\n                except KeyError:\n                    shortmsg = '???'\n                message = shortmsg\n\n            self._response_headers.append((':status',\n                                           '{} {}'.format(code, message)))\n\n        def send_header(self, keyword, value):\n            self._response_headers.append((keyword, value))\n\n        def version_string(self):\n            return self.server_version + ' ' + self.sys_version\n\n        def handle_one_request(self, stream):\n            self.stream = stream\n\n            stream.wfile = io.BytesIO()\n\n            self.command = stream.method\n            self.path = stream.path\n            self.request_version = stream.version\n            self.headers = stream.headers\n            self.rfile = stream.rfile\n            self.wfile = stream.wfile\n            self._response_headers = []\n\n            if stream.method is None:\n                self.send_error(400)\n            else:\n                mname = 'do_' + stream.method\n                if hasattr(self, mname):\n                    method = getattr(self, mname)\n\n                    if self.rfile is not None:\n                        self.rfile.seek(0)\n\n                    method()\n                else:\n                    self.send_error(501, 'Unsupported method ({})'\\\n                                        .format(stream.method))\n\n            self.wfile.seek(0)\n            data_prd = DataProvider(self.wfile, self.read_cb)\n            stream.data_prd = data_prd\n\n            self.send_header(':version', 'HTTP/1.1')\n            self.send_header('server', self.version_string())\n            self.send_header('date', self.date_time_string())\n\n            self.session.submit_response(stream.stream_id,\n                                         self._response_headers, data_prd)\n\n\n        def send_cb(self, session, data):\n            return self.request.send(data)\n\n        def read_cb(self, session, stream_id, length, read_ctrl, source):\n            data = source.read(length)\n            if not data:\n                read_ctrl.flags = READ_EOF\n            return data\n\n        def on_ctrl_recv_cb(self, session, frame):\n            if frame.frame_type == SYN_STREAM:\n                stream = Stream(frame.stream_id)\n                self.ssctrl.streams[frame.stream_id] = stream\n\n                stream.process_headers(frame.nv)\n            elif frame.frame_type == HEADERS:\n                if frame.stream_id in self.ssctrl.streams:\n                    stream = self.ssctrl.streams[frame.stream_id]\n                    stream.process_headers(frame.nv)\n\n        def on_data_chunk_recv_cb(self, session, flags, stream_id, data):\n            if stream_id in self.ssctrl.streams:\n                stream = self.ssctrl.streams[stream_id]\n                if stream.method == 'POST':\n                    if not stream.rfile:\n                        stream.rfile = io.BytesIO()\n                    stream.rfile.write(data)\n                else:\n                    # We don't allow request body if method is not POST\n                    session.submit_rst_stream(stream_id, PROTOCOL_ERROR)\n\n        def on_stream_close_cb(self, session, stream_id, status_code):\n            if stream_id in self.ssctrl.streams:\n                del self.ssctrl.streams[stream_id]\n\n        def on_request_recv_cb(self, session, stream_id):\n            if stream_id in self.ssctrl.streams:\n                stream = self.ssctrl.streams[stream_id]\n                self.handle_one_request(stream)\n\n        def handle(self):\n            self.request.setsockopt(socket.IPPROTO_TCP,\n                                    socket.TCP_NODELAY, True)\n            try:\n                self.request.do_handshake()\n                self.request.setblocking(False)\n\n                version = npn_get_version(self.request.selected_npn_protocol())\n                if version == 0:\n                    return\n\n                self.ssctrl = SessionCtrl()\n                self.session = Session(\\\n                    SERVER, version,\n                    send_cb=self.send_cb,\n                    on_ctrl_recv_cb=self.on_ctrl_recv_cb,\n                    on_data_chunk_recv_cb=self.on_data_chunk_recv_cb,\n                    on_stream_close_cb=self.on_stream_close_cb,\n                    on_request_recv_cb=self.on_request_recv_cb)\n\n                self.session.submit_settings(\\\n                    FLAG_SETTINGS_NONE,\n                    [(SETTINGS_MAX_CONCURRENT_STREAMS, ID_FLAG_SETTINGS_NONE,\n                      100)]\n                    )\n\n                while self.session.want_read() or self.session.want_write():\n                    want_read = want_write = False\n                    try:\n                        data = self.request.recv(4096)\n                        if data:\n                            self.session.recv(data)\n                        else:\n                            break\n                    except ssl.SSLWantReadError:\n                        want_read = True\n                    except ssl.SSLWantWriteError:\n                        want_write = True\n                    try:\n                        self.session.send()\n                    except ssl.SSLWantReadError:\n                        want_read = True\n                    except ssl.SSLWantWriteError:\n                        want_write = True\n\n                    if want_read or want_write:\n                        select.select([self.request] if want_read else [],\n                                      [self.request] if want_write else [],\n                                      [])\n            finally:\n                self.request.setblocking(True)\n\n        # The following methods and attributes are copied from\n        # Lib/http/server.py of cpython source code\n\n        def date_time_string(self, timestamp=None):\n            \"\"\"Return the current date and time formatted for a\n            message header.\"\"\"\n            if timestamp is None:\n                timestamp = time.time()\n            year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)\n            s = \"%s, %02d %3s %4d %02d:%02d:%02d GMT\" % (\n                    self.weekdayname[wd],\n                    day, self.monthname[month], year,\n                    hh, mm, ss)\n            return s\n\n        weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n\n        monthname = [None,\n                     'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n                     'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n\n        # The Python system version, truncated to its first component.\n        sys_version = \"Python/\" + sys.version.split()[0]\n\n        # Table mapping response codes to messages; entries have the\n        # form {code: (shortmessage, longmessage)}.\n        # See RFC 2616 and 6585.\n        responses = {\n            100: ('Continue', 'Request received, please continue'),\n            101: ('Switching Protocols',\n                  'Switching to new protocol; obey Upgrade header'),\n\n            200: ('OK', 'Request fulfilled, document follows'),\n            201: ('Created', 'Document created, URL follows'),\n            202: ('Accepted',\n                  'Request accepted, processing continues off-line'),\n            203: ('Non-Authoritative Information',\n                  'Request fulfilled from cache'),\n            204: ('No Content', 'Request fulfilled, nothing follows'),\n            205: ('Reset Content', 'Clear input form for further input.'),\n            206: ('Partial Content', 'Partial content follows.'),\n\n            300: ('Multiple Choices',\n                  'Object has several resources -- see URI list'),\n            301: ('Moved Permanently',\n                  'Object moved permanently -- see URI list'),\n            302: ('Found', 'Object moved temporarily -- see URI list'),\n            303: ('See Other', 'Object moved -- see Method and URL list'),\n            304: ('Not Modified',\n                  'Document has not changed since given time'),\n            305: ('Use Proxy',\n                  'You must use proxy specified in Location to access this '\n                  'resource.'),\n            307: ('Temporary Redirect',\n                  'Object moved temporarily -- see URI list'),\n\n            400: ('Bad Request',\n                  'Bad request syntax or unsupported method'),\n            401: ('Unauthorized',\n                  'No permission -- see authorization schemes'),\n            402: ('Payment Required',\n                  'No payment -- see charging schemes'),\n            403: ('Forbidden',\n                  'Request forbidden -- authorization will not help'),\n            404: ('Not Found', 'Nothing matches the given URI'),\n            405: ('Method Not Allowed',\n                  'Specified method is invalid for this resource.'),\n            406: ('Not Acceptable', 'URI not available in preferred format.'),\n            407: ('Proxy Authentication Required', 'You must authenticate with '\n                  'this proxy before proceeding.'),\n            408: ('Request Timeout', 'Request timed out; try again later.'),\n            409: ('Conflict', 'Request conflict.'),\n            410: ('Gone',\n                  'URI no longer exists and has been permanently removed.'),\n            411: ('Length Required', 'Client must specify Content-Length.'),\n            412: ('Precondition Failed', 'Precondition in headers is false.'),\n            413: ('Request Entity Too Large', 'Entity is too large.'),\n            414: ('Request-URI Too Long', 'URI is too long.'),\n            415: ('Unsupported Media Type',\n                  'Entity body in unsupported format.'),\n            416: ('Requested Range Not Satisfiable',\n                  'Cannot satisfy request range.'),\n            417: ('Expectation Failed',\n                  'Expect condition could not be satisfied.'),\n            428: ('Precondition Required',\n                  'The origin server requires the request to be conditional.'),\n            429: ('Too Many Requests', 'The user has sent too many requests '\n                  'in a given amount of time (\"rate limiting\").'),\n            431: ('Request Header Fields Too Large',\n                  'The server is unwilling to process '\n                  'the request because its header fields are too large.'),\n\n            500: ('Internal Server Error', 'Server got itself in trouble'),\n            501: ('Not Implemented',\n                  'Server does not support this operation'),\n            502: ('Bad Gateway',\n                  'Invalid responses from another server/proxy.'),\n            503: ('Service Unavailable',\n                  'The server cannot process the request due to a high load'),\n            504: ('Gateway Timeout',\n                  'The gateway server did not receive a timely response'),\n            505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),\n            511: ('Network Authentication Required',\n                  'The client needs to authenticate to gain network access.'),\n            }\n\n    class ThreadedSPDYServer(socketserver.ThreadingMixIn,\n                             socketserver.TCPServer):\n        def __init__(self, server_address, RequestHandlerCalss,\n                     cert_file, key_file):\n            self.allow_reuse_address = True\n\n            self.ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n            self.ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | \\\n                ssl.OP_NO_COMPRESSION\n            self.ctx.load_cert_chain(cert_file, key_file)\n            self.ctx.set_npn_protocols(get_npn_protocols())\n\n            socketserver.TCPServer.__init__(self, server_address,\n                                            RequestHandlerCalss)\n\n        def start(self, daemon=False):\n            server_thread = threading.Thread(target=self.serve_forever)\n            server_thread.daemon = daemon\n            server_thread.start()\n\n        def process_request(self, request, client_address):\n            # ThreadingMixIn.process_request() dispatches request and\n            # client_address to separate thread. To cleanly shutdown\n            # SSL/TLS wrapped socket, we wrap socket here.\n\n            # SSL/TLS handshake is postponed to each thread.\n            request = self.ctx.wrap_socket(\\\n                request, server_side=True, do_handshake_on_connect=False)\n\n            socketserver.ThreadingMixIn.process_request(self,\n                                                        request, client_address)\n\n\n    # Simple SPDY client implementation. Since this implementation\n    # uses TLS NPN, Python 3.3.0 or later is required.\n\n    from urllib.parse import urlsplit\n\n    class BaseSPDYStreamHandler:\n        def __init__(self, url, fetcher):\n            self.url = url\n            self.fetcher = fetcher\n            self.stream_id = None\n\n        def on_header(self, nv):\n            pass\n\n        def on_data(self, data):\n            pass\n\n        def on_close(self, status_code):\n            pass\n\n    class UrlFetchError(Exception):\n        pass\n\n    class UrlFetcher:\n        def __init__(self, server_address, urls, StreamHandlerClass):\n            self.server_address = server_address\n            self.handlers = [StreamHandlerClass(url, self) for url in urls]\n            self.streams = {}\n            self.finished = []\n\n            self.ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)\n            self.ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | \\\n                ssl.OP_NO_COMPRESSION\n            self.ctx.set_npn_protocols(get_npn_protocols())\n\n        def send_cb(self, session, data):\n            return self.sock.send(data)\n\n        def before_ctrl_send_cb(self, session, frame):\n            if frame.frame_type == SYN_STREAM:\n                handler = session.get_stream_user_data(frame.stream_id)\n                if handler:\n                    handler.stream_id = frame.stream_id\n                    self.streams[handler.stream_id] = handler\n\n        def on_ctrl_recv_cb(self, session, frame):\n            if frame.frame_type == SYN_REPLY or frame.frame_type == HEADERS:\n                if frame.stream_id in self.streams:\n                    handler = self.streams[frame.stream_id]\n                    handler.on_header(frame.nv)\n\n        def on_data_chunk_recv_cb(self, session, flags, stream_id, data):\n            if stream_id in self.streams:\n                handler = self.streams[stream_id]\n                handler.on_data(data)\n\n        def on_stream_close_cb(self, session, stream_id, status_code):\n            if stream_id in self.streams:\n                handler = self.streams[stream_id]\n                handler.on_close(status_code)\n                del self.streams[stream_id]\n                self.finished.append(handler)\n\n        def connect(self, server_address):\n            self.sock = None\n            for res in socket.getaddrinfo(server_address[0], server_address[1],\n                                          socket.AF_UNSPEC,\n                                          socket.SOCK_STREAM):\n                af, socktype, proto, canonname, sa = res\n                try:\n                    self.sock = socket.socket(af, socktype, proto)\n                except OSError as msg:\n                    self.sock = None\n                    continue\n                try:\n                    self.sock.connect(sa)\n                except OSError as msg:\n                    self.sock.close()\n                    self.sock = None\n                    continue\n                break\n            else:\n                raise UrlFetchError('Could not connect to {}'\\\n                                        .format(server_address))\n\n        def tls_handshake(self):\n            self.sock = self.ctx.wrap_socket(self.sock, server_side=False,\n                                             do_handshake_on_connect=False)\n            self.sock.do_handshake()\n\n            self.version = npn_get_version(self.sock.selected_npn_protocol())\n            if self.version == 0:\n                raise UrlFetchError('NPN failed')\n\n        def loop(self):\n            self.connect(self.server_address)\n            try:\n                self._loop()\n            finally:\n                self.sock.shutdown(socket.SHUT_RDWR)\n                self.sock.close()\n\n        def _loop(self):\n            self.tls_handshake()\n            self.sock.setblocking(False)\n\n            session = Session(CLIENT,\n                              self.version,\n                              send_cb=self.send_cb,\n                              on_ctrl_recv_cb=self.on_ctrl_recv_cb,\n                              on_data_chunk_recv_cb=self.on_data_chunk_recv_cb,\n                              before_ctrl_send_cb=self.before_ctrl_send_cb,\n                              on_stream_close_cb=self.on_stream_close_cb)\n\n            session.submit_settings(\\\n                FLAG_SETTINGS_NONE,\n                [(SETTINGS_MAX_CONCURRENT_STREAMS, ID_FLAG_SETTINGS_NONE, 100)]\n                )\n\n            if self.server_address[1] == 443:\n                hostport = self.server_address[0]\n            else:\n                hostport = '{}:{}'.format(self.server_address[0],\n                                          self.server_address[1])\n\n            for handler in self.handlers:\n                res = urlsplit(handler.url)\n                if res.path:\n                    path = res.path\n                else:\n                    path = '/'\n                if res.query:\n                    path = '?'.join([path, res.query])\n\n                session.submit_request(0,\n                                       [(':method', 'GET'),\n                                        (':scheme', 'https'),\n                                        (':path', path),\n                                        (':version', 'HTTP/1.1'),\n                                        (':host', hostport),\n                                        ('accept', '*/*'),\n                                        ('user-agent', 'python-spdylay')],\n                                       stream_user_data=handler)\n\n            while (session.want_read() or session.want_write()) \\\n                    and not len(self.finished) == len(self.handlers):\n                want_read = want_write = False\n                try:\n                    data = self.sock.recv(4096)\n                    if data:\n                        session.recv(data)\n                    else:\n                        break\n                except ssl.SSLWantReadError:\n                    want_read = True\n                except ssl.SSLWantWriteError:\n                    want_write = True\n                try:\n                    session.send()\n                except ssl.SSLWantReadError:\n                    want_read = True\n                except ssl.SSLWantWriteError:\n                    want_write = True\n\n                if want_read or want_write:\n                    select.select([self.sock] if want_read else [],\n                                  [self.sock] if want_write else [],\n                                  [])\n\n    def _urlfetch_session_one(urls, StreamHandlerClass):\n        res = urlsplit(urls[0])\n        if res.scheme != 'https':\n            raise UrlFetchError('Unsupported scheme {}'.format(res.scheme))\n        hostname = res.hostname\n        port = res.port if res.port else 443\n\n        f = UrlFetcher((hostname, port), urls, StreamHandlerClass)\n        f.loop()\n\n    def urlfetch(url_or_urls, StreamHandlerClass):\n        if isinstance(url_or_urls, str):\n            _urlfetch_session_one([url_or_urls], StreamHandlerClass)\n        else:\n            urls = []\n            prev_addr = (None, None)\n            for url in url_or_urls:\n                res = urlsplit(url)\n                port = res.port if res.port else 443\n                if prev_addr != (res.hostname, port):\n                    if urls:\n                        _urlfetch_session_one(urls, StreamHandlerClass)\n                        urls = []\n                prev_addr = (res.hostname, port)\n                urls.append(url)\n            if urls:\n                _urlfetch_session_one(urls, StreamHandlerClass)\n\nexcept ImportError:\n    # No server for 2.x because they lack TLS NPN.\n    pass\n"
  },
  {
    "path": "python/spdylay_tests.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\nimport io\nimport collections\n\nimport spdylay\n\nclass BufferList:\n    def __init__(self):\n        self.buffers = collections.deque()\n\n    def add_buffer(self, bytebuf):\n        self.buffers.append(bytebuf)\n\n    def get_bytes(self, length):\n        while self.buffers:\n            first = self.buffers[0]\n            data = first.read(length)\n            if data:\n                return data\n            else:\n                self.buffers.popleft()\n        return None\n\nclass IOBridge:\n    def __init__(self, inputs, outputs):\n        self.inputs = inputs\n        self.outputs = outputs\n\nclass Streams:\n    def __init__(self, iob):\n        self.iob = iob\n        self.streams = {}\n        self.recv_frames = []\n        self.recv_data = io.BytesIO()\n\ndef recv_cb(session, length):\n    iob = session.user_data.iob\n    return iob.inputs.get_bytes(length)\n\ndef send_cb(session, data):\n    iob = session.user_data.iob\n    iob.outputs.add_buffer(io.BytesIO(data))\n    return len(data)\n\ndef read_cb(session, stream_id, length, read_ctrl, source):\n    data = source.read(length)\n    if not data:\n        read_ctrl.flags = spdylay.READ_EOF\n    return data\n\ndef on_data_chunk_recv_cb(session, flags, stream_id, data):\n    session.user_data.recv_data.write(data)\n\ndef on_ctrl_recv_cb(session, frame):\n    session.user_data.recv_frames.append(frame)\n\nclass SpdylayTests(unittest.TestCase):\n\n    def setUp(self):\n        client_output = BufferList()\n        server_output = BufferList()\n\n        client_iob = IOBridge(server_output, client_output)\n        server_iob = IOBridge(client_output, server_output)\n\n        self.client_streams = Streams(client_iob)\n        self.server_streams = Streams(server_iob)\n\n        self.client_session = spdylay.Session(\\\n            spdylay.CLIENT,\n            spdylay.PROTO_SPDY3,\n            user_data=self.client_streams,\n            recv_cb=recv_cb,\n            send_cb=send_cb,\n            on_ctrl_recv_cb=on_ctrl_recv_cb,\n            on_data_chunk_recv_cb=on_data_chunk_recv_cb)\n\n        self.server_session = spdylay.Session(\\\n            spdylay.SERVER,\n            spdylay.PROTO_SPDY3,\n            user_data=self.server_streams,\n            recv_cb=recv_cb,\n            send_cb=send_cb,\n            on_ctrl_recv_cb=on_ctrl_recv_cb,\n            on_data_chunk_recv_cb=on_data_chunk_recv_cb)\n\n    def test_submit_request_and_response(self):\n        data_prd = spdylay.DataProvider(io.BytesIO(b'Hello World'), read_cb)\n        self.client_session.submit_request(0, [(u':method', u'POST')],\n                                           data_prd=data_prd,\n                                           stream_user_data=data_prd)\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual(0, frame.assoc_stream_id)\n        self.assertEqual(0, frame.pri)\n        self.assertEqual((u':method', u'POST'), frame.nv[0])\n\n        self.assertEqual(b'Hello World',\n                         self.server_streams.recv_data.getvalue())\n\n        self.assertEqual(data_prd, self.client_session.get_stream_user_data(1))\n\n        data_prd = spdylay.DataProvider(io.BytesIO(b'Foo the bar'), read_cb)\n        self.server_session.submit_response(1, [(u':status', u'200 OK')],\n                                            data_prd=data_prd)\n        self.server_session.send()\n        self.client_session.recv()\n\n        self.assertEqual(1, len(self.client_streams.recv_frames))\n        frame = self.client_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_REPLY, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual((u':status', u'200 OK'), frame.nv[0])\n\n        self.assertEqual(b'Foo the bar',\n                         self.client_streams.recv_data.getvalue())\n\n    def test_submit_syn_stream_and_syn_stream(self):\n        self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_FIN, 2,\n                                              [(u':path', u'/')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual(0, frame.assoc_stream_id)\n        self.assertEqual(2, frame.pri)\n        self.assertEqual((u':path', u'/'), frame.nv[0])\n\n        self.server_session.submit_syn_reply(spdylay.CTRL_FLAG_FIN, 1,\n                                             [(u':version', u'HTTP/1.1')])\n        self.server_session.send()\n        self.client_session.recv()\n\n        self.assertEqual(1, len(self.client_streams.recv_frames))\n        frame = self.client_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_REPLY, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual((u':version', u'HTTP/1.1'), frame.nv[0])\n\n    def test_submit_rst_stream(self):\n        self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_FIN, 2,\n                                              [(u':path', u'/')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.server_session.submit_rst_stream(1, spdylay.PROTOCOL_ERROR)\n        self.server_session.send()\n        self.client_session.recv()\n\n        self.assertEqual(1, len(self.client_streams.recv_frames))\n        frame = self.client_streams.recv_frames[0]\n        self.assertEqual(spdylay.RST_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual(spdylay.PROTOCOL_ERROR, frame.status_code)\n\n    def test_submit_goaway(self):\n        self.client_session.submit_goaway(spdylay.GOAWAY_PROTOCOL_ERROR)\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.GOAWAY, frame.frame_type)\n        self.assertEqual(spdylay.GOAWAY_PROTOCOL_ERROR, frame.status_code)\n\n    def test_resume_data(self):\n        self.assertFalse(self.client_session.resume_data(1))\n\n    def test_get_pri_lowest(self):\n        self.assertEqual(7, self.client_session.get_pri_lowest())\n\n    def test_fail_session(self):\n        self.client_session.fail_session(spdylay.GOAWAY_PROTOCOL_ERROR)\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.GOAWAY, frame.frame_type)\n        self.assertEqual(spdylay.GOAWAY_PROTOCOL_ERROR, frame.status_code)\n\n        self.assertFalse(self.client_session.want_read())\n        self.assertFalse(self.client_session.want_write())\n\n    def test_deferred_data(self):\n        def deferred_read_cb(session, stream_id, length, read_ctrl, source):\n            return spdylay.ERR_DEFERRED\n\n        data_prd = spdylay.DataProvider(io.BytesIO(b'Hello World'),\n                                        deferred_read_cb)\n        self.client_session.submit_request(0, [(u':method', u'POST')],\n                                           data_prd=data_prd,\n                                           stream_user_data=data_prd)\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual(0, frame.assoc_stream_id)\n        self.assertEqual(0, frame.pri)\n        self.assertEqual((u':method', u'POST'), frame.nv[0])\n\n        self.assertEqual(b'', self.server_streams.recv_data.getvalue())\n\n        data_prd.read_cb = read_cb\n\n        self.client_session.resume_data(1)\n\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(b'Hello World',\n                         self.server_streams.recv_data.getvalue())\n\n    def test_recv_cb_eof(self):\n        def eof_recv_cb(session, length):\n            raise spdylay.EOFError()\n\n        self.client_session = spdylay.Session(\\\n            spdylay.CLIENT,\n            spdylay.PROTO_SPDY3,\n            user_data=self.client_streams,\n            recv_cb=eof_recv_cb)\n\n        with self.assertRaises(spdylay.EOFError):\n            self.client_session.recv()\n\n    def test_recv_cb_callback_failure(self):\n        def cbfail_recv_cb(session, length):\n            raise spdylay.CallbackFailureError()\n\n        self.client_session = spdylay.Session(\\\n            spdylay.CLIENT,\n            spdylay.PROTO_SPDY3,\n            user_data=self.client_streams,\n            recv_cb=cbfail_recv_cb)\n\n        with self.assertRaises(spdylay.CallbackFailureError):\n            self.client_session.recv()\n\n    def test_send_cb_callback_failure(self):\n        def cbfail_send_cb(session, data):\n            raise spdylay.CallbackFailureError()\n\n        self.client_session = spdylay.Session(\\\n            spdylay.CLIENT,\n            spdylay.PROTO_SPDY3,\n            user_data=self.client_streams,\n            send_cb=cbfail_send_cb)\n\n        self.client_session.submit_goaway(spdylay.GOAWAY_OK)\n\n        with self.assertRaises(spdylay.CallbackFailureError):\n            self.client_session.send()\n\n    def test_submit_data(self):\n        self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_NONE, 2,\n                                              [(u':path', u'/')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n\n        data_prd = spdylay.DataProvider(io.BytesIO(b'Hello World'), read_cb)\n        self.client_session.submit_data(1, spdylay.DATA_FLAG_FIN, data_prd)\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(b'Hello World',\n                         self.server_streams.recv_data.getvalue())\n\n    def test_submit_headers(self):\n        self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_NONE, 2,\n                                              [(u':path', u'/')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n\n        self.client_session.submit_headers(spdylay.CTRL_FLAG_FIN, 1,\n                                           [(u':host', u'localhost')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(2, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[1]\n        self.assertEqual(spdylay.HEADERS, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual((u':host', u'localhost'), frame.nv[0])\n\n    def test_submit_ping(self):\n        self.client_session.submit_ping()\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.PING, frame.frame_type)\n        self.assertEqual(1, frame.unique_id)\n\n    def test_submit_window_update(self):\n        self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_NONE, 2,\n                                              [(u':path', u'/')])\n        self.client_session.send()\n        self.server_session.recv()\n\n        self.assertEqual(1, len(self.server_streams.recv_frames))\n        frame = self.server_streams.recv_frames[0]\n        self.assertEqual(spdylay.SYN_STREAM, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n\n        self.server_session.submit_window_update(1, 4096)\n        self.server_session.send()\n        self.client_session.recv()\n\n        self.assertEqual(1, len(self.client_streams.recv_frames))\n        frame = self.client_streams.recv_frames[0]\n        self.assertEqual(spdylay.WINDOW_UPDATE, frame.frame_type)\n        self.assertEqual(1, frame.stream_id)\n        self.assertEqual(4096, frame.delta_window_size)\n\n    def test_get_npn_protocols(self):\n        protos = spdylay.get_npn_protocols()\n        self.assertEqual(['spdy/3', 'spdy/2'], protos)\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "python/spdyserv.py",
    "content": "#!/usr/bin/env python\n\n# The example SPDY server. Python 3.3 or later is required because TLS\n# NPN is used in spdylay.ThreadedSPDYServer. Put private key and\n# certificate file in the current working directory.\n\nimport spdylay\n\n# private key file\nKEY_FILE='server.key'\n# certificate file\nCERT_FILE='server.crt'\n\nclass MySPDYRequestHandler(spdylay.BaseSPDYRequestHandler):\n\n    def do_GET(self):\n        if self.path == '/notfound':\n            # Example code to return error\n            self.send_error(404)\n            return\n\n        self.send_response(200)\n        self.send_header('content-type', 'text/html; charset=UTF-8')\n\n        content = '''\\\n<html>\n<head><title>SPDY FTW</title></head>\n<body>\n<h1>SPDY FTW</h1>\n<p>The age of HTTP/1.1 is over. The time of SPDY has come.</p>\n</body>\n</html>'''.encode('UTF-8')\n\n        self.wfile.write(content)\n\nif __name__ == \"__main__\":\n    HOST, PORT = \"localhost\", 3000\n\n    server = spdylay.ThreadedSPDYServer((HOST, PORT),\n                                        MySPDYRequestHandler,\n                                        cert_file=CERT_FILE,\n                                        key_file=KEY_FILE)\n    server.start()\n"
  },
  {
    "path": "shrpx.conf.sample",
    "content": "#\n# Sample configuration file for Shrpx.\n#\n# * Line staring '#' is treated as comment.\n#\n# * The option name in the configuration file is the long command-line\n#   option name with leading '--' stripped (e.g., frontend). Put '='\n#   between option name and value. Don't put extra leading or trailing\n#   spaces.\n#\n# * The options which do not take argument in the command-line *take*\n#   argument in the configuration file. Specify 'yes' as argument\n#   (e.g., spdy-proxy=yes). If other string is given, it disables the\n#   option.\n#\n# * To specify private key and certificate file, use private-key-file\n#   and certificate-file. See the examples below.\n#\n# * conf option cannot be used in the configuration file. It will be\n#   ignored.\n#\n# Examples:\n#\n# frontend=0.0.0.0,3000\n# backend=127.0.0.1,80\n# private-key-file=/path/to/server.key\n# certificate-file=/path/to/server.crt\n# spdy-proxy=no\n# workers=1\n"
  },
  {
    "path": "src/.gitignore",
    "content": "spdycat\nspdyd\nshrpx\nshrpx-unittest\nshrpx-unittest.log\nshrpx-unittest.trs\ntest-suite.log\n"
  },
  {
    "path": "src/EventPoll.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef EVENT_POLL_H\n#define EVENT_POLL_H\n\n#include \"spdylay_config.h\"\n\n#ifdef HAVE_EPOLL\n#  include \"EventPoll_epoll.h\"\n#elif HAVE_KQUEUE\n#  include \"EventPoll_kqueue.h\"\n#endif // HAVE_KQUEUE\n\n#endif // EVENT_POLL_H\n"
  },
  {
    "path": "src/EventPollEvent.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef EVENT_POLL_EVENT_H\n#define EVENT_POLL_EVENT_H\n\n#include \"spdylay_config.h\"\n\nnamespace spdylay {\n\nenum EventPollEvent {\n  EP_POLLIN = 1,\n  EP_POLLOUT = 1 << 1,\n  EP_POLLHUP = 1 << 2,\n  EP_POLLERR = 1 << 3\n};\n\nenum EventPollOp {\n  EP_ADD,\n  EP_MOD\n};\n\n} // namespace spdylay\n\n#endif // EVENT_POLL_EVENT_H\n"
  },
  {
    "path": "src/EventPoll_epoll.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"EventPoll_epoll.h\"\n\n#include <unistd.h>\n\n#include <cassert>\n\nnamespace spdylay {\n\nEventPoll::EventPoll(size_t max_events)\n  : max_events_(max_events), num_events_(0)\n{\n  epfd_ = epoll_create(1);\n  assert(epfd_ != -1);\n  evlist_ = new epoll_event[max_events_];\n}\n\nEventPoll::~EventPoll()\n{\n  if(epfd_ != -1) {\n    close(epfd_);\n  }\n  delete [] evlist_;\n}\n\nint EventPoll::poll(int timeout)\n{\n  num_events_ = 0;\n  int n = epoll_wait(epfd_, evlist_, max_events_, timeout);\n  if(n > 0) {\n    num_events_ = n;\n  }\n  return n;\n}\n\nsize_t EventPoll::get_num_events()\n{\n  return num_events_;\n}\n\nvoid* EventPoll::get_user_data(size_t p)\n{\n  return evlist_[p].data.ptr;\n}\n\nint EventPoll::get_events(size_t p)\n{\n  int events = 0;\n  int revents = evlist_[p].events;\n  if(revents & EPOLLIN) {\n    events |= EP_POLLIN;\n  }\n  if(revents & EPOLLOUT) {\n    events |= EP_POLLOUT;\n  }\n  if(revents & EPOLLHUP) {\n    events |= EP_POLLHUP;\n  }\n  if(revents & EPOLLERR) {\n    events |= EP_POLLERR;\n  }\n  return events;\n}\n\nnamespace {\nint update_event(int epfd, int op, int fd, int events, void *user_data)\n{\n  epoll_event ev;\n  ev.events = 0;\n  if(events & EP_POLLIN) {\n    ev.events |= EPOLLIN;\n  }\n  if(events & EP_POLLOUT) {\n    ev.events |= EPOLLOUT;\n  }\n  ev.data.ptr = user_data;\n  return epoll_ctl(epfd, op, fd, &ev);\n}\n} // namespace\n\nint EventPoll::ctl_event(int op, int fd, int events, void *user_data)\n{\n  if(op == EP_ADD) {\n    op = EPOLL_CTL_ADD;\n  } else if(op == EP_MOD) {\n    op = EPOLL_CTL_MOD;\n  } else {\n    return -1;\n  }\n  return update_event(epfd_, op, fd, events, user_data);\n}\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/EventPoll_epoll.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef EVENT_POLL_EPOLL_H\n#define EVENT_POLL_EPOLL_H\n\n#include \"spdylay_config.h\"\n\n#include <cstdlib>\n\n#include <sys/epoll.h>\n\n#include \"EventPollEvent.h\"\n\nnamespace spdylay {\n\nclass EventPoll {\npublic:\n  EventPoll(size_t max_events);\n  ~EventPoll();\n  // Returns 0 if this function succeeds, or -1.\n  // On success\n  int poll(int timeout);\n  // Returns number of events detected in previous call of poll().\n  size_t get_num_events();\n  // Returns events of p-eth event.\n  int get_events(size_t p);\n  // Returns user data of p-th event.\n  void* get_user_data(size_t p);\n  // Adds/Modifies event to watch.\n  int ctl_event(int op, int fd, int events, void *user_data);\nprivate:\n  epoll_event *evlist_;\n  size_t max_events_;\n  size_t num_events_;\n  int epfd_;\n};\n\n} // namespace spdylay\n\n#endif // EVENT_POLL_EPOLL_H\n"
  },
  {
    "path": "src/EventPoll_kqueue.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"EventPoll_kqueue.h\"\n\n#include <unistd.h>\n\n#include <cerrno>\n#include <cassert>\n\n#ifdef KEVENT_UDATA_INTPTR_T\n# define PTR_TO_UDATA(X) (reinterpret_cast<intptr_t>(X))\n#else // !KEVENT_UDATA_INTPTR_T\n# define PTR_TO_UDATA(X) (X)\n#endif // !KEVENT_UDATA_INTPTR_T\n\nnamespace spdylay {\n\nEventPoll::EventPoll(size_t max_events)\n  : max_events_(max_events), num_events_(0)\n{\n  kq_ = kqueue();\n  assert(kq_ != -1);\n  evlist_ = new struct kevent[max_events_];\n}\n\nEventPoll::~EventPoll()\n{\n  if(kq_ != -1) {\n    close(kq_);\n  }\n  delete [] evlist_;\n}\n\nint EventPoll::poll(int timeout)\n{\n  timespec ts, *ts_ptr;\n  if(timeout == -1) {\n    ts_ptr = 0;\n  } else {\n    ts.tv_sec = timeout/1000;\n    ts.tv_nsec = (timeout%1000)*1000000;\n    ts_ptr = &ts;\n  }\n  num_events_ = 0;\n  int n;\n  while((n = kevent(kq_, evlist_, 0, evlist_, max_events_, ts_ptr)) == -1 &&\n        errno == EINTR);\n  if(n > 0) {\n    num_events_ = n;\n  }\n  return n;\n}\n\nsize_t EventPoll::get_num_events()\n{\n  return num_events_;\n}\n\nvoid* EventPoll::get_user_data(size_t p)\n{\n  return reinterpret_cast<void*>(evlist_[p].udata);\n}\n\nint EventPoll::get_events(size_t p)\n{\n  int events = 0;\n  short filter = evlist_[p].filter;\n  if(filter == EVFILT_READ) {\n    events |= EP_POLLIN;\n  }\n  if(filter == EVFILT_WRITE) {\n    events |= EP_POLLOUT;\n  }\n  return events;\n}\n\nnamespace {\nint update_event(int kq, int fd, int events, void *user_data)\n{\n  struct kevent changelist[2];\n  EV_SET(&changelist[0], fd, EVFILT_READ,\n         EV_ADD | ((events & EP_POLLIN) ? EV_ENABLE : EV_DISABLE),\n         0, 0, PTR_TO_UDATA(user_data));\n  EV_SET(&changelist[1], fd, EVFILT_WRITE,\n         EV_ADD | ((events & EP_POLLOUT) ? EV_ENABLE : EV_DISABLE),\n         0, 0, PTR_TO_UDATA(user_data));\n  timespec ts = { 0, 0 };\n  return kevent(kq, changelist, 2, changelist, 0, &ts);\n}\n} // namespace\n\nint EventPoll::ctl_event(int op, int fd, int events, void *user_data)\n{\n  return update_event(kq_, fd, events, user_data);\n}\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/EventPoll_kqueue.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef EVENT_POLL_KQUEUE_H\n#define EVENT_POLL_KQUEUE_H\n\n#include \"spdylay_config.h\"\n\n#include <cstdlib>\n\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\n\n#include \"EventPollEvent.h\"\n\nnamespace spdylay {\n\nclass EventPoll {\npublic:\n  EventPoll(size_t max_events);\n  ~EventPoll();\n  // Returns 0 if this function succeeds, or -1.\n  // On success\n  int poll(int timeout);\n  // Returns number of events detected in previous call of poll().\n  size_t get_num_events();\n  // Returns events of p-eth event.\n  int get_events(size_t p);\n  // Returns user data of p-th event.\n  void* get_user_data(size_t p);\n  // Adds/Modifies event to watch.\n  int ctl_event(int op, int fd, int events, void *user_data);\nprivate:\n  int kq_;\n  size_t max_events_;\n  struct kevent *evlist_;\n  size_t num_events_;\n};\n\n} // namespace spdylay\n\n#endif // EVENT_POLL_KQUEUE_H\n"
  },
  {
    "path": "src/HtmlParser.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"HtmlParser.h\"\n\n#include <libxml/uri.h>\n\n#include \"util.h\"\n\nnamespace spdylay {\n\nParserData::ParserData(const std::string& base_uri)\n  : base_uri(base_uri)\n{}\n\nHtmlParser::HtmlParser(const std::string& base_uri)\n  : base_uri_(base_uri),\n    parser_ctx_(0),\n    parser_data_(base_uri)\n{}\n\nHtmlParser::~HtmlParser()\n{\n  htmlFreeParserCtxt(parser_ctx_);\n}\n\nnamespace {\nconst char* get_attr(const xmlChar **attrs, const char *name)\n{\n  for(; *attrs; attrs += 2) {\n    if(util::strieq(reinterpret_cast<const char*>(attrs[0]), name)) {\n      return reinterpret_cast<const char*>(attrs[1]);\n    }\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nvoid start_element_func\n(void* user_data,\n const xmlChar *name,\n const xmlChar **attrs)\n{\n  ParserData *parser_data = static_cast<ParserData*>(user_data);\n  if(util::strieq(reinterpret_cast<const char*>(name), \"link\")) {\n    const char *rel_attr = get_attr(attrs, \"rel\");\n    const char *href_attr = get_attr(attrs, \"href\");\n    if((util::strieq(rel_attr, \"shortcut icon\") ||\n        util::strieq(rel_attr, \"stylesheet\")) &&\n       href_attr) {\n      xmlChar *u = xmlBuildURI(reinterpret_cast<const xmlChar*>(href_attr),\n                               reinterpret_cast<const xmlChar*>\n                               (parser_data->base_uri.c_str()));\n      if(u) {\n        parser_data->links.push_back(reinterpret_cast<char*>(u));\n        free(u);\n      }\n    }\n  } else if(util::strieq(reinterpret_cast<const char*>(name), \"img\")) {\n    const char *src_attr = get_attr(attrs, \"src\");\n    if(src_attr) {\n      xmlChar *u = xmlBuildURI(reinterpret_cast<const xmlChar*>(src_attr),\n                               reinterpret_cast<const xmlChar*>\n                               (parser_data->base_uri.c_str()));\n      if(u) {\n        parser_data->links.push_back(reinterpret_cast<char*>(u));\n        free(u);\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nxmlSAXHandler saxHandler =\n  {\n    0, // internalSubsetSAXFunc\n    0, // isStandaloneSAXFunc\n    0, // hasInternalSubsetSAXFunc\n    0, // hasExternalSubsetSAXFunc\n    0, // resolveEntitySAXFunc\n    0, // getEntitySAXFunc\n    0, // entityDeclSAXFunc\n    0, // notationDeclSAXFunc\n    0, // attributeDeclSAXFunc\n    0, // elementDeclSAXFunc\n    0, //   unparsedEntityDeclSAXFunc\n    0, //   setDocumentLocatorSAXFunc\n    0, //   startDocumentSAXFunc\n    0, //   endDocumentSAXFunc\n    &start_element_func, //   startElementSAXFunc\n    0, //   endElementSAXFunc\n    0, //   referenceSAXFunc\n    0, //   charactersSAXFunc\n    0, //   ignorableWhitespaceSAXFunc\n    0, //   processingInstructionSAXFunc\n    0, //   commentSAXFunc\n    0, //   warningSAXFunc\n    0, //   errorSAXFunc\n    0, //   fatalErrorSAXFunc\n    0, //   getParameterEntitySAXFunc\n    0, //   cdataBlockSAXFunc\n    0, //   externalSubsetSAXFunc\n    0, //   unsigned int        initialized\n    0, //   void *      _private\n    0, //   startElementNsSAX2Func\n    0, //   endElementNsSAX2Func\n    0, //   xmlStructuredErrorFunc\n  };\n} // namespace\n\nint HtmlParser::parse_chunk(const char *chunk, size_t size, int fin)\n{\n  if(!parser_ctx_) {\n    parser_ctx_ = htmlCreatePushParserCtxt(&saxHandler,\n                                           &parser_data_,\n                                           chunk, size,\n                                           base_uri_.c_str(),\n                                           XML_CHAR_ENCODING_NONE);\n    if(!parser_ctx_) {\n      return -1;\n    } else {\n      if(fin) {\n        return parse_chunk_internal(0, 0, fin);\n      } else {\n        return 0;\n      }\n    }\n  } else {\n    return parse_chunk_internal(chunk, size, fin);\n  }\n}\n\nint HtmlParser::parse_chunk_internal(const char *chunk, size_t size,\n                                     int fin)\n{\n  int rv = htmlParseChunk(parser_ctx_, chunk, size, fin);\n  if(rv == 0) {\n    return 0;\n  } else {\n    return -1;\n  }\n}\n\nconst std::vector<std::string>& HtmlParser::get_links() const\n{\n  return parser_data_.links;\n}\n\nvoid HtmlParser::clear_links()\n{\n  parser_data_.links.clear();\n}\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/HtmlParser.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef HTML_PARSER_H\n#define HTML_PARSER_H\n\n#include \"spdylay_config.h\"\n\n#include <vector>\n#include <string>\n\n#ifdef HAVE_LIBXML2\n\n#include <libxml/HTMLparser.h>\n\nnamespace spdylay {\n\nstruct ParserData {\n  std::string base_uri;\n  std::vector<std::string> links;\n  ParserData(const std::string& base_uri);\n};\n\nclass HtmlParser {\npublic:\n  HtmlParser(const std::string& base_uri);\n  ~HtmlParser();\n  int parse_chunk(const char *chunk, size_t size, int fin);\n  const std::vector<std::string>& get_links() const;\n  void clear_links();\nprivate:\n  int parse_chunk_internal(const char *chunk, size_t size, int fin);\n\n  std::string base_uri_;\n  htmlParserCtxtPtr parser_ctx_;\n  ParserData parser_data_;\n};\n\n} // namespace spdylay\n\n#else // !HAVE_LIBXML2\n\nnamespace spdylay {\n\nclass HtmlParser {\npublic:\n  HtmlParser(const std::string& base_uri) {}\n  ~HtmlParser() {}\n  int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }\n  const std::vector<std::string>& get_links() const { return links_; }\n  void clear_links() {}\nprivate:\n  std::vector<std::string> links_;\n};\n\n} // namespace spdylay\n\n#endif // !HAVE_LIBXML2\n\n#endif // HTML_PARSER_H\n"
  },
  {
    "path": "src/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nbin_PROGRAMS =\ncheck_PROGRAMS =\nTESTS =\n\nif ENABLE_SRC\n\nAM_CFLAGS = -Wall\nAM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \\\n\t@OPENSSL_CFLAGS@ @XML_CPPFLAGS@ @LIBEVENT_OPENSSL_CFLAGS@ @DEFS@\nAM_LDFLAGS = @OPENSSL_LIBS@ @XML_LIBS@ @LIBEVENT_OPENSSL_LIBS@ @SRC_LIBS@\n\nLDADD = $(top_builddir)/lib/libspdylay.la\n\nbin_PROGRAMS += spdycat\n\nif ENABLE_SPDYD\nbin_PROGRAMS += spdyd\nendif # ENABLE_SPDYD\n\nif HAVE_LIBEVENT_OPENSSL\nbin_PROGRAMS += shrpx\nendif # HAVE_LIBEVENT_OPENSSL\n\nHELPER_OBJECTS = util.cc timegm.c spdylay_ssl.cc\nHELPER_HFILES = util.h timegm.h spdylay_ssl.h spdylay_config.h\n\nEVENT_OBJECTS =\nEVENT_HFILES = EventPoll.h EventPollEvent.h\n\nif HAVE_EPOLL\nEVENT_OBJECTS += EventPoll_epoll.cc\nEVENT_HFILES += EventPoll_epoll.h\nendif # HAVE_EPOLL\n\nif HAVE_KQUEUE\nEVENT_OBJECTS += EventPoll_kqueue.cc\nEVENT_HFILES += EventPoll_kqueue.h\nendif # HAVE_KQUEUE\n\nHTML_PARSER_OBJECTS =\nHTML_PARSER_HFILES = HtmlParser.h\n\nif HAVE_LIBXML2\nHTML_PARSER_OBJECTS += HtmlParser.cc\nendif # HAVE_LIBXML2\n\nspdycat_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} spdycat.cc \\\n\t${HTML_PARSER_OBJECTS} ${HTML_PARSER_HFILES} \\\n\thttp-parser/http_parser.c http-parser/http_parser.h\n\nif ENABLE_SPDYD\nSPDY_SERVER_OBJECTS = SpdyServer.cc\nSPDY_SERVER_HFILES = SpdyServer.h\n\nspdyd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \\\n\t${EVENT_OBJECTS} ${EVENT_HFILES} \\\n\t${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \\\n\tspdyd.cc\nendif # ENABLE_SPDYD\n\nif HAVE_LIBEVENT_OPENSSL\nSHRPX_SRCS = \\\n\tutil.cc util.h timegm.c timegm.h base64.h \\\n\tshrpx_config.cc shrpx_config.h \\\n\tshrpx_error.h \\\n\tshrpx_listen_handler.cc shrpx_listen_handler.h \\\n\tshrpx_client_handler.cc shrpx_client_handler.h \\\n\tshrpx_upstream.h \\\n\tshrpx_spdy_upstream.cc shrpx_spdy_upstream.h \\\n\tshrpx_https_upstream.cc shrpx_https_upstream.h \\\n\tshrpx_downstream_queue.cc shrpx_downstream_queue.h \\\n\tshrpx_downstream.cc shrpx_downstream.h \\\n\tshrpx_downstream_connection.cc shrpx_downstream_connection.h \\\n\tshrpx_http_downstream_connection.cc shrpx_http_downstream_connection.h \\\n\tshrpx_spdy_downstream_connection.cc shrpx_spdy_downstream_connection.h \\\n\tshrpx_spdy_session.cc shrpx_spdy_session.h \\\n\tshrpx_log.cc shrpx_log.h \\\n\tshrpx_http.cc shrpx_http.h \\\n\tshrpx_io_control.cc shrpx_io_control.h \\\n\tshrpx_ssl.cc shrpx_ssl.h \\\n\tshrpx_thread_event_receiver.cc shrpx_thread_event_receiver.h \\\n\tshrpx_worker.cc shrpx_worker.h \\\n\tshrpx_accesslog.cc shrpx_accesslog.h\\\n\thttp-parser/http_parser.c http-parser/http_parser.h\n\nshrpx_SOURCES = ${SHRPX_SRCS} shrpx.cc shrpx.h\n\nif HAVE_CUNIT\ncheck_PROGRAMS += shrpx-unittest\nshrpx_unittest_SOURCES = shrpx-unittest.cc \\\n\tshrpx_ssl_test.cc shrpx_ssl_test.h\\\n\t${SHRPX_SRCS}\nshrpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\\\n\t -DSPDYLAY_TESTS_DIR=\\\"$(top_srcdir)/tests\\\"\nshrpx_unittest_LDFLAGS = -static @OPENSSL_LIBS@ @LIBEVENT_OPENSSL_LIBS@\\\n\t@SRC_LIBS@ @CUNIT_LIBS@ @TESTS_LIBS@\nTESTS += shrpx-unittest\nendif # HAVE_CUNIT\n\nendif # HAVE_LIBEVENT_OPENSSL\n\nendif # ENABLE_SRC\n"
  },
  {
    "path": "src/SpdyServer.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"SpdyServer.h\"\n\n#include <sys/stat.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n\n#include <cassert>\n#include <set>\n#include <iostream>\n\n#include <openssl/err.h>\n#include <zlib.h>\n\n#include \"spdylay_ssl.h\"\n#include \"util.h\"\n#include \"EventPoll.h\"\n\n#ifndef O_BINARY\n# define O_BINARY (0)\n#endif // O_BINARY\n\nnamespace spdylay {\n\nnamespace {\nConfig config;\nconst std::string STATUS_200 = \"200 OK\";\nconst std::string STATUS_304 = \"304 Not Modified\";\nconst std::string STATUS_400 = \"400 Bad Request\";\nconst std::string STATUS_404 = \"404 Not Found\";\nconst std::string DEFAULT_HTML = \"index.html\";\nconst std::string SPDYD_SERVER = \"spdyd spdylay/\" SPDYLAY_VERSION;\n} // namespace\n\nConfig::Config()\n  : on_request_recv_callback(0),\n    data_ptr(0),\n    port(0),\n    version(0),\n    verbose(false),\n    daemon(false),\n    verify_client(false),\n    no_tls(false)\n{}\n\nRequest::Request(int32_t stream_id)\n  : stream_id(stream_id),\n    file(-1)\n{}\n\nRequest::~Request()\n{\n  if(file != -1) {\n    close(file);\n  }\n}\n\nEventHandler::EventHandler(const Config *config)\n  : config_(config),\n    mark_del_(false)\n{}\n\nnamespace {\nvoid on_close(Sessions &sessions, EventHandler *hd);\n} // namespace\n\nclass Sessions {\npublic:\n  Sessions(int max_events, SSL_CTX *ssl_ctx)\n    : eventPoll_(max_events),\n      ssl_ctx_(ssl_ctx)\n  {}\n  ~Sessions()\n  {\n    for(std::set<EventHandler*>::iterator i = handlers_.begin(),\n          eoi = handlers_.end(); i != eoi; ++i) {\n      on_close(*this, *i);\n      delete *i;\n    }\n    SSL_CTX_free(ssl_ctx_);\n  }\n  void add_handler(EventHandler *handler)\n  {\n    handlers_.insert(handler);\n  }\n  void remove_handler(EventHandler *handler)\n  {\n    handlers_.erase(handler);\n  }\n  SSL* ssl_session_new(int fd)\n  {\n    SSL *ssl = SSL_new(ssl_ctx_);\n    if(SSL_set_fd(ssl, fd) == 0) {\n      SSL_free(ssl);\n      return 0;\n    }\n    return ssl;\n  }\n  int add_poll(EventHandler *handler)\n  {\n    return update_poll_internal(handler, EP_ADD);\n  }\n  int mod_poll(EventHandler *handler)\n  {\n    return update_poll_internal(handler, EP_MOD);\n  }\n  int poll(int timeout)\n  {\n    return eventPoll_.poll(timeout);\n  }\n  void* get_user_data(int p)\n  {\n    return eventPoll_.get_user_data(p);\n  }\n  int get_events(int p)\n  {\n    return eventPoll_.get_events(p);\n  }\nprivate:\n  int update_poll_internal(EventHandler *handler, int op)\n  {\n    int events = 0;\n    if(handler->want_read()) {\n      events |= EP_POLLIN;\n    }\n    if(handler->want_write()) {\n      events |= EP_POLLOUT;\n    }\n    return eventPoll_.ctl_event(op, handler->fd(), events, handler);\n  }\n\n  std::set<EventHandler*> handlers_;\n  EventPoll eventPoll_;\n  SSL_CTX *ssl_ctx_;\n};\n\nnamespace {\nvoid print_session_id(int64_t id)\n{\n  std::cout << \"[id=\" << id << \"] \";\n}\n} // namespace\n\nnamespace {\nvoid on_session_closed(EventHandler *hd, int64_t session_id)\n{\n  if(hd->config()->verbose) {\n    print_session_id(session_id);\n    print_timer();\n    std::cout << \" closed\" << std::endl;\n  }\n}\n} // namespace\n\nSpdyEventHandler::SpdyEventHandler(const Config* config,\n                                   int fd, SSL *ssl,\n                                   uint16_t version,\n                                   const spdylay_session_callbacks *callbacks,\n                                   int64_t session_id)\n  : EventHandler(config),\n    ssl_(ssl),\n    session_id_(session_id),\n    fd_(fd),\n    version_(version),\n    io_flags_(0)\n{\n  int r;\n  r = spdylay_session_server_new(&session_, version, callbacks, this);\n  assert(r == 0);\n  spdylay_settings_entry entry;\n  entry.settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  entry.value = 100;\n  entry.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  r = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE,\n                              &entry, 1);\n  assert(r == 0);\n}\n\nSpdyEventHandler::~SpdyEventHandler()\n{\n  on_session_closed(this, session_id_);\n  spdylay_session_del(session_);\n  for(std::map<int32_t, Request*>::iterator i = id2req_.begin(),\n        eoi = id2req_.end(); i != eoi; ++i) {\n    delete (*i).second;\n  }\n  if(ssl_) {\n    SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);\n    SSL_shutdown(ssl_);\n    SSL_free(ssl_);\n  }\n  shutdown(fd_, SHUT_WR);\n  close(fd_);\n}\n\nuint16_t SpdyEventHandler::version() const\n{\n  return version_;\n}\n\nint SpdyEventHandler::execute(Sessions *sessions)\n{\n  int r;\n\n  io_flags_ = 0;\n\n  r = spdylay_session_recv(session_);\n  if(r == 0) {\n    r = spdylay_session_send(session_);\n  }\n  return r;\n}\n\nbool SpdyEventHandler::want_read()\n{\n  return spdylay_session_want_read(session_) || (io_flags_ & WANT_READ);\n}\n\nbool SpdyEventHandler::want_write()\n{\n  return spdylay_session_want_write(session_) || (io_flags_ & WANT_WRITE);\n}\n\nint SpdyEventHandler::fd() const\n{\n  return fd_;\n}\n\nbool SpdyEventHandler::finish()\n{\n  return !spdylay_session_want_read(session_) &&\n    !spdylay_session_want_write(session_);\n}\n\nssize_t SpdyEventHandler::send_data(const uint8_t *data, size_t len, int flags)\n{\n  ssize_t r;\n\n  if(ssl_) {\n    ERR_clear_error();\n    r = SSL_write(ssl_, data, len);\n    if(r < 0) {\n      io_flags_ = get_ssl_io_demand(ssl_, r);\n    }\n  } else {\n    while((r = ::send(fd_, data, len, 0)) == -1 && errno == EINTR);\n    if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {\n      io_flags_ |= WANT_WRITE;\n    }\n  }\n  return r;\n}\n\nssize_t SpdyEventHandler::recv_data(uint8_t *data, size_t len, int flags)\n{\n  ssize_t r;\n\n  if(ssl_) {\n    ERR_clear_error();\n    r = SSL_read(ssl_, data, len);\n    if(r < 0) {\n      io_flags_ = get_ssl_io_demand(ssl_, r);\n    }\n  } else {\n    while((r = ::recv(fd_, data, len, 0)) == -1 && errno == EINTR);\n    if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {\n      io_flags_ |= WANT_READ;\n    }\n  }\n  return r;\n}\n\nbool SpdyEventHandler::would_block() const\n{\n  return io_flags_;\n}\n\nint SpdyEventHandler::submit_file_response(const std::string& status,\n                                           int32_t stream_id,\n                                           time_t last_modified,\n                                           off_t file_length,\n                                           spdylay_data_provider *data_prd)\n{\n  std::string date_str = util::http_date(time(0));\n  std::string content_length = util::to_str(file_length);\n  std::string last_modified_str;\n  const char *nv[] = {\n    \":status\", status.c_str(),\n    \":version\", \"HTTP/1.1\",\n    \"server\", SPDYD_SERVER.c_str(),\n    \"content-length\", content_length.c_str(),\n    \"cache-control\", \"max-age=3600\",\n    \"date\", date_str.c_str(),\n    0, 0,\n    0\n  };\n  if(last_modified != 0) {\n    last_modified_str = util::http_date(last_modified);\n    nv[12] = \"last-modified\";\n    nv[13] = last_modified_str.c_str();\n  }\n  return spdylay_submit_response(session_, stream_id, nv, data_prd);\n}\n\nint SpdyEventHandler::submit_response\n(const std::string& status,\n int32_t stream_id,\n const std::vector<std::pair<std::string, std::string> >& headers,\n spdylay_data_provider *data_prd)\n{\n  std::string date_str = util::http_date(time(0));\n  const char **nv = new const char*[8+headers.size()*2+1];\n  nv[0] = \":status\";\n  nv[1] = status.c_str();\n  nv[2] = \":version\";\n  nv[3] = \"HTTP/1.1\";\n  nv[4] = \"server\";\n  nv[5] = SPDYD_SERVER.c_str();\n  nv[6] = \"date\";\n  nv[7] = date_str.c_str();\n  for(size_t i = 0; i < headers.size(); ++i) {\n    nv[8+i*2] = headers[i].first.c_str();\n    nv[8+i*2+1] = headers[i].second.c_str();\n  }\n  nv[8+headers.size()*2] = 0;\n  int r = spdylay_submit_response(session_, stream_id, nv, data_prd);\n  delete [] nv;\n  return r;\n}\n\nint SpdyEventHandler::submit_response(const std::string& status,\n                                      int32_t stream_id,\n                                      spdylay_data_provider *data_prd)\n{\n  const char *nv[] = {\n    \":status\", status.c_str(),\n    \":version\", \"HTTP/1.1\",\n    \"server\", SPDYD_SERVER.c_str(),\n    0\n  };\n  return spdylay_submit_response(session_, stream_id, nv, data_prd);\n}\n\nvoid SpdyEventHandler::add_stream(int32_t stream_id, Request *req)\n{\n  id2req_[stream_id] = req;\n}\n\nvoid SpdyEventHandler::remove_stream(int32_t stream_id)\n{\n  Request *req = id2req_[stream_id];\n  id2req_.erase(stream_id);\n  delete req;\n}\n\nRequest* SpdyEventHandler::get_stream(int32_t stream_id)\n{\n  return id2req_[stream_id];\n}\n\nint64_t SpdyEventHandler::session_id() const\n{\n  return session_id_;\n}\n\nnamespace {\nssize_t hd_send_callback(spdylay_session *session,\n                         const uint8_t *data, size_t len, int flags,\n                         void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  ssize_t r = hd->send_data(data, len, flags);\n  if(r < 0) {\n    if(hd->would_block()) {\n      r = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      r = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(r == 0) {\n    // In OpenSSL, r == 0 means EOF because SSL_write may do read.\n    r = SPDYLAY_ERR_CALLBACK_FAILURE;\n  }\n  return r;\n}\n} // namespace\n\nnamespace {\nssize_t hd_recv_callback(spdylay_session *session,\n                         uint8_t *data, size_t len, int flags, void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  ssize_t r = hd->recv_data(data, len, flags);\n  if(r < 0) {\n    if(hd->would_block()) {\n      r = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      r = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(r == 0) {\n    r = SPDYLAY_ERR_EOF;\n  }\n  return r;\n}\n} // namespace\n\nssize_t file_read_callback\n(spdylay_session *session, int32_t stream_id,\n uint8_t *buf, size_t length, int *eof,\n spdylay_data_source *source, void *user_data)\n{\n  int fd = source->fd;\n  ssize_t r;\n  while((r = read(fd, buf, length)) == -1 && errno == EINTR);\n  if(r == -1) {\n    return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;\n  } else {\n    if(r == 0) {\n      *eof = 1;\n    }\n    return r;\n  }\n}\n\nnamespace {\nbool check_url(const std::string& url)\n{\n  // We don't like '\\' in url.\n  return !url.empty() && url[0] == '/' &&\n    url.find('\\\\') == std::string::npos &&\n    url.find(\"/../\") == std::string::npos &&\n    url.find(\"/./\") == std::string::npos &&\n    !util::endsWith(url, \"/..\") && !util::endsWith(url, \"/.\");\n}\n} // namespace\n\nnamespace {\nvoid prepare_status_response(Request *req, SpdyEventHandler *hd,\n                             const std::string& status)\n{\n  int pipefd[2];\n  if(status == STATUS_304 || pipe(pipefd) == -1) {\n    hd->submit_response(status, req->stream_id, 0);\n  } else {\n    std::stringstream ss;\n    ss << \"<html><head><title>\" << status << \"</title></head><body>\"\n       << \"<h1>\" << status << \"</h1>\"\n       << \"<hr>\"\n       << \"<address>\" << SPDYD_SERVER << \" at port \" << hd->config()->port\n       << \"</address>\"\n       << \"</body></html>\";\n    std::string body = ss.str();\n    gzFile write_fd = gzdopen(pipefd[1], \"w\");\n    gzwrite(write_fd, body.c_str(), body.size());\n    gzclose(write_fd);\n    close(pipefd[1]);\n\n    req->file = pipefd[0];\n    spdylay_data_provider data_prd;\n    data_prd.source.fd = pipefd[0];\n    data_prd.read_callback = file_read_callback;\n    std::vector<std::pair<std::string, std::string> > headers;\n    headers.push_back(std::make_pair(\"content-encoding\", \"gzip\"));\n    headers.push_back(std::make_pair(\"content-type\",\n                                     \"text/html; charset=UTF-8\"));\n    hd->submit_response(status, req->stream_id, headers, &data_prd);\n  }\n}\n} // namespace\n\nnamespace {\nvoid prepare_response(Request *req, SpdyEventHandler *hd)\n{\n  std::string url;\n  bool url_found = false;\n  bool method_found = false;\n  bool scheme_found = false;\n  bool version_found = false;\n  bool host_found = false;\n  time_t last_mod = 0;\n  bool last_mod_found = false;\n  for(size_t i = 0; i < req->headers.size(); ++i) {\n    const std::string &field = req->headers[i].first;\n    const std::string &value = req->headers[i].second;\n    if(!url_found && field == \":path\") {\n      url_found = true;\n      url = value;\n    } else if(field == \":method\") {\n      method_found = true;\n    } else if(field == \":scheme\") {\n      scheme_found = true;\n    } else if(field == \":version\") {\n      version_found = true;\n    } else if(field == \":host\") {\n      host_found = true;\n    } else if(!last_mod_found && field == \"if-modified-since\") {\n      last_mod_found = true;\n      last_mod = util::parse_http_date(value);\n    }\n  }\n  if(!url_found || !method_found || !scheme_found || !version_found ||\n     !host_found) {\n    prepare_status_response(req, hd, STATUS_400);\n    return;\n  }\n  size_t query_pos = url.find(\"?\");\n  if(query_pos != std::string::npos) {\n    // Do not response to this request to allow clients to test timeouts.\n    if (url.find(\"spdyd_do_not_respond_to_req=yes\",\n                 query_pos) != std::string::npos) {\n      return;\n    }\n    url = url.substr(0, query_pos);\n  }\n  url = util::percentDecode(url.begin(), url.end());\n  if(!check_url(url)) {\n    prepare_status_response(req, hd, STATUS_404);\n    return;\n  }\n  std::string path = hd->config()->htdocs+url;\n  if(path[path.size()-1] == '/') {\n    path += DEFAULT_HTML;\n  }\n  int file = open(path.c_str(), O_RDONLY | O_BINARY);\n  if(file == -1) {\n    prepare_status_response(req, hd, STATUS_404);\n  } else {\n    struct stat buf;\n    if(fstat(file, &buf) == -1) {\n      close(file);\n      prepare_status_response(req, hd, STATUS_404);\n    } else {\n      req->file = file;\n      spdylay_data_provider data_prd;\n      data_prd.source.fd = file;\n      data_prd.read_callback = file_read_callback;\n      if(last_mod_found && buf.st_mtime <= last_mod) {\n        prepare_status_response(req, hd, STATUS_304);\n      } else {\n        hd->submit_file_response(STATUS_200, req->stream_id, buf.st_mtime,\n                                 buf.st_size, &data_prd);\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid append_nv(Request *req, char **nv)\n{\n  for(int i = 0; nv[i]; i += 2) {\n    req->headers.push_back(std::make_pair(nv[i], nv[i+1]));\n  }\n}\n} // namespace\n\nnamespace {\nvoid hd_on_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  if(hd->config()->verbose) {\n    print_session_id(hd->session_id());\n    on_ctrl_recv_callback(session, type, frame, user_data);\n  }\n  switch(type) {\n  case SPDYLAY_SYN_STREAM: {\n    int32_t stream_id = frame->syn_stream.stream_id;\n    Request *req = new Request(stream_id);\n    append_nv(req, frame->syn_stream.nv);\n    hd->add_stream(stream_id, req);\n    break;\n  }\n  case SPDYLAY_HEADERS: {\n    int32_t stream_id = frame->headers.stream_id;\n    Request *req = hd->get_stream(stream_id);\n    append_nv(req, frame->headers.nv);\n    break;\n  }\n  default:\n    break;\n  }\n}\n} // namespace\n\nvoid htdocs_on_request_recv_callback\n(spdylay_session *session, int32_t stream_id, void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  prepare_response(hd->get_stream(stream_id), hd);\n}\n\nnamespace {\nvoid hd_on_ctrl_send_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  if(hd->config()->verbose) {\n    print_session_id(hd->session_id());\n    on_ctrl_send_callback(session, type, frame, user_data);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_data_chunk_recv_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id,\n const uint8_t *data, size_t len, void *user_data)\n{\n  // TODO Handle POST\n}\n} // namespace\n\nnamespace {\nvoid hd_on_data_recv_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data)\n{\n  // TODO Handle POST\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  if(hd->config()->verbose) {\n    print_session_id(hd->session_id());\n    on_data_recv_callback(session, flags, stream_id, length, user_data);\n  }\n}\n} // namespace\n\nnamespace {\nvoid hd_on_data_send_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  if(hd->config()->verbose) {\n    print_session_id(hd->session_id());\n    on_data_send_callback(session, flags, stream_id, length, user_data);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_stream_close_callback\n(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,\n void *user_data)\n{\n  SpdyEventHandler *hd = (SpdyEventHandler*)user_data;\n  hd->remove_stream(stream_id);\n  if(hd->config()->verbose) {\n    print_session_id(hd->session_id());\n    print_timer();\n    printf(\" stream_id=%d closed\\n\", stream_id);\n    fflush(stdout);\n  }\n}\n} // namespace\n\nnamespace {\nvoid fill_callback(spdylay_session_callbacks& callbacks, const Config *config)\n{\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = hd_send_callback;\n  callbacks.recv_callback = hd_recv_callback;\n  callbacks.on_stream_close_callback = on_stream_close_callback;\n  callbacks.on_ctrl_recv_callback = hd_on_ctrl_recv_callback;\n  callbacks.on_ctrl_send_callback = hd_on_ctrl_send_callback;\n  callbacks.on_data_recv_callback = hd_on_data_recv_callback;\n  callbacks.on_data_send_callback = hd_on_data_send_callback;\n  if(config->verbose) {\n    callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n    callbacks.on_ctrl_recv_parse_error_callback =\n      on_ctrl_recv_parse_error_callback;\n    callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;\n  }\n  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n  callbacks.on_request_recv_callback = config->on_request_recv_callback;\n}\n} // namespace\n\nclass SSLAcceptEventHandler : public EventHandler {\npublic:\n  SSLAcceptEventHandler(const Config *config,\n                        int fd, SSL *ssl, int64_t session_id)\n    : EventHandler(config),\n      ssl_(ssl),\n      session_id_(session_id),\n      fd_(fd),\n      version_(0),\n      io_flags_(WANT_READ),\n      fail_(false),\n      finish_(false)\n  {}\n  virtual ~SSLAcceptEventHandler()\n  {\n    if(fail_) {\n      on_session_closed(this, session_id_);\n      SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);\n      SSL_shutdown(ssl_);\n      SSL_free(ssl_);\n      shutdown(fd_, SHUT_WR);\n      close(fd_);\n    }\n  }\n  virtual int execute(Sessions *sessions)\n  {\n    io_flags_ = 0;\n    ERR_clear_error();\n    int r = SSL_accept(ssl_);\n    if(r == 1) {\n      finish_ = true;\n      const unsigned char *next_proto = 0;\n      unsigned int next_proto_len;\n      SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);\n      if(next_proto) {\n        std::string proto(next_proto, next_proto+next_proto_len);\n        if(config()->verbose) {\n          std::cout << \"The negotiated next protocol: \" << proto << std::endl;\n        }\n        version_ = spdylay_npn_get_version(next_proto, next_proto_len);\n        if(config()->version != 0) {\n          if(config()->version != version_) {\n            version_ = 0;\n            std::cerr << \"The negotiated next protocol is not supported.\"\n                      << std::endl;\n          }\n        }\n        if(version_) {\n          add_next_handler(sessions);\n        } else {\n          fail_ = true;\n        }\n      } else {\n        fail_ = true;\n      }\n    } else if(r == 0) {\n      finish_ = true;\n      fail_ = true;\n    } else {\n      io_flags_ = get_ssl_io_demand(ssl_, r);\n      if(io_flags_ == 0) {\n        finish_ = true;\n        fail_ = true;\n      }\n    }\n    if(config()->verbose && fail_) {\n      std::cerr << \"SSL/TLS handshake failed: \"\n                << ERR_error_string(ERR_get_error(), 0) << std::endl;\n    }\n    return 0;\n  }\n  virtual bool want_read()\n  {\n    return io_flags_ & WANT_READ;\n  }\n  virtual bool want_write()\n  {\n    return io_flags_ & WANT_WRITE;\n  }\n  virtual int fd() const\n  {\n    return fd_;\n  }\n  virtual bool finish()\n  {\n    return finish_;\n  }\nprivate:\n  void add_next_handler(Sessions *sessions)\n  {\n    spdylay_session_callbacks callbacks;\n    fill_callback(callbacks, config());\n    SpdyEventHandler *hd = new SpdyEventHandler(config(),\n                                                fd_, ssl_, version_, &callbacks,\n                                                session_id_);\n    if(sessions->mod_poll(hd) == -1) {\n      // fd_, ssl_ are freed by ~SpdyEventHandler()\n      delete hd;\n    } else {\n      sessions->add_handler(hd);\n    }\n  }\n\n  SSL *ssl_;\n  int64_t session_id_;\n  int fd_;\n  uint16_t version_;\n  uint8_t io_flags_;\n  bool fail_, finish_;\n};\n\nclass ListenEventHandler : public EventHandler {\npublic:\n  ListenEventHandler(const Config* config,\n                     int fd, int64_t *session_id_seed_ptr)\n    : EventHandler(config),\n      session_id_seed_ptr_(session_id_seed_ptr),\n      fd_(fd)\n  {}\n  virtual ~ListenEventHandler()\n  {}\n  virtual int execute(Sessions *sessions)\n  {\n    int cfd;\n    while((cfd = accept(fd_, 0, 0)) == -1 && errno == EINTR);\n    if(cfd != -1) {\n      if(make_non_block(cfd) == -1 ||\n         set_tcp_nodelay(cfd) == -1) {\n        close(cfd);\n      } else {\n        add_next_handler(sessions, cfd);\n      }\n    }\n    return 0;\n  }\n  virtual bool want_read()\n  {\n    return true;\n  }\n  virtual bool want_write()\n  {\n    return false;\n  }\n  virtual int fd() const\n  {\n    return fd_;\n  }\n  virtual bool finish()\n  {\n    return false;\n  }\nprivate:\n  void add_next_handler(Sessions *sessions, int cfd)\n  {\n    int64_t session_id = ++(*session_id_seed_ptr_);\n    if(config()->no_tls) {\n      spdylay_session_callbacks callbacks;\n      fill_callback(callbacks, config());\n      SpdyEventHandler *hd = new SpdyEventHandler(config(),\n                                                  cfd, 0,\n                                                  config()->version, &callbacks,\n                                                  session_id);\n      if(sessions->add_poll(hd) == -1) {\n        delete hd;\n      } else {\n        sessions->add_handler(hd);\n      }\n    } else {\n      SSL *ssl = sessions->ssl_session_new(cfd);\n      if(ssl == 0) {\n        close(cfd);\n        return;\n      }\n      SSLAcceptEventHandler *hd = new SSLAcceptEventHandler(config(), cfd, ssl,\n                                                            session_id);\n      if(sessions->add_poll(hd) == -1) {\n        delete hd;\n        SSL_free(ssl);\n        close(cfd);\n      } else {\n        sessions->add_handler(hd);\n      }\n    }\n  }\n\n  int64_t *session_id_seed_ptr_;\n  int fd_;\n};\n\nnamespace {\nvoid on_close(Sessions &sessions, EventHandler *hd)\n{\n  sessions.remove_handler(hd);\n  delete hd;\n}\n} // namespace\n\nSpdyServer::SpdyServer(const Config *config)\n  : config_(config)\n{\n  memset(sfd_, -1, sizeof(sfd_));\n}\n\nSpdyServer::~SpdyServer()\n{\n  for(int i = 0; i < 2; ++i) {\n    if(sfd_[i] != -1) {\n      close(sfd_[i]);\n    }\n  }\n}\n\nint SpdyServer::listen()\n{\n  int families[] = { AF_INET, AF_INET6 };\n  bool bind_ok = false;\n  for(int i = 0; i < 2; ++i) {\n    const char* ipv = (families[i] == AF_INET ? \"IPv4\" : \"IPv6\");\n    int sfd = make_listen_socket(config_->host, config_->port, families[i]);\n    if(sfd == -1) {\n      std::cerr << ipv << \": Could not listen on port \" << config_->port\n                << std::endl;\n      continue;\n    }\n    make_non_block(sfd);\n    sfd_[i] = sfd;\n    if(config_->verbose) {\n      std::cout << ipv << \": listen on port \" << config_->port << std::endl;\n    }\n    bind_ok = true;\n  }\n  if(!bind_ok) {\n    return -1;\n  }\n  return 0;\n}\n\nnamespace {\nint next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,\n                  void *arg)\n{\n  std::pair<unsigned char*, size_t> *next_proto =\n    static_cast<std::pair<unsigned char*, size_t>* >(arg);\n  *data = next_proto->first;\n  *len = next_proto->second;\n  return SSL_TLSEXT_ERR_OK;\n}\n} // namespace\n\nnamespace {\nint verify_callback(int preverify_ok, X509_STORE_CTX *ctx)\n{\n  // We don't verify the client certificate. Just request it for the\n  // testing purpose.\n  return 1;\n}\n} // namespace\n\nint SpdyServer::run()\n{\n  SSL_CTX *ssl_ctx = 0;\n  std::pair<unsigned char*, size_t> next_proto;\n  unsigned char proto_list[23];\n  if(!config_->no_tls) {\n    ssl_ctx = SSL_CTX_new(SSLv23_server_method());\n    if(!ssl_ctx) {\n      std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n      return -1;\n    }\n    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);\n    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);\n    SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n    if(SSL_CTX_use_PrivateKey_file(ssl_ctx,\n                                   config_->private_key_file.c_str(),\n                                   SSL_FILETYPE_PEM) != 1) {\n      std::cerr << \"SSL_CTX_use_PrivateKey_file failed.\" << std::endl;\n      return -1;\n    }\n    if(SSL_CTX_use_certificate_chain_file(ssl_ctx,\n                                          config_->cert_file.c_str()) != 1) {\n      std::cerr << \"SSL_CTX_use_certificate_file failed.\" << std::endl;\n      return -1;\n    }\n    if(SSL_CTX_check_private_key(ssl_ctx) != 1) {\n      std::cerr << \"SSL_CTX_check_private_key failed.\" << std::endl;\n      return -1;\n    }\n    if(config_->verify_client) {\n      SSL_CTX_set_verify(ssl_ctx,\n                         SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |\n                         SSL_VERIFY_FAIL_IF_NO_PEER_CERT,\n                         verify_callback);\n    }\n\n    // We speaks \"spdy/3.1\", \"spdy/3\" and \"spdy/2\".\n    proto_list[0] = 8;\n    memcpy(&proto_list[1], \"spdy/3.1\", 8);\n    proto_list[9] = 6;\n    memcpy(&proto_list[10], \"spdy/3\", 6);\n    proto_list[16] = 6;\n    memcpy(&proto_list[17], \"spdy/2\", 6);\n\n    switch(config_->version) {\n    case SPDYLAY_PROTO_SPDY3_1:\n      next_proto.first = proto_list;\n      next_proto.second = 9;\n      break;\n    case SPDYLAY_PROTO_SPDY3:\n      next_proto.first = proto_list+9;\n      next_proto.second = 7;\n      break;\n    case SPDYLAY_PROTO_SPDY2:\n      next_proto.first = proto_list+16;\n      next_proto.second = 7;\n      break;\n    default:\n      next_proto.first = proto_list;\n      next_proto.second = sizeof(proto_list);\n    }\n    SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);\n  }\n\n  const size_t MAX_EVENTS = 256;\n  Sessions sessions(MAX_EVENTS, ssl_ctx);\n\n  int64_t session_id_seed = 0;\n  int families[] = { AF_INET, AF_INET6 };\n  bool bind_ok = false;\n  for(int i = 0; i < 2; ++i) {\n    const char* ipv = (families[i] == AF_INET ? \"IPv4\" : \"IPv6\");\n    if(sfd_[i] == -1) {\n      continue;\n    }\n    ListenEventHandler *listen_hd = new ListenEventHandler(config_,\n                                                           sfd_[i],\n                                                           &session_id_seed);\n    if(sessions.add_poll(listen_hd) == -1) {\n      std::cerr <<  ipv << \": Adding listening socket to poll failed.\"\n                << std::endl;\n      delete listen_hd;\n      continue;\n    }\n    sessions.add_handler(listen_hd);\n    bind_ok = true;\n  }\n  if(!bind_ok) {\n    return -1;\n  }\n\n  std::vector<EventHandler*> del_list;\n  while(1) {\n    int n = sessions.poll(-1);\n    if(n == -1) {\n      perror(\"EventPoll\");\n    } else {\n      for(int i = 0; i < n; ++i) {\n        EventHandler *hd =\n          static_cast<EventHandler*>(sessions.get_user_data(i));\n        int events = sessions.get_events(i);\n        int r = 0;\n        if(hd->mark_del()) {\n          continue;\n        }\n        if((events & EP_POLLIN) || (events & EP_POLLOUT)) {\n          r = hd->execute(&sessions);\n        } else if(events & (EP_POLLERR | EP_POLLHUP)) {\n          hd->mark_del(true);\n        }\n        if(r != 0) {\n          hd->mark_del(true);\n        } else {\n          if(hd->finish()) {\n            hd->mark_del(true);\n          } else {\n            sessions.mod_poll(hd);\n          }\n        }\n        if(hd->mark_del()) {\n          del_list.push_back(hd);\n        }\n      }\n      for(std::vector<EventHandler*>::iterator i = del_list.begin(),\n            eoi = del_list.end(); i != eoi; ++i) {\n        on_close(sessions, *i);\n        sessions.remove_handler(*i);\n      }\n      del_list.clear();\n    }\n  }\n  return 0;\n}\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/SpdyServer.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDY_SERVER_H\n#define SPDY_SERVER_H\n\n#include \"spdylay_config.h\"\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <cstdlib>\n\n#include <string>\n#include <vector>\n#include <map>\n\n#include <openssl/ssl.h>\n\n#include <spdylay/spdylay.h>\n\nnamespace spdylay {\n\nstruct Config {\n  std::string htdocs;\n  std::string host;\n  std::string private_key_file;\n  std::string cert_file;\n  spdylay_on_request_recv_callback on_request_recv_callback;\n  void *data_ptr;\n  uint16_t port;\n  uint16_t version;\n  bool verbose;\n  bool daemon;\n  bool verify_client;\n  bool no_tls;\n  Config();\n};\n\nclass Sessions;\n\nclass EventHandler {\npublic:\n  EventHandler(const Config *config);\n  virtual ~EventHandler() {}\n  virtual int execute(Sessions *sessions) = 0;\n  virtual bool want_read() = 0;\n  virtual bool want_write() = 0;\n  virtual int fd() const = 0;\n  virtual bool finish() = 0;\n  const Config* config() const\n  {\n    return config_;\n  }\n  bool mark_del()\n  {\n    return mark_del_;\n  }\n  void mark_del(bool d)\n  {\n    mark_del_ = d;\n  }\nprivate:\n  const Config *config_;\n  bool mark_del_;\n};\n\nstruct Request {\n  std::vector<std::pair<std::string, std::string> > headers;\n  std::pair<std::string, size_t> response_body;\n  int32_t stream_id;\n  int file;\n  Request(int32_t stream_id);\n  ~Request();\n};\n\nclass SpdyEventHandler : public EventHandler {\npublic:\n  SpdyEventHandler(const Config* config,\n                   int fd, SSL *ssl, uint16_t version,\n                   const spdylay_session_callbacks *callbacks,\n                   int64_t session_id);\n  virtual ~SpdyEventHandler();\n  virtual int execute(Sessions *sessions);\n  virtual bool want_read();\n  virtual bool want_write();\n  virtual int fd() const;\n  virtual bool finish();\n\n  uint16_t version() const;\n\n  ssize_t send_data(const uint8_t *data, size_t len, int flags);\n\n  ssize_t recv_data(uint8_t *data, size_t len, int flags);\n\n  bool would_block() const;\n\n  int submit_file_response(const std::string& status,\n                           int32_t stream_id,\n                           time_t last_modified,\n                           off_t file_length,\n                           spdylay_data_provider *data_prd);\n\n  int submit_response(const std::string& status,\n                      int32_t stream_id,\n                      spdylay_data_provider *data_prd);\n\n  int submit_response\n  (const std::string& status,\n   int32_t stream_id,\n   const std::vector<std::pair<std::string, std::string> >& headers,\n   spdylay_data_provider *data_prd);\n\n  void add_stream(int32_t stream_id, Request *req);\n  void remove_stream(int32_t stream_id);\n  Request* get_stream(int32_t stream_id);\n  int64_t session_id() const;\nprivate:\n  std::map<int32_t, Request*> id2req_;\n  spdylay_session *session_;\n  SSL* ssl_;\n  int64_t session_id_;\n  int fd_;\n  uint16_t version_;\n  uint8_t io_flags_;\n};\n\nclass SpdyServer {\npublic:\n  SpdyServer(const Config* config);\n  ~SpdyServer();\n  int listen();\n  int run();\nprivate:\n  const Config *config_;\n  int sfd_[2];\n};\n\nvoid htdocs_on_request_recv_callback\n(spdylay_session *session, int32_t stream_id, void *user_data);\n\nssize_t file_read_callback\n(spdylay_session *session, int32_t stream_id,\n uint8_t *buf, size_t length, int *eof,\n spdylay_data_source *source, void *user_data);\n\n} // namespace spdylay\n\n#endif // SPDY_SERVER_H\n"
  },
  {
    "path": "src/base64.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef BASE64_H\n#define BASE64_H\n\n#include <string>\n\nnamespace spdylay {\n\nnamespace base64 {\n\ntemplate<typename InputIterator>\nstd::string encode(InputIterator first, InputIterator last)\n{\n  static const char CHAR_TABLE[] = {\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',\n    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\n    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',\n    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',\n    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',\n    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\n    'w', 'x', 'y', 'z', '0', '1', '2', '3',\n    '4', '5', '6', '7', '8', '9', '+', '/',\n  };\n  std::string res;\n  size_t len = last-first;\n  if(len == 0) {\n    return res;\n  }\n  size_t r = len%3;\n  InputIterator j = last-r;\n  char temp[4];\n  while(first != j) {\n    int n = static_cast<unsigned char>(*first++) << 16;\n    n += static_cast<unsigned char>(*first++) << 8;\n    n += static_cast<unsigned char>(*first++);\n    temp[0] = CHAR_TABLE[n >> 18];\n    temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];\n    temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu];\n    temp[3] = CHAR_TABLE[n & 0x3fu];\n    res.append(temp, sizeof(temp));\n  }\n  if(r == 2) {\n    int n = static_cast<unsigned char>(*first++) << 16;\n    n += static_cast<unsigned char>(*first++) << 8;\n    temp[0] = CHAR_TABLE[n >> 18];\n    temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];\n    temp[2] = CHAR_TABLE[(n >> 6) & 0x3fu];\n    temp[3] = '=';\n    res.append(temp, sizeof(temp));\n  } else if(r == 1) {\n    int n = static_cast<unsigned char>(*first++) << 16;\n    temp[0] = CHAR_TABLE[n >> 18];\n    temp[1] = CHAR_TABLE[(n >> 12) & 0x3fu];\n    temp[2] = '=';\n    temp[3] = '=';\n    res.append(temp, sizeof(temp));\n  }\n  return res;\n}\n\ntemplate<typename InputIterator>\nInputIterator getNext\n(InputIterator first,\n InputIterator last,\n const int* tbl)\n{\n  for(; first != last; ++first) {\n    if(tbl[static_cast<size_t>(*first)] != -1 || *first == '=') {\n      break;\n    }\n  }\n  return first;\n}\n\ntemplate<typename InputIterator>\nstd::string decode(InputIterator first, InputIterator last)\n{\n  static const int INDEX_TABLE[] = {\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,\n    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,\n    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,\n    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\n  };\n  std::string res;\n  InputIterator k[4];\n  int eq = 0;\n  for(; first != last;) {\n    for(int i = 1; i <= 4; ++i) {\n      k[i-1] = getNext(first, last, INDEX_TABLE);\n      if(k[i-1] == last) {\n        // If i == 1, input may look like this: \"TWFu\\n\" (i.e.,\n        // garbage at the end)\n        if(i != 1) {\n          res.clear();\n        }\n        return res;\n      } else if(*k[i-1] == '=' && eq == 0) {\n        eq = i;\n      }\n      first = k[i-1]+1;\n    }\n    if(eq) {\n      break;\n    }\n    int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18)+\n      (INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12)+\n      (INDEX_TABLE[static_cast<unsigned char>(*k[2])] << 6)+\n      INDEX_TABLE[static_cast<unsigned char>(*k[3])];\n    res += n >> 16;\n    res += n >> 8 & 0xffu;\n    res += n & 0xffu;\n  }\n  if(eq) {\n    if(eq <= 2) {\n      res.clear();\n      return res;\n    } else {\n      for(int i = eq; i <= 4; ++i) {\n        if(*k[i-1] != '=') {\n          res.clear();\n          return res;\n        }\n      }\n      if(eq == 3) {\n        int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18)+\n          (INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12);\n        res += n >> 16;\n      } else if(eq == 4) {\n        int n = (INDEX_TABLE[static_cast<unsigned char>(*k[0])] << 18)+\n          (INDEX_TABLE[static_cast<unsigned char>(*k[1])] << 12)+\n          (INDEX_TABLE[static_cast<unsigned char>(*k[2])] << 6);\n        res += n >> 16;\n        res += n >> 8 & 0xffu;\n      }\n    }\n  }\n  return res;\n}\n\n} // namespace base64\n\n} // namespace spdylay\n\n#endif // BASE64_H\n"
  },
  {
    "path": "src/http-parser/0001-Support-custom-HTTP-version-string-e.g.-ICY.patch",
    "content": "From 0230de8d757d45dad9ab9fa8cb84613b53eb80b6 Mon Sep 17 00:00:00 2001\nFrom: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>\nDate: Fri, 21 Feb 2014 21:59:40 +0900\nSubject: [PATCH 1/3] Support custom HTTP-version string (e.g., ICY)\n\n---\n http_parser.c | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/http_parser.c b/http_parser.c\nindex cbe8a90..c0825b0 100644\n--- a/http_parser.c\n+++ b/http_parser.c\n@@ -282,6 +282,7 @@ enum state\n   , s_start_req_or_res\n   , s_res_or_resp_H\n   , s_start_res\n+  , s_res_proto_custom\n   , s_res_H\n   , s_res_HT\n   , s_res_HTT\n@@ -761,6 +762,10 @@ reexecute:\n             break;\n \n           default:\n+            if(TOKEN(ch)) {\n+              parser->state = s_res_proto_custom;\n+              break;\n+            }\n             SET_ERRNO(HPE_INVALID_CONSTANT);\n             goto error;\n         }\n@@ -769,6 +774,21 @@ reexecute:\n         break;\n       }\n \n+      /* Custom HTTP-version string (e.g., ICY) */\n+      case s_res_proto_custom:\n+        if (ch == ' ') {\n+          /* We assume this is HTTP/1.0 */\n+          parser->http_major = 1;\n+          parser->http_minor = 0;\n+          parser->state = s_res_first_status_code;\n+          break;\n+        }\n+        if (!TOKEN(ch)) {\n+          SET_ERRNO(HPE_INVALID_CONSTANT);\n+          goto error;\n+        }\n+        break;\n+\n       case s_res_H:\n         STRICT_CHECK(ch != 'T');\n         UPDATE_STATE(s_res_HT);\n-- \n2.1.4\n\n"
  },
  {
    "path": "src/http-parser/0002-Allow-chars-in-32-126-except-for-58-for-TOKEN.patch",
    "content": "From fad236762ca992a8a4a2b7f6c7077ddb53da5888 Mon Sep 17 00:00:00 2001\nFrom: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>\nDate: Fri, 21 Feb 2014 22:05:34 +0900\nSubject: [PATCH 2/3] Allow chars in [32, 126], except for 58 (':'), for TOKEN\n\n---\n http_parser.c | 12 ++++++------\n 1 file changed, 6 insertions(+), 6 deletions(-)\n\ndiff --git a/http_parser.c b/http_parser.c\nindex c0825b0..60c992e 100644\n--- a/http_parser.c\n+++ b/http_parser.c\n@@ -196,21 +196,21 @@ static const char tokens[256] = {\n /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n         0,       0,       0,       0,       0,       0,       0,       0,\n /*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n-        0,      '!',      0,      '#',     '$',     '%',     '&',    '\\'',\n+       ' ',     '!',     '\"',     '#',     '$',     '%',     '&',    '\\'',\n /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n-        0,       0,      '*',     '+',      0,      '-',     '.',      0,\n+       '(',     ')',     '*',     '+',     ',',     '-',     '.',     '/',\n /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n        '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',\n /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n-       '8',     '9',      0,       0,       0,       0,       0,       0,\n+       '8',     '9',      0,      ';',     '<',     '=',     '>',     '?',\n /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n-        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',\n+       '@',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n        'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n /*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n-       'x',     'y',     'z',      0,       0,       0,      '^',     '_',\n+       'x',     'y',     'z',     '[',    '\\\\',     ']',     '^',     '_',\n /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n        '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n@@ -218,7 +218,7 @@ static const char tokens[256] = {\n /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n-       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };\n+       'x',     'y',     'z',     '{',     '|',     '}',     '~',       0 };\n \n \n static const int8_t unhex[256] =\n-- \n2.1.4\n\n"
  },
  {
    "path": "src/http-parser/0003-Use-http_parser-for-tunneling-connection-transparent.patch",
    "content": "From 0bde8052816e615cb4216e5ef07233ee4e625951 Mon Sep 17 00:00:00 2001\nFrom: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>\nDate: Mon, 10 Sep 2012 23:01:26 +0900\nSubject: [PATCH 3/3] Use http_parser for tunneling connection transparently\n\n---\n http_parser.c | 11 ++++++++---\n 1 file changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/http_parser.c b/http_parser.c\nindex 60c992e..f6c84e9 100644\n--- a/http_parser.c\n+++ b/http_parser.c\n@@ -1857,9 +1857,14 @@ reexecute:\n         if (parser->upgrade && (parser->method == HTTP_CONNECT ||\n                                 (parser->flags & F_SKIPBODY) || !hasBody)) {\n           /* Exit, the rest of the message is in a different protocol. */\n-          UPDATE_STATE(NEW_MESSAGE());\n-          CALLBACK_NOTIFY(message_complete);\n-          RETURN((p - data) + 1);\n+          /* We want to use http_parser for tunneling connection\n+             transparently */\n+          /* Read body until EOF */\n+          UPDATE_STATE(s_body_identity_eof);\n+          break;\n+          /* UPDATE_STATE(NEW_MESSAGE()); */\n+          /* CALLBACK_NOTIFY(message_complete); */\n+          /* RETURN((p - data) + 1); */\n         }\n \n         if (parser->flags & F_SKIPBODY) {\n-- \n2.1.4\n\n"
  },
  {
    "path": "src/http-parser/AUTHORS",
    "content": "# Authors ordered by first contribution.\nRyan Dahl <ry@tinyclouds.org>\nJeremy Hinegardner <jeremy@hinegardner.org>\nSergey Shepelev <temotor@gmail.com>\nJoe Damato <ice799@gmail.com>\ntomika <tomika_nospam@freemail.hu>\nPhoenix Sol <phoenix@burninglabs.com>\nCliff Frey <cliff@meraki.com>\nEwen Cheslack-Postava <ewencp@cs.stanford.edu>\nSantiago Gala <sgala@apache.org>\nTim Becker <tim.becker@syngenio.de>\nJeff Terrace <jterrace@gmail.com>\nBen Noordhuis <info@bnoordhuis.nl>\nNathan Rajlich <nathan@tootallnate.net>\nMark Nottingham <mnot@mnot.net>\nAman Gupta <aman@tmm1.net>\nTim Becker <tim.becker@kuriositaet.de>\nSean Cunningham <sean.cunningham@mandiant.com>\nPeter Griess <pg@std.in>\nSalman Haq <salman.haq@asti-usa.com>\nCliff Frey <clifffrey@gmail.com>\nJon Kolb <jon@b0g.us>\nFouad Mardini <f.mardini@gmail.com>\nPaul Querna <pquerna@apache.org>\nFelix Geisendörfer <felix@debuggable.com>\nkoichik <koichik@improvement.jp>\nAndre Caron <andre.l.caron@gmail.com>\nIvo Raisr <ivosh@ivosh.net>\nJames McLaughlin <jamie@lacewing-project.org>\nDavid Gwynne <loki@animata.net>\nThomas LE ROUX <thomas@november-eleven.fr>\nRandy Rizun <rrizun@ortivawireless.com>\nAndre Louis Caron <andre.louis.caron@usherbrooke.ca>\nSimon Zimmermann <simonz05@gmail.com>\nErik Dubbelboer <erik@dubbelboer.com>\nMartell Malone <martellmalone@gmail.com>\nBertrand Paquet <bpaquet@octo.com>\nBogDan Vatra <bogdan@kde.org>\nPeter Faiman <peter@thepicard.org>\nCorey Richardson <corey@octayn.net>\nTóth Tamás <tomika_nospam@freemail.hu>\nCam Swords <cam.swords@gmail.com>\nChris Dickinson <christopher.s.dickinson@gmail.com>\nUli Köhler <ukoehler@btronik.de>\nCharlie Somerville <charlie@charliesomerville.com>\nPatrik Stutz <patrik.stutz@gmail.com>\nFedor Indutny <fedor.indutny@gmail.com>\nrunner <runner.mei@gmail.com>\nAlexis Campailla <alexis@janeasystems.com>\nDavid Wragg <david@wragg.org>\nVinnie Falco <vinnie.falco@gmail.com>\nAlex Butum <alexbutum@linux.com>\nRex Feng <rexfeng@gmail.com>\nAlex Kocharin <alex@kocharin.ru>\nMark Koopman <markmontymark@yahoo.com>\nHelge Heß <me@helgehess.eu>\nAlexis La Goutte <alexis.lagoutte@gmail.com>\nGeorge Miroshnykov <george.miroshnykov@gmail.com>\nMaciej Małecki <me@mmalecki.com>\nMarc O'Morain <github.com@marcomorain.com>\nJeff Pinner <jpinner@twitter.com>\nTimothy J Fontaine <tjfontaine@gmail.com>\nAkagi201 <akagi201@gmail.com>\nRomain Giraud <giraud.romain@gmail.com>\nJay Satiro <raysatiro@yahoo.com>\nArne Steen <Arne.Steen@gmx.de>\nKjell Schubert <kjell.schubert@gmail.com>\n"
  },
  {
    "path": "src/http-parser/CONTRIBUTIONS",
    "content": "Contributors must agree to the Contributor License Agreement before patches\ncan be accepted.\n\nhttp://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ\n"
  },
  {
    "path": "src/http-parser/LICENSE-MIT",
    "content": "http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright\nIgor Sysoev.\n\nAdditional changes are licensed under the same terms as NGINX and\ncopyright Joyent, Inc. and other Node contributors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to\ndeal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\nIN THE SOFTWARE. \n"
  },
  {
    "path": "src/http-parser/README.md",
    "content": "HTTP Parser\n===========\n\n[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)\n\nThis is a parser for HTTP messages written in C. It parses both requests and\nresponses. The parser is designed to be used in performance HTTP\napplications. It does not make any syscalls nor allocations, it does not\nbuffer data, it can be interrupted at anytime. Depending on your\narchitecture, it only requires about 40 bytes of data per message\nstream (in a web server that is per connection).\n\nFeatures:\n\n  * No dependencies\n  * Handles persistent streams (keep-alive).\n  * Decodes chunked encoding.\n  * Upgrade support\n  * Defends against buffer overflow attacks.\n\nThe parser extracts the following information from HTTP messages:\n\n  * Header fields and values\n  * Content-Length\n  * Request method\n  * Response status code\n  * Transfer-Encoding\n  * HTTP version\n  * Request URL\n  * Message body\n\n\nUsage\n-----\n\nOne `http_parser` object is used per TCP connection. Initialize the struct\nusing `http_parser_init()` and set the callbacks. That might look something\nlike this for a request parser:\n```c\nhttp_parser_settings settings;\nsettings.on_url = my_url_callback;\nsettings.on_header_field = my_header_field_callback;\n/* ... */\n\nhttp_parser *parser = malloc(sizeof(http_parser));\nhttp_parser_init(parser, HTTP_REQUEST);\nparser->data = my_socket;\n```\n\nWhen data is received on the socket execute the parser and check for errors.\n\n```c\nsize_t len = 80*1024, nparsed;\nchar buf[len];\nssize_t recved;\n\nrecved = recv(fd, buf, len, 0);\n\nif (recved < 0) {\n  /* Handle error. */\n}\n\n/* Start up / continue the parser.\n * Note we pass recved==0 to signal that EOF has been received.\n */\nnparsed = http_parser_execute(parser, &settings, buf, recved);\n\nif (parser->upgrade) {\n  /* handle new protocol */\n} else if (nparsed != recved) {\n  /* Handle error. Usually just close the connection. */\n}\n```\n\nHTTP needs to know where the end of the stream is. For example, sometimes\nservers send responses without Content-Length and expect the client to\nconsume input (for the body) until EOF. To tell http_parser about EOF, give\n`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors\ncan still be encountered during an EOF, so one must still be prepared\nto receive them.\n\nScalar valued message information such as `status_code`, `method`, and the\nHTTP version are stored in the parser structure. This data is only\ntemporally stored in `http_parser` and gets reset on each new message. If\nthis information is needed later, copy it out of the structure during the\n`headers_complete` callback.\n\nThe parser decodes the transfer-encoding for both requests and responses\ntransparently. That is, a chunked encoding is decoded before being sent to\nthe on_body callback.\n\n\nThe Special Problem of Upgrade\n------------------------------\n\nHTTP supports upgrading the connection to a different protocol. An\nincreasingly common example of this is the Web Socket protocol which sends\na request like\n\n        GET /demo HTTP/1.1\n        Upgrade: WebSocket\n        Connection: Upgrade\n        Host: example.com\n        Origin: http://example.com\n        WebSocket-Protocol: sample\n\nfollowed by non-HTTP data.\n\n(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more\ninformation the Web Socket protocol.)\n\nTo support this, the parser will treat this as a normal HTTP message without a\nbody, issuing both on_headers_complete and on_message_complete callbacks. However\nhttp_parser_execute() will stop parsing at the end of the headers and return.\n\nThe user is expected to check if `parser->upgrade` has been set to 1 after\n`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied\noffset by the return value of `http_parser_execute()`.\n\n\nCallbacks\n---------\n\nDuring the `http_parser_execute()` call, the callbacks set in\n`http_parser_settings` will be executed. The parser maintains state and\nnever looks behind, so buffering the data is not necessary. If you need to\nsave certain data for later usage, you can do that from the callbacks.\n\nThere are two types of callbacks:\n\n* notification `typedef int (*http_cb) (http_parser*);`\n    Callbacks: on_message_begin, on_headers_complete, on_message_complete.\n* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`\n    Callbacks: (requests only) on_url,\n               (common) on_header_field, on_header_value, on_body;\n\nCallbacks must return 0 on success. Returning a non-zero value indicates\nerror to the parser, making it exit immediately.\n\nIn case you parse HTTP message in chunks (i.e. `read()` request line\nfrom socket, parse, read half headers, parse, etc) your data callbacks\nmay be called more than once. Http-parser guarantees that data pointer is only\nvalid for the lifetime of callback. You can also `read()` into a heap allocated\nbuffer to avoid copying memory around if this fits your application.\n\nReading headers may be a tricky task if you read/parse headers partially.\nBasically, you need to remember whether last header callback was field or value\nand apply the following logic:\n\n    (on_header_field and on_header_value shortened to on_h_*)\n     ------------------------ ------------ --------------------------------------------\n    | State (prev. callback) | Callback   | Description/action                         |\n     ------------------------ ------------ --------------------------------------------\n    | nothing (first call)   | on_h_field | Allocate new buffer and copy callback data |\n    |                        |            | into it                                    |\n     ------------------------ ------------ --------------------------------------------\n    | value                  | on_h_field | New header started.                        |\n    |                        |            | Copy current name,value buffers to headers |\n    |                        |            | list and allocate new buffer for new name  |\n     ------------------------ ------------ --------------------------------------------\n    | field                  | on_h_field | Previous name continues. Reallocate name   |\n    |                        |            | buffer and append callback data to it      |\n     ------------------------ ------------ --------------------------------------------\n    | field                  | on_h_value | Value for current header started. Allocate |\n    |                        |            | new buffer and copy callback data to it    |\n     ------------------------ ------------ --------------------------------------------\n    | value                  | on_h_value | Value continues. Reallocate value buffer   |\n    |                        |            | and append callback data to it             |\n     ------------------------ ------------ --------------------------------------------\n\n\nParsing URLs\n------------\n\nA simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.\nUsers of this library may wish to use it to parse URLs constructed from\nconsecutive `on_url` callbacks.\n\nSee examples of reading in headers:\n\n* [partial example](http://gist.github.com/155877) in C\n* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C\n* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript\n"
  },
  {
    "path": "src/http-parser/bench.c",
    "content": "/* Copyright Fedor Indutny. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include \"http_parser.h\"\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/time.h>\n\nstatic const char data[] =\n    \"POST /joyent/http-parser HTTP/1.1\\r\\n\"\n    \"Host: github.com\\r\\n\"\n    \"DNT: 1\\r\\n\"\n    \"Accept-Encoding: gzip, deflate, sdch\\r\\n\"\n    \"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\\r\\n\"\n    \"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) \"\n        \"AppleWebKit/537.36 (KHTML, like Gecko) \"\n        \"Chrome/39.0.2171.65 Safari/537.36\\r\\n\"\n    \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\"\n        \"image/webp,*/*;q=0.8\\r\\n\"\n    \"Referer: https://github.com/joyent/http-parser\\r\\n\"\n    \"Connection: keep-alive\\r\\n\"\n    \"Transfer-Encoding: chunked\\r\\n\"\n    \"Cache-Control: max-age=0\\r\\n\\r\\nb\\r\\nhello world\\r\\n0\\r\\n\\r\\n\";\nstatic const size_t data_len = sizeof(data) - 1;\n\nstatic int on_info(http_parser* p) {\n  return 0;\n}\n\n\nstatic int on_data(http_parser* p, const char *at, size_t length) {\n  return 0;\n}\n\nstatic http_parser_settings settings = {\n  .on_message_begin = on_info,\n  .on_headers_complete = on_info,\n  .on_message_complete = on_info,\n  .on_header_field = on_data,\n  .on_header_value = on_data,\n  .on_url = on_data,\n  .on_status = on_data,\n  .on_body = on_data\n};\n\nint bench(int iter_count, int silent) {\n  struct http_parser parser;\n  int i;\n  int err;\n  struct timeval start;\n  struct timeval end;\n  float rps;\n\n  if (!silent) {\n    err = gettimeofday(&start, NULL);\n    assert(err == 0);\n  }\n\n  for (i = 0; i < iter_count; i++) {\n    size_t parsed;\n    http_parser_init(&parser, HTTP_REQUEST);\n\n    parsed = http_parser_execute(&parser, &settings, data, data_len);\n    assert(parsed == data_len);\n  }\n\n  if (!silent) {\n    err = gettimeofday(&end, NULL);\n    assert(err == 0);\n\n    fprintf(stdout, \"Benchmark result:\\n\");\n\n    rps = (float) (end.tv_sec - start.tv_sec) +\n          (end.tv_usec - start.tv_usec) * 1e-6f;\n    fprintf(stdout, \"Took %f seconds to run\\n\", rps);\n\n    rps = (float) iter_count / rps;\n    fprintf(stdout, \"%f req/sec\\n\", rps);\n    fflush(stdout);\n  }\n\n  return 0;\n}\n\nint main(int argc, char** argv) {\n  if (argc == 2 && strcmp(argv[1], \"infinite\") == 0) {\n    for (;;)\n      bench(5000000, 1);\n    return 0;\n  } else {\n    return bench(5000000, 0);\n  }\n}\n"
  },
  {
    "path": "src/http-parser/contrib/parsertrace.c",
    "content": "/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev\n *\n * Additional changes are licensed under the same terms as NGINX and\n * copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* Dump what the parser finds to stdout as it happen */\n\n#include \"http_parser.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nint on_message_begin(http_parser* _) {\n  (void)_;\n  printf(\"\\n***MESSAGE BEGIN***\\n\\n\");\n  return 0;\n}\n\nint on_headers_complete(http_parser* _) {\n  (void)_;\n  printf(\"\\n***HEADERS COMPLETE***\\n\\n\");\n  return 0;\n}\n\nint on_message_complete(http_parser* _) {\n  (void)_;\n  printf(\"\\n***MESSAGE COMPLETE***\\n\\n\");\n  return 0;\n}\n\nint on_url(http_parser* _, const char* at, size_t length) {\n  (void)_;\n  printf(\"Url: %.*s\\n\", (int)length, at);\n  return 0;\n}\n\nint on_header_field(http_parser* _, const char* at, size_t length) {\n  (void)_;\n  printf(\"Header field: %.*s\\n\", (int)length, at);\n  return 0;\n}\n\nint on_header_value(http_parser* _, const char* at, size_t length) {\n  (void)_;\n  printf(\"Header value: %.*s\\n\", (int)length, at);\n  return 0;\n}\n\nint on_body(http_parser* _, const char* at, size_t length) {\n  (void)_;\n  printf(\"Body: %.*s\\n\", (int)length, at);\n  return 0;\n}\n\nvoid usage(const char* name) {\n  fprintf(stderr,\n          \"Usage: %s $type $filename\\n\"\n          \"  type: -x, where x is one of {r,b,q}\\n\"\n          \"  parses file as a Response, reQuest, or Both\\n\",\n          name);\n  exit(EXIT_FAILURE);\n}\n\nint main(int argc, char* argv[]) {\n  enum http_parser_type file_type;\n\n  if (argc != 3) {\n    usage(argv[0]);\n  }\n\n  char* type = argv[1];\n  if (type[0] != '-') {\n    usage(argv[0]);\n  }\n\n  switch (type[1]) {\n    /* in the case of \"-\", type[1] will be NUL */\n    case 'r':\n      file_type = HTTP_RESPONSE;\n      break;\n    case 'q':\n      file_type = HTTP_REQUEST;\n      break;\n    case 'b':\n      file_type = HTTP_BOTH;\n      break;\n    default:\n      usage(argv[0]);\n  }\n\n  char* filename = argv[2];\n  FILE* file = fopen(filename, \"r\");\n  if (file == NULL) {\n    perror(\"fopen\");\n    goto fail;\n  }\n\n  fseek(file, 0, SEEK_END);\n  long file_length = ftell(file);\n  if (file_length == -1) {\n    perror(\"ftell\");\n    goto fail;\n  }\n  fseek(file, 0, SEEK_SET);\n\n  char* data = malloc(file_length);\n  if (fread(data, 1, file_length, file) != (size_t)file_length) {\n    fprintf(stderr, \"couldn't read entire file\\n\");\n    free(data);\n    goto fail;\n  }\n\n  http_parser_settings settings;\n  memset(&settings, 0, sizeof(settings));\n  settings.on_message_begin = on_message_begin;\n  settings.on_url = on_url;\n  settings.on_header_field = on_header_field;\n  settings.on_header_value = on_header_value;\n  settings.on_headers_complete = on_headers_complete;\n  settings.on_body = on_body;\n  settings.on_message_complete = on_message_complete;\n\n  http_parser parser;\n  http_parser_init(&parser, file_type);\n  size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);\n  free(data);\n\n  if (nparsed != (size_t)file_length) {\n    fprintf(stderr,\n            \"Error: %s (%s)\\n\",\n            http_errno_description(HTTP_PARSER_ERRNO(&parser)),\n            http_errno_name(HTTP_PARSER_ERRNO(&parser)));\n    goto fail;\n  }\n\n  return EXIT_SUCCESS;\n\nfail:\n  fclose(file);\n  return EXIT_FAILURE;\n}\n"
  },
  {
    "path": "src/http-parser/contrib/url_parser.c",
    "content": "#include \"http_parser.h\"\n#include <stdio.h>\n#include <string.h>\n\nvoid\ndump_url (const char *url, const struct http_parser_url *u)\n{\n  unsigned int i;\n\n  printf(\"\\tfield_set: 0x%x, port: %u\\n\", u->field_set, u->port);\n  for (i = 0; i < UF_MAX; i++) {\n    if ((u->field_set & (1 << i)) == 0) {\n      printf(\"\\tfield_data[%u]: unset\\n\", i);\n      continue;\n    }\n\n    printf(\"\\tfield_data[%u]: off: %u, len: %u, part: %.*s\\n\",\n           i,\n           u->field_data[i].off,\n           u->field_data[i].len,\n           u->field_data[i].len,\n           url + u->field_data[i].off);\n  }\n}\n\nint main(int argc, char ** argv) {\n  struct http_parser_url u;\n  int len, connect, result;\n\n  if (argc != 3) {\n    printf(\"Syntax : %s connect|get url\\n\", argv[0]);\n    return 1;\n  }\n  len = strlen(argv[2]);\n  connect = strcmp(\"connect\", argv[1]) == 0 ? 1 : 0;\n  printf(\"Parsing %s, connect %d\\n\", argv[2], connect);\n\n  result = http_parser_parse_url(argv[2], len, connect, &u);\n  if (result != 0) {\n    printf(\"Parse error : %d\\n\", result);\n    return result;\n  }\n  printf(\"Parse ok, result : \\n\");\n  dump_url(argv[2], &u);\n  return 0;\n}"
  },
  {
    "path": "src/http-parser/http_parser.c",
    "content": "/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev\n *\n * Additional changes are licensed under the same terms as NGINX and\n * copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include \"http_parser.h\"\n#include <assert.h>\n#include <stddef.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n#ifndef ULLONG_MAX\n# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */\n#endif\n\n#ifndef MIN\n# define MIN(a,b) ((a) < (b) ? (a) : (b))\n#endif\n\n#ifndef ARRAY_SIZE\n# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))\n#endif\n\n#ifndef BIT_AT\n# define BIT_AT(a, i)                                                \\\n  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \\\n   (1 << ((unsigned int) (i) & 7))))\n#endif\n\n#ifndef ELEM_AT\n# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))\n#endif\n\n#define SET_ERRNO(e)                                                 \\\ndo {                                                                 \\\n  parser->http_errno = (e);                                          \\\n} while(0)\n\n#define CURRENT_STATE() p_state\n#define UPDATE_STATE(V) p_state = (enum state) (V);\n#define RETURN(V)                                                    \\\ndo {                                                                 \\\n  parser->state = CURRENT_STATE();                                   \\\n  return (V);                                                        \\\n} while (0);\n#define REEXECUTE()                                                  \\\n  goto reexecute;                                                    \\\n\n\n#ifdef __GNUC__\n# define LIKELY(X) __builtin_expect(!!(X), 1)\n# define UNLIKELY(X) __builtin_expect(!!(X), 0)\n#else\n# define LIKELY(X) (X)\n# define UNLIKELY(X) (X)\n#endif\n\n\n/* Run the notify callback FOR, returning ER if it fails */\n#define CALLBACK_NOTIFY_(FOR, ER)                                    \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (LIKELY(settings->on_##FOR)) {                                  \\\n    parser->state = CURRENT_STATE();                                 \\\n    if (UNLIKELY(0 != settings->on_##FOR(parser))) {                 \\\n      SET_ERRNO(HPE_CB_##FOR);                                       \\\n    }                                                                \\\n    UPDATE_STATE(parser->state);                                     \\\n                                                                     \\\n    /* We either errored above or got paused; get out */             \\\n    if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {             \\\n      return (ER);                                                   \\\n    }                                                                \\\n  }                                                                  \\\n} while (0)\n\n/* Run the notify callback FOR and consume the current byte */\n#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)\n\n/* Run the notify callback FOR and don't consume the current byte */\n#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)\n\n/* Run data callback FOR with LEN bytes, returning ER if it fails */\n#define CALLBACK_DATA_(FOR, LEN, ER)                                 \\\ndo {                                                                 \\\n  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \\\n                                                                     \\\n  if (FOR##_mark) {                                                  \\\n    if (LIKELY(settings->on_##FOR)) {                                \\\n      parser->state = CURRENT_STATE();                               \\\n      if (UNLIKELY(0 !=                                              \\\n                   settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \\\n        SET_ERRNO(HPE_CB_##FOR);                                     \\\n      }                                                              \\\n      UPDATE_STATE(parser->state);                                   \\\n                                                                     \\\n      /* We either errored above or got paused; get out */           \\\n      if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) {           \\\n        return (ER);                                                 \\\n      }                                                              \\\n    }                                                                \\\n    FOR##_mark = NULL;                                               \\\n  }                                                                  \\\n} while (0)\n  \n/* Run the data callback FOR and consume the current byte */\n#define CALLBACK_DATA(FOR)                                           \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)\n\n/* Run the data callback FOR and don't consume the current byte */\n#define CALLBACK_DATA_NOADVANCE(FOR)                                 \\\n    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)\n\n/* Set the mark FOR; non-destructive if mark is already set */\n#define MARK(FOR)                                                    \\\ndo {                                                                 \\\n  if (!FOR##_mark) {                                                 \\\n    FOR##_mark = p;                                                  \\\n  }                                                                  \\\n} while (0)\n\n/* Don't allow the total size of the HTTP headers (including the status\n * line) to exceed HTTP_MAX_HEADER_SIZE.  This check is here to protect\n * embedders against denial-of-service attacks where the attacker feeds\n * us a never-ending header that the embedder keeps buffering.\n *\n * This check is arguably the responsibility of embedders but we're doing\n * it on the embedder's behalf because most won't bother and this way we\n * make the web a little safer.  HTTP_MAX_HEADER_SIZE is still far bigger\n * than any reasonable request or response so this should never affect\n * day-to-day operation.\n */\n#define COUNT_HEADER_SIZE(V)                                         \\\ndo {                                                                 \\\n  parser->nread += (V);                                              \\\n  if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) {            \\\n    SET_ERRNO(HPE_HEADER_OVERFLOW);                                  \\\n    goto error;                                                      \\\n  }                                                                  \\\n} while (0)\n\n\n#define PROXY_CONNECTION \"proxy-connection\"\n#define CONNECTION \"connection\"\n#define CONTENT_LENGTH \"content-length\"\n#define TRANSFER_ENCODING \"transfer-encoding\"\n#define UPGRADE \"upgrade\"\n#define CHUNKED \"chunked\"\n#define KEEP_ALIVE \"keep-alive\"\n#define CLOSE \"close\"\n\n\nstatic const char *method_strings[] =\n  {\n#define XX(num, name, string) #string,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\n/* Tokens as defined by rfc 2616. Also lowercases them.\n *        token       = 1*<any CHAR except CTLs or separators>\n *     separators     = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *                    | \",\" | \";\" | \":\" | \"\\\" | <\">\n *                    | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *                    | \"{\" | \"}\" | SP | HT\n */\nstatic const char tokens[256] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0,       0,       0,       0,       0,       0,       0,       0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n       ' ',     '!',     '\"',     '#',     '$',     '%',     '&',    '\\'',\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n       '(',     ')',     '*',     '+',     ',',     '-',     '.',     '/',\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n       '8',     '9',      0,      ';',     '<',     '=',     '>',     '?',\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n       '@',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n       'x',     'y',     'z',     '[',    '\\\\',     ']',     '^',     '_',\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n       'x',     'y',     'z',     '{',     '|',     '}',     '~',       0 };\n\n\nstatic const int8_t unhex[256] =\n  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1\n  };\n\n\n#if HTTP_PARSER_STRICT\n# define T(v) 0\n#else\n# define T(v) v\n#endif\n\n\nstatic const uint8_t normal_url_char[32] = {\n/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */\n        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,\n/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */\n        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,\n/*  32 sp    33  !    34  \"    35  #    36  $    37  %    38  &    39  '  */\n        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,\n/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,\n/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  88  X    89  Y    90  Z    91  [    92  \\    93  ]    94  ^    95  _  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,\n/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */\n        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };\n\n#undef T\n\nenum state\n  { s_dead = 1 /* important that this is > 0 */\n\n  , s_start_req_or_res\n  , s_res_or_resp_H\n  , s_start_res\n  , s_res_proto_custom\n  , s_res_H\n  , s_res_HT\n  , s_res_HTT\n  , s_res_HTTP\n  , s_res_first_http_major\n  , s_res_http_major\n  , s_res_first_http_minor\n  , s_res_http_minor\n  , s_res_first_status_code\n  , s_res_status_code\n  , s_res_status_start\n  , s_res_status\n  , s_res_line_almost_done\n\n  , s_start_req\n\n  , s_req_method\n  , s_req_spaces_before_url\n  , s_req_schema\n  , s_req_schema_slash\n  , s_req_schema_slash_slash\n  , s_req_server_start\n  , s_req_server\n  , s_req_server_with_at\n  , s_req_path\n  , s_req_query_string_start\n  , s_req_query_string\n  , s_req_fragment_start\n  , s_req_fragment\n  , s_req_http_start\n  , s_req_http_H\n  , s_req_http_HT\n  , s_req_http_HTT\n  , s_req_http_HTTP\n  , s_req_first_http_major\n  , s_req_http_major\n  , s_req_first_http_minor\n  , s_req_http_minor\n  , s_req_line_almost_done\n\n  , s_header_field_start\n  , s_header_field\n  , s_header_value_discard_ws\n  , s_header_value_discard_ws_almost_done\n  , s_header_value_discard_lws\n  , s_header_value_start\n  , s_header_value\n  , s_header_value_lws\n\n  , s_header_almost_done\n\n  , s_chunk_size_start\n  , s_chunk_size\n  , s_chunk_parameters\n  , s_chunk_size_almost_done\n\n  , s_headers_almost_done\n  , s_headers_done\n\n  /* Important: 's_headers_done' must be the last 'header' state. All\n   * states beyond this must be 'body' states. It is used for overflow\n   * checking. See the PARSING_HEADER() macro.\n   */\n\n  , s_chunk_data\n  , s_chunk_data_almost_done\n  , s_chunk_data_done\n\n  , s_body_identity\n  , s_body_identity_eof\n\n  , s_message_done\n  };\n\n\n#define PARSING_HEADER(state) (state <= s_headers_done)\n\n\nenum header_states\n  { h_general = 0\n  , h_C\n  , h_CO\n  , h_CON\n\n  , h_matching_connection\n  , h_matching_proxy_connection\n  , h_matching_content_length\n  , h_matching_transfer_encoding\n  , h_matching_upgrade\n\n  , h_connection\n  , h_content_length\n  , h_transfer_encoding\n  , h_upgrade\n\n  , h_matching_transfer_encoding_chunked\n  , h_matching_connection_token_start\n  , h_matching_connection_keep_alive\n  , h_matching_connection_close\n  , h_matching_connection_upgrade\n  , h_matching_connection_token\n\n  , h_transfer_encoding_chunked\n  , h_connection_keep_alive\n  , h_connection_close\n  , h_connection_upgrade\n  };\n\nenum http_host_state\n  {\n    s_http_host_dead = 1\n  , s_http_userinfo_start\n  , s_http_userinfo\n  , s_http_host_start\n  , s_http_host_v6_start\n  , s_http_host\n  , s_http_host_v6\n  , s_http_host_v6_end\n  , s_http_host_port_start\n  , s_http_host_port\n};\n\n/* Macros for character classes; depends on strict-mode  */\n#define CR                  '\\r'\n#define LF                  '\\n'\n#define LOWER(c)            (unsigned char)(c | 0x20)\n#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')\n#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')\n#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))\n#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))\n#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \\\n  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\\'' || (c) == '(' || \\\n  (c) == ')')\n#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \\\n  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \\\n  (c) == '$' || (c) == ',')\n\n#define STRICT_TOKEN(c)     (tokens[(unsigned char)c])\n\n#if HTTP_PARSER_STRICT\n#define TOKEN(c)            (tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))\n#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')\n#else\n#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])\n#define IS_URL_CHAR(c)                                                         \\\n  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))\n#define IS_HOST_CHAR(c)                                                        \\\n  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')\n#endif\n\n\n#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)\n\n\n#if HTTP_PARSER_STRICT\n# define STRICT_CHECK(cond)                                          \\\ndo {                                                                 \\\n  if (cond) {                                                        \\\n    SET_ERRNO(HPE_STRICT);                                           \\\n    goto error;                                                      \\\n  }                                                                  \\\n} while (0)\n# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)\n#else\n# define STRICT_CHECK(cond)\n# define NEW_MESSAGE() start_state\n#endif\n\n\n/* Map errno values to strings for human-readable output */\n#define HTTP_STRERROR_GEN(n, s) { \"HPE_\" #n, s },\nstatic struct {\n  const char *name;\n  const char *description;\n} http_strerror_tab[] = {\n  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)\n};\n#undef HTTP_STRERROR_GEN\n\nint http_message_needs_eof(const http_parser *parser);\n\n/* Our URL parser.\n *\n * This is designed to be shared by http_parser_execute() for URL validation,\n * hence it has a state transition + byte-for-byte interface. In addition, it\n * is meant to be embedded in http_parser_parse_url(), which does the dirty\n * work of turning state transitions URL components for its API.\n *\n * This function should only be invoked with non-space characters. It is\n * assumed that the caller cares about (and can detect) the transition between\n * URL and non-URL states by looking for these.\n */\nstatic enum state\nparse_url_char(enum state s, const char ch)\n{\n  if (ch == ' ' || ch == '\\r' || ch == '\\n') {\n    return s_dead;\n  }\n\n#if HTTP_PARSER_STRICT\n  if (ch == '\\t' || ch == '\\f') {\n    return s_dead;\n  }\n#endif\n\n  switch (s) {\n    case s_req_spaces_before_url:\n      /* Proxied requests are followed by scheme of an absolute URI (alpha).\n       * All methods except CONNECT are followed by '/' or '*'.\n       */\n\n      if (ch == '/' || ch == '*') {\n        return s_req_path;\n      }\n\n      if (IS_ALPHA(ch)) {\n        return s_req_schema;\n      }\n\n      break;\n\n    case s_req_schema:\n      if (IS_ALPHA(ch)) {\n        return s;\n      }\n\n      if (ch == ':') {\n        return s_req_schema_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash:\n      if (ch == '/') {\n        return s_req_schema_slash_slash;\n      }\n\n      break;\n\n    case s_req_schema_slash_slash:\n      if (ch == '/') {\n        return s_req_server_start;\n      }\n\n      break;\n\n    case s_req_server_with_at:\n      if (ch == '@') {\n        return s_dead;\n      }\n\n    /* FALLTHROUGH */\n    case s_req_server_start:\n    case s_req_server:\n      if (ch == '/') {\n        return s_req_path;\n      }\n\n      if (ch == '?') {\n        return s_req_query_string_start;\n      }\n\n      if (ch == '@') {\n        return s_req_server_with_at;\n      }\n\n      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {\n        return s_req_server;\n      }\n\n      break;\n\n    case s_req_path:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_query_string_start;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_query_string_start:\n    case s_req_query_string:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_query_string;\n      }\n\n      switch (ch) {\n        case '?':\n          /* allow extra '?' in query string */\n          return s_req_query_string;\n\n        case '#':\n          return s_req_fragment_start;\n      }\n\n      break;\n\n    case s_req_fragment_start:\n      if (IS_URL_CHAR(ch)) {\n        return s_req_fragment;\n      }\n\n      switch (ch) {\n        case '?':\n          return s_req_fragment;\n\n        case '#':\n          return s;\n      }\n\n      break;\n\n    case s_req_fragment:\n      if (IS_URL_CHAR(ch)) {\n        return s;\n      }\n\n      switch (ch) {\n        case '?':\n        case '#':\n          return s;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n\n  /* We should never fall out of the switch above unless there's an error */\n  return s_dead;\n}\n\nsize_t http_parser_execute (http_parser *parser,\n                            const http_parser_settings *settings,\n                            const char *data,\n                            size_t len)\n{\n  char c, ch;\n  int8_t unhex_val;\n  const char *p = data;\n  const char *header_field_mark = 0;\n  const char *header_value_mark = 0;\n  const char *url_mark = 0;\n  const char *body_mark = 0;\n  const char *status_mark = 0;\n  enum state p_state = (enum state) parser->state;\n\n  /* We're in an error state. Don't bother doing anything. */\n  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n    return 0;\n  }\n\n  if (len == 0) {\n    switch (CURRENT_STATE()) {\n      case s_body_identity_eof:\n        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if\n         * we got paused.\n         */\n        CALLBACK_NOTIFY_NOADVANCE(message_complete);\n        return 0;\n\n      case s_dead:\n      case s_start_req_or_res:\n      case s_start_res:\n      case s_start_req:\n        return 0;\n\n      default:\n        SET_ERRNO(HPE_INVALID_EOF_STATE);\n        return 1;\n    }\n  }\n\n\n  if (CURRENT_STATE() == s_header_field)\n    header_field_mark = data;\n  if (CURRENT_STATE() == s_header_value)\n    header_value_mark = data;\n  switch (CURRENT_STATE()) {\n  case s_req_path:\n  case s_req_schema:\n  case s_req_schema_slash:\n  case s_req_schema_slash_slash:\n  case s_req_server_start:\n  case s_req_server:\n  case s_req_server_with_at:\n  case s_req_query_string_start:\n  case s_req_query_string:\n  case s_req_fragment_start:\n  case s_req_fragment:\n    url_mark = data;\n    break;\n  case s_res_status:\n    status_mark = data;\n    break;\n  default:\n    break;\n  }\n\n  for (p=data; p != data + len; p++) {\n    ch = *p;\n\n    if (PARSING_HEADER(CURRENT_STATE()))\n      COUNT_HEADER_SIZE(1);\n\nreexecute:\n    switch (CURRENT_STATE()) {\n\n      case s_dead:\n        /* this state is used after a 'Connection: close' message\n         * the parser will error out if it reads another message\n         */\n        if (LIKELY(ch == CR || ch == LF))\n          break;\n\n        SET_ERRNO(HPE_CLOSED_CONNECTION);\n        goto error;\n\n      case s_start_req_or_res:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (ch == 'H') {\n          UPDATE_STATE(s_res_or_resp_H);\n\n          CALLBACK_NOTIFY(message_begin);\n        } else {\n          parser->type = HTTP_REQUEST;\n          UPDATE_STATE(s_start_req);\n          REEXECUTE();\n        }\n\n        break;\n      }\n\n      case s_res_or_resp_H:\n        if (ch == 'T') {\n          parser->type = HTTP_RESPONSE;\n          UPDATE_STATE(s_res_HT);\n        } else {\n          if (UNLIKELY(ch != 'E')) {\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n          }\n\n          parser->type = HTTP_REQUEST;\n          parser->method = HTTP_HEAD;\n          parser->index = 2;\n          UPDATE_STATE(s_req_method);\n        }\n        break;\n\n      case s_start_res:\n      {\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        switch (ch) {\n          case 'H':\n            UPDATE_STATE(s_res_H);\n            break;\n\n          case CR:\n          case LF:\n            break;\n\n          default:\n            if(TOKEN(ch)) {\n              parser->state = s_res_proto_custom;\n              break;\n            }\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n\n        CALLBACK_NOTIFY(message_begin);\n        break;\n      }\n\n      /* Custom HTTP-version string (e.g., ICY) */\n      case s_res_proto_custom:\n        if (ch == ' ') {\n          /* We assume this is HTTP/1.0 */\n          parser->http_major = 1;\n          parser->http_minor = 0;\n          parser->state = s_res_first_status_code;\n          break;\n        }\n        if (!TOKEN(ch)) {\n          SET_ERRNO(HPE_INVALID_CONSTANT);\n          goto error;\n        }\n        break;\n\n      case s_res_H:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_res_HT);\n        break;\n\n      case s_res_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_res_HTT);\n        break;\n\n      case s_res_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_res_HTTP);\n        break;\n\n      case s_res_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_res_first_http_major);\n        break;\n\n      case s_res_first_http_major:\n        if (UNLIKELY(ch < '0' || ch > '9')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_res_http_major);\n        break;\n\n      /* major HTTP version or dot */\n      case s_res_http_major:\n      {\n        if (ch == '.') {\n          UPDATE_STATE(s_res_first_http_minor);\n          break;\n        }\n\n        if (!IS_NUM(ch)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (UNLIKELY(parser->http_major > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_res_first_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_res_http_minor);\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_res_http_minor:\n      {\n        if (ch == ' ') {\n          UPDATE_STATE(s_res_first_status_code);\n          break;\n        }\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (UNLIKELY(parser->http_minor > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_first_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          if (ch == ' ') {\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n        parser->status_code = ch - '0';\n        UPDATE_STATE(s_res_status_code);\n        break;\n      }\n\n      case s_res_status_code:\n      {\n        if (!IS_NUM(ch)) {\n          switch (ch) {\n            case ' ':\n              UPDATE_STATE(s_res_status_start);\n              break;\n            case CR:\n              UPDATE_STATE(s_res_line_almost_done);\n              break;\n            case LF:\n              UPDATE_STATE(s_header_field_start);\n              break;\n            default:\n              SET_ERRNO(HPE_INVALID_STATUS);\n              goto error;\n          }\n          break;\n        }\n\n        parser->status_code *= 10;\n        parser->status_code += ch - '0';\n\n        if (UNLIKELY(parser->status_code > 999)) {\n          SET_ERRNO(HPE_INVALID_STATUS);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_res_status_start:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_res_line_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          break;\n        }\n\n        MARK(status);\n        UPDATE_STATE(s_res_status);\n        parser->index = 0;\n        break;\n      }\n\n      case s_res_status:\n        if (ch == CR) {\n          UPDATE_STATE(s_res_line_almost_done);\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          CALLBACK_DATA(status);\n          break;\n        }\n\n        break;\n\n      case s_res_line_almost_done:\n        STRICT_CHECK(ch != LF);\n        UPDATE_STATE(s_header_field_start);\n        break;\n\n      case s_start_req:\n      {\n        if (ch == CR || ch == LF)\n          break;\n        parser->flags = 0;\n        parser->content_length = ULLONG_MAX;\n\n        if (UNLIKELY(!IS_ALPHA(ch))) {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        parser->method = (enum http_method) 0;\n        parser->index = 1;\n        switch (ch) {\n          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;\n          case 'D': parser->method = HTTP_DELETE; break;\n          case 'G': parser->method = HTTP_GET; break;\n          case 'H': parser->method = HTTP_HEAD; break;\n          case 'L': parser->method = HTTP_LOCK; break;\n          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;\n          case 'N': parser->method = HTTP_NOTIFY; break;\n          case 'O': parser->method = HTTP_OPTIONS; break;\n          case 'P': parser->method = HTTP_POST;\n            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */\n            break;\n          case 'R': parser->method = HTTP_REPORT; break;\n          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;\n          case 'T': parser->method = HTTP_TRACE; break;\n          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;\n          default:\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n        }\n        UPDATE_STATE(s_req_method);\n\n        CALLBACK_NOTIFY(message_begin);\n\n        break;\n      }\n\n      case s_req_method:\n      {\n        const char *matcher;\n        if (UNLIKELY(ch == '\\0')) {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        matcher = method_strings[parser->method];\n        if (ch == ' ' && matcher[parser->index] == '\\0') {\n          UPDATE_STATE(s_req_spaces_before_url);\n        } else if (ch == matcher[parser->index]) {\n          ; /* nada */\n        } else if (parser->method == HTTP_CONNECT) {\n          if (parser->index == 1 && ch == 'H') {\n            parser->method = HTTP_CHECKOUT;\n          } else if (parser->index == 2  && ch == 'P') {\n            parser->method = HTTP_COPY;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_MKCOL) {\n          if (parser->index == 1 && ch == 'O') {\n            parser->method = HTTP_MOVE;\n          } else if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_MERGE;\n          } else if (parser->index == 1 && ch == '-') {\n            parser->method = HTTP_MSEARCH;\n          } else if (parser->index == 2 && ch == 'A') {\n            parser->method = HTTP_MKACTIVITY;\n          } else if (parser->index == 3 && ch == 'A') {\n            parser->method = HTTP_MKCALENDAR;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->method == HTTP_SUBSCRIBE) {\n          if (parser->index == 1 && ch == 'E') {\n            parser->method = HTTP_SEARCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 1 && parser->method == HTTP_POST) {\n          if (ch == 'R') {\n            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */\n          } else if (ch == 'U') {\n            parser->method = HTTP_PUT; /* or HTTP_PURGE */\n          } else if (ch == 'A') {\n            parser->method = HTTP_PATCH;\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 2) {\n          if (parser->method == HTTP_PUT) {\n            if (ch == 'R') {\n              parser->method = HTTP_PURGE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else if (parser->method == HTTP_UNLOCK) {\n            if (ch == 'S') {\n              parser->method = HTTP_UNSUBSCRIBE;\n            } else {\n              SET_ERRNO(HPE_INVALID_METHOD);\n              goto error;\n            }\n          } else {\n            SET_ERRNO(HPE_INVALID_METHOD);\n            goto error;\n          }\n        } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {\n          parser->method = HTTP_PROPPATCH;\n        } else {\n          SET_ERRNO(HPE_INVALID_METHOD);\n          goto error;\n        }\n\n        ++parser->index;\n        break;\n      }\n\n      case s_req_spaces_before_url:\n      {\n        if (ch == ' ') break;\n\n        MARK(url);\n        if (parser->method == HTTP_CONNECT) {\n          UPDATE_STATE(s_req_server_start);\n        }\n\n        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n        if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n          SET_ERRNO(HPE_INVALID_URL);\n          goto error;\n        }\n\n        break;\n      }\n\n      case s_req_schema:\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      {\n        switch (ch) {\n          /* No whitespace allowed here */\n          case ' ':\n          case CR:\n          case LF:\n            SET_ERRNO(HPE_INVALID_URL);\n            goto error;\n          default:\n            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n\n        break;\n      }\n\n      case s_req_server:\n      case s_req_server_with_at:\n      case s_req_path:\n      case s_req_query_string_start:\n      case s_req_query_string:\n      case s_req_fragment_start:\n      case s_req_fragment:\n      {\n        switch (ch) {\n          case ' ':\n            UPDATE_STATE(s_req_http_start);\n            CALLBACK_DATA(url);\n            break;\n          case CR:\n          case LF:\n            parser->http_major = 0;\n            parser->http_minor = 9;\n            UPDATE_STATE((ch == CR) ?\n              s_req_line_almost_done :\n              s_header_field_start);\n            CALLBACK_DATA(url);\n            break;\n          default:\n            UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));\n            if (UNLIKELY(CURRENT_STATE() == s_dead)) {\n              SET_ERRNO(HPE_INVALID_URL);\n              goto error;\n            }\n        }\n        break;\n      }\n\n      case s_req_http_start:\n        switch (ch) {\n          case 'H':\n            UPDATE_STATE(s_req_http_H);\n            break;\n          case ' ':\n            break;\n          default:\n            SET_ERRNO(HPE_INVALID_CONSTANT);\n            goto error;\n        }\n        break;\n\n      case s_req_http_H:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_req_http_HT);\n        break;\n\n      case s_req_http_HT:\n        STRICT_CHECK(ch != 'T');\n        UPDATE_STATE(s_req_http_HTT);\n        break;\n\n      case s_req_http_HTT:\n        STRICT_CHECK(ch != 'P');\n        UPDATE_STATE(s_req_http_HTTP);\n        break;\n\n      case s_req_http_HTTP:\n        STRICT_CHECK(ch != '/');\n        UPDATE_STATE(s_req_first_http_major);\n        break;\n\n      /* first digit of major HTTP version */\n      case s_req_first_http_major:\n        if (UNLIKELY(ch < '1' || ch > '9')) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major = ch - '0';\n        UPDATE_STATE(s_req_http_major);\n        break;\n\n      /* major HTTP version or dot */\n      case s_req_http_major:\n      {\n        if (ch == '.') {\n          UPDATE_STATE(s_req_first_http_minor);\n          break;\n        }\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_major *= 10;\n        parser->http_major += ch - '0';\n\n        if (UNLIKELY(parser->http_major > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* first digit of minor HTTP version */\n      case s_req_first_http_minor:\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor = ch - '0';\n        UPDATE_STATE(s_req_http_minor);\n        break;\n\n      /* minor HTTP version or end of request line */\n      case s_req_http_minor:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_req_line_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_field_start);\n          break;\n        }\n\n        /* XXX allow spaces after digit? */\n\n        if (UNLIKELY(!IS_NUM(ch))) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        parser->http_minor *= 10;\n        parser->http_minor += ch - '0';\n\n        if (UNLIKELY(parser->http_minor > 999)) {\n          SET_ERRNO(HPE_INVALID_VERSION);\n          goto error;\n        }\n\n        break;\n      }\n\n      /* end of request line */\n      case s_req_line_almost_done:\n      {\n        if (UNLIKELY(ch != LF)) {\n          SET_ERRNO(HPE_LF_EXPECTED);\n          goto error;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        break;\n      }\n\n      case s_header_field_start:\n      {\n        if (ch == CR) {\n          UPDATE_STATE(s_headers_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          /* they might be just sending \\n instead of \\r\\n so this would be\n           * the second \\n to denote the end of headers*/\n          UPDATE_STATE(s_headers_almost_done);\n          REEXECUTE();\n        }\n\n        c = TOKEN(ch);\n\n        if (UNLIKELY(!c)) {\n          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n          goto error;\n        }\n\n        MARK(header_field);\n\n        parser->index = 0;\n        UPDATE_STATE(s_header_field);\n\n        switch (c) {\n          case 'c':\n            parser->header_state = h_C;\n            break;\n\n          case 'p':\n            parser->header_state = h_matching_proxy_connection;\n            break;\n\n          case 't':\n            parser->header_state = h_matching_transfer_encoding;\n            break;\n\n          case 'u':\n            parser->header_state = h_matching_upgrade;\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_field:\n      {\n        const char* start = p;\n        for (; p != data + len; p++) {\n          ch = *p;\n          c = TOKEN(ch);\n\n          if (!c)\n            break;\n\n          switch (parser->header_state) {\n            case h_general:\n              break;\n\n            case h_C:\n              parser->index++;\n              parser->header_state = (c == 'o' ? h_CO : h_general);\n              break;\n\n            case h_CO:\n              parser->index++;\n              parser->header_state = (c == 'n' ? h_CON : h_general);\n              break;\n\n            case h_CON:\n              parser->index++;\n              switch (c) {\n                case 'n':\n                  parser->header_state = h_matching_connection;\n                  break;\n                case 't':\n                  parser->header_state = h_matching_content_length;\n                  break;\n                default:\n                  parser->header_state = h_general;\n                  break;\n              }\n              break;\n\n            /* connection */\n\n            case h_matching_connection:\n              parser->index++;\n              if (parser->index > sizeof(CONNECTION)-1\n                  || c != CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* proxy-connection */\n\n            case h_matching_proxy_connection:\n              parser->index++;\n              if (parser->index > sizeof(PROXY_CONNECTION)-1\n                  || c != PROXY_CONNECTION[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {\n                parser->header_state = h_connection;\n              }\n              break;\n\n            /* content-length */\n\n            case h_matching_content_length:\n              parser->index++;\n              if (parser->index > sizeof(CONTENT_LENGTH)-1\n                  || c != CONTENT_LENGTH[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {\n                parser->header_state = h_content_length;\n              }\n              break;\n\n            /* transfer-encoding */\n\n            case h_matching_transfer_encoding:\n              parser->index++;\n              if (parser->index > sizeof(TRANSFER_ENCODING)-1\n                  || c != TRANSFER_ENCODING[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {\n                parser->header_state = h_transfer_encoding;\n              }\n              break;\n\n            /* upgrade */\n\n            case h_matching_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE)-1\n                  || c != UPGRADE[parser->index]) {\n                parser->header_state = h_general;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                parser->header_state = h_upgrade;\n              }\n              break;\n\n            case h_connection:\n            case h_content_length:\n            case h_transfer_encoding:\n            case h_upgrade:\n              if (ch != ' ') parser->header_state = h_general;\n              break;\n\n            default:\n              assert(0 && \"Unknown header_state\");\n              break;\n          }\n        }\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len) {\n          --p;\n          break;\n        }\n\n        if (ch == ':') {\n          UPDATE_STATE(s_header_value_discard_ws);\n          CALLBACK_DATA(header_field);\n          break;\n        }\n\n        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);\n        goto error;\n      }\n\n      case s_header_value_discard_ws:\n        if (ch == ' ' || ch == '\\t') break;\n\n        if (ch == CR) {\n          UPDATE_STATE(s_header_value_discard_ws_almost_done);\n          break;\n        }\n\n        if (ch == LF) {\n          UPDATE_STATE(s_header_value_discard_lws);\n          break;\n        }\n\n        /* FALLTHROUGH */\n\n      case s_header_value_start:\n      {\n        MARK(header_value);\n\n        UPDATE_STATE(s_header_value);\n        parser->index = 0;\n\n        c = LOWER(ch);\n\n        switch (parser->header_state) {\n          case h_upgrade:\n            parser->flags |= F_UPGRADE;\n            parser->header_state = h_general;\n            break;\n\n          case h_transfer_encoding:\n            /* looking for 'Transfer-Encoding: chunked' */\n            if ('c' == c) {\n              parser->header_state = h_matching_transfer_encoding_chunked;\n            } else {\n              parser->header_state = h_general;\n            }\n            break;\n\n          case h_content_length:\n            if (UNLIKELY(!IS_NUM(ch))) {\n              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n              goto error;\n            }\n\n            parser->content_length = ch - '0';\n            break;\n\n          case h_connection:\n            /* looking for 'Connection: keep-alive' */\n            if (c == 'k') {\n              parser->header_state = h_matching_connection_keep_alive;\n            /* looking for 'Connection: close' */\n            } else if (c == 'c') {\n              parser->header_state = h_matching_connection_close;\n            } else if (c == 'u') {\n              parser->header_state = h_matching_connection_upgrade;\n            } else {\n              parser->header_state = h_matching_connection_token;\n            }\n            break;\n\n          /* Multi-value `Connection` header */\n          case h_matching_connection_token_start:\n            break;\n\n          default:\n            parser->header_state = h_general;\n            break;\n        }\n        break;\n      }\n\n      case s_header_value:\n      {\n        const char* start = p;\n        enum header_states h_state = (enum header_states) parser->header_state;\n        for (; p != data + len; p++) {\n          ch = *p;\n          if (ch == CR) {\n            UPDATE_STATE(s_header_almost_done);\n            parser->header_state = h_state;\n            CALLBACK_DATA(header_value);\n            break;\n          }\n\n          if (ch == LF) {\n            UPDATE_STATE(s_header_almost_done);\n            COUNT_HEADER_SIZE(p - start);\n            parser->header_state = h_state;\n            CALLBACK_DATA_NOADVANCE(header_value);\n            REEXECUTE();\n          }\n\n          c = LOWER(ch);\n\n          switch (h_state) {\n            case h_general:\n            {\n              const char* p_cr;\n              const char* p_lf;\n              size_t limit = data + len - p;\n\n              limit = MIN(limit, HTTP_MAX_HEADER_SIZE);\n\n              p_cr = (const char*) memchr(p, CR, limit);\n              p_lf = (const char*) memchr(p, LF, limit);\n              if (p_cr != NULL) {\n                if (p_lf != NULL && p_cr >= p_lf)\n                  p = p_lf;\n                else\n                  p = p_cr;\n              } else if (UNLIKELY(p_lf != NULL)) {\n                p = p_lf;\n              } else {\n                p = data + len;\n              }\n              --p;\n\n              break;\n            }\n\n            case h_connection:\n            case h_transfer_encoding:\n              assert(0 && \"Shouldn't get here.\");\n              break;\n\n            case h_content_length:\n            {\n              uint64_t t;\n\n              if (ch == ' ') break;\n\n              if (UNLIKELY(!IS_NUM(ch))) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\n                goto error;\n              }\n\n              t = parser->content_length;\n              t *= 10;\n              t += ch - '0';\n\n              /* Overflow? Test against a conservative limit for simplicity. */\n              if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {\n                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n                parser->header_state = h_state;\n                goto error;\n              }\n\n              parser->content_length = t;\n              break;\n            }\n\n            /* Transfer-Encoding: chunked */\n            case h_matching_transfer_encoding_chunked:\n              parser->index++;\n              if (parser->index > sizeof(CHUNKED)-1\n                  || c != CHUNKED[parser->index]) {\n                h_state = h_general;\n              } else if (parser->index == sizeof(CHUNKED)-2) {\n                h_state = h_transfer_encoding_chunked;\n              }\n              break;\n\n            case h_matching_connection_token_start:\n              /* looking for 'Connection: keep-alive' */\n              if (c == 'k') {\n                h_state = h_matching_connection_keep_alive;\n              /* looking for 'Connection: close' */\n              } else if (c == 'c') {\n                h_state = h_matching_connection_close;\n              } else if (c == 'u') {\n                h_state = h_matching_connection_upgrade;\n              } else if (STRICT_TOKEN(c)) {\n                h_state = h_matching_connection_token;\n              } else if (c == ' ' || c == '\\t') {\n                /* Skip lws */\n              } else {\n                h_state = h_general;\n              }\n              break;\n\n            /* looking for 'Connection: keep-alive' */\n            case h_matching_connection_keep_alive:\n              parser->index++;\n              if (parser->index > sizeof(KEEP_ALIVE)-1\n                  || c != KEEP_ALIVE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(KEEP_ALIVE)-2) {\n                h_state = h_connection_keep_alive;\n              }\n              break;\n\n            /* looking for 'Connection: close' */\n            case h_matching_connection_close:\n              parser->index++;\n              if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(CLOSE)-2) {\n                h_state = h_connection_close;\n              }\n              break;\n\n            /* looking for 'Connection: upgrade' */\n            case h_matching_connection_upgrade:\n              parser->index++;\n              if (parser->index > sizeof(UPGRADE) - 1 ||\n                  c != UPGRADE[parser->index]) {\n                h_state = h_matching_connection_token;\n              } else if (parser->index == sizeof(UPGRADE)-2) {\n                h_state = h_connection_upgrade;\n              }\n              break;\n\n            case h_matching_connection_token:\n              if (ch == ',') {\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              }\n              break;\n\n            case h_transfer_encoding_chunked:\n              if (ch != ' ') h_state = h_general;\n              break;\n\n            case h_connection_keep_alive:\n            case h_connection_close:\n            case h_connection_upgrade:\n              if (ch == ',') {\n                if (h_state == h_connection_keep_alive) {\n                  parser->flags |= F_CONNECTION_KEEP_ALIVE;\n                } else if (h_state == h_connection_close) {\n                  parser->flags |= F_CONNECTION_CLOSE;\n                } else if (h_state == h_connection_upgrade) {\n                  parser->flags |= F_CONNECTION_UPGRADE;\n                }\n                h_state = h_matching_connection_token_start;\n                parser->index = 0;\n              } else if (ch != ' ') {\n                h_state = h_matching_connection_token;\n              }\n              break;\n\n            default:\n              UPDATE_STATE(s_header_value);\n              h_state = h_general;\n              break;\n          }\n        }\n        parser->header_state = h_state;\n\n        COUNT_HEADER_SIZE(p - start);\n\n        if (p == data + len)\n          --p;\n        break;\n      }\n\n      case s_header_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        UPDATE_STATE(s_header_value_lws);\n        break;\n      }\n\n      case s_header_value_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_start);\n          REEXECUTE();\n        }\n\n        /* finished the header */\n        switch (parser->header_state) {\n          case h_connection_keep_alive:\n            parser->flags |= F_CONNECTION_KEEP_ALIVE;\n            break;\n          case h_connection_close:\n            parser->flags |= F_CONNECTION_CLOSE;\n            break;\n          case h_transfer_encoding_chunked:\n            parser->flags |= F_CHUNKED;\n            break;\n          case h_connection_upgrade:\n            parser->flags |= F_CONNECTION_UPGRADE;\n            break;\n          default:\n            break;\n        }\n\n        UPDATE_STATE(s_header_field_start);\n        REEXECUTE();\n      }\n\n      case s_header_value_discard_ws_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n        UPDATE_STATE(s_header_value_discard_lws);\n        break;\n      }\n\n      case s_header_value_discard_lws:\n      {\n        if (ch == ' ' || ch == '\\t') {\n          UPDATE_STATE(s_header_value_discard_ws);\n          break;\n        } else {\n          switch (parser->header_state) {\n            case h_connection_keep_alive:\n              parser->flags |= F_CONNECTION_KEEP_ALIVE;\n              break;\n            case h_connection_close:\n              parser->flags |= F_CONNECTION_CLOSE;\n              break;\n            case h_connection_upgrade:\n              parser->flags |= F_CONNECTION_UPGRADE;\n              break;\n            case h_transfer_encoding_chunked:\n              parser->flags |= F_CHUNKED;\n              break;\n            default:\n              break;\n          }\n\n          /* header value was empty */\n          MARK(header_value);\n          UPDATE_STATE(s_header_field_start);\n          CALLBACK_DATA_NOADVANCE(header_value);\n          REEXECUTE();\n        }\n      }\n\n      case s_headers_almost_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        if (parser->flags & F_TRAILING) {\n          /* End of a chunked request */\n          UPDATE_STATE(s_message_done);\n          CALLBACK_NOTIFY_NOADVANCE(chunk_complete);\n          REEXECUTE();\n        }\n\n        UPDATE_STATE(s_headers_done);\n\n        /* Set this here so that on_headers_complete() callbacks can see it */\n        parser->upgrade =\n          ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==\n           (F_UPGRADE | F_CONNECTION_UPGRADE) ||\n           parser->method == HTTP_CONNECT);\n\n        /* Here we call the headers_complete callback. This is somewhat\n         * different than other callbacks because if the user returns 1, we\n         * will interpret that as saying that this message has no body. This\n         * is needed for the annoying case of recieving a response to a HEAD\n         * request.\n         *\n         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so\n         * we have to simulate it by handling a change in errno below.\n         */\n        if (settings->on_headers_complete) {\n          switch (settings->on_headers_complete(parser)) {\n            case 0:\n              break;\n\n            case 1:\n              parser->flags |= F_SKIPBODY;\n              break;\n\n            default:\n              SET_ERRNO(HPE_CB_headers_complete);\n              RETURN(p - data); /* Error */\n          }\n        }\n\n        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {\n          RETURN(p - data);\n        }\n\n        REEXECUTE();\n      }\n\n      case s_headers_done:\n      {\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        int hasBody = parser->flags & F_CHUNKED ||\n          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);\n        if (parser->upgrade && (parser->method == HTTP_CONNECT ||\n                                (parser->flags & F_SKIPBODY) || !hasBody)) {\n          /* Exit, the rest of the message is in a different protocol. */\n          /* We want to use http_parser for tunneling connection\n             transparently */\n          /* Read body until EOF */\n          UPDATE_STATE(s_body_identity_eof);\n          break;\n          /* UPDATE_STATE(NEW_MESSAGE()); */\n          /* CALLBACK_NOTIFY(message_complete); */\n          /* RETURN((p - data) + 1); */\n        }\n\n        if (parser->flags & F_SKIPBODY) {\n          UPDATE_STATE(NEW_MESSAGE());\n          CALLBACK_NOTIFY(message_complete);\n        } else if (parser->flags & F_CHUNKED) {\n          /* chunked encoding - ignore Content-Length header */\n          UPDATE_STATE(s_chunk_size_start);\n        } else {\n          if (parser->content_length == 0) {\n            /* Content-Length header given but zero: Content-Length: 0\\r\\n */\n            UPDATE_STATE(NEW_MESSAGE());\n            CALLBACK_NOTIFY(message_complete);\n          } else if (parser->content_length != ULLONG_MAX) {\n            /* Content-Length header given and non-zero */\n            UPDATE_STATE(s_body_identity);\n          } else {\n            if (!http_message_needs_eof(parser)) {\n              /* Assume content-length 0 - read the next */\n              UPDATE_STATE(NEW_MESSAGE());\n              CALLBACK_NOTIFY(message_complete);\n            } else {\n              /* Read body until EOF */\n              UPDATE_STATE(s_body_identity_eof);\n            }\n          }\n        }\n\n        break;\n      }\n\n      case s_body_identity:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* The difference between advancing content_length and p is because\n         * the latter will automaticaly advance on the next loop iteration.\n         * Further, if content_length ends up at 0, we want to see the last\n         * byte again for our message complete callback.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          UPDATE_STATE(s_message_done);\n\n          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.\n           *\n           * The alternative to doing this is to wait for the next byte to\n           * trigger the data callback, just as in every other case. The\n           * problem with this is that this makes it difficult for the test\n           * harness to distinguish between complete-on-EOF and\n           * complete-on-length. It's not clear that this distinction is\n           * important for applications, but let's keep it for now.\n           */\n          CALLBACK_DATA_(body, p - body_mark + 1, p - data);\n          REEXECUTE();\n        }\n\n        break;\n      }\n\n      /* read until EOF */\n      case s_body_identity_eof:\n        MARK(body);\n        p = data + len - 1;\n\n        break;\n\n      case s_message_done:\n        UPDATE_STATE(NEW_MESSAGE());\n        CALLBACK_NOTIFY(message_complete);\n        if (parser->upgrade) {\n          /* Exit, the rest of the message is in a different protocol. */\n          RETURN((p - data) + 1);\n        }\n        break;\n\n      case s_chunk_size_start:\n      {\n        assert(parser->nread == 1);\n        assert(parser->flags & F_CHUNKED);\n\n        unhex_val = unhex[(unsigned char)ch];\n        if (UNLIKELY(unhex_val == -1)) {\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        parser->content_length = unhex_val;\n        UPDATE_STATE(s_chunk_size);\n        break;\n      }\n\n      case s_chunk_size:\n      {\n        uint64_t t;\n\n        assert(parser->flags & F_CHUNKED);\n\n        if (ch == CR) {\n          UPDATE_STATE(s_chunk_size_almost_done);\n          break;\n        }\n\n        unhex_val = unhex[(unsigned char)ch];\n\n        if (unhex_val == -1) {\n          if (ch == ';' || ch == ' ') {\n            UPDATE_STATE(s_chunk_parameters);\n            break;\n          }\n\n          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);\n          goto error;\n        }\n\n        t = parser->content_length;\n        t *= 16;\n        t += unhex_val;\n\n        /* Overflow? Test against a conservative limit for simplicity. */\n        if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {\n          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);\n          goto error;\n        }\n\n        parser->content_length = t;\n        break;\n      }\n\n      case s_chunk_parameters:\n      {\n        assert(parser->flags & F_CHUNKED);\n        /* just ignore this shit. TODO check for overflow */\n        if (ch == CR) {\n          UPDATE_STATE(s_chunk_size_almost_done);\n          break;\n        }\n        break;\n      }\n\n      case s_chunk_size_almost_done:\n      {\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n\n        parser->nread = 0;\n\n        if (parser->content_length == 0) {\n          parser->flags |= F_TRAILING;\n          UPDATE_STATE(s_header_field_start);\n        } else {\n          UPDATE_STATE(s_chunk_data);\n        }\n        CALLBACK_NOTIFY(chunk_header);\n        break;\n      }\n\n      case s_chunk_data:\n      {\n        uint64_t to_read = MIN(parser->content_length,\n                               (uint64_t) ((data + len) - p));\n\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length != 0\n            && parser->content_length != ULLONG_MAX);\n\n        /* See the explanation in s_body_identity for why the content\n         * length and data pointers are managed this way.\n         */\n        MARK(body);\n        parser->content_length -= to_read;\n        p += to_read - 1;\n\n        if (parser->content_length == 0) {\n          UPDATE_STATE(s_chunk_data_almost_done);\n        }\n\n        break;\n      }\n\n      case s_chunk_data_almost_done:\n        assert(parser->flags & F_CHUNKED);\n        assert(parser->content_length == 0);\n        STRICT_CHECK(ch != CR);\n        UPDATE_STATE(s_chunk_data_done);\n        CALLBACK_DATA(body);\n        break;\n\n      case s_chunk_data_done:\n        assert(parser->flags & F_CHUNKED);\n        STRICT_CHECK(ch != LF);\n        parser->nread = 0;\n        UPDATE_STATE(s_chunk_size_start);\n        CALLBACK_NOTIFY(chunk_complete);\n        break;\n\n      default:\n        assert(0 && \"unhandled state\");\n        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);\n        goto error;\n    }\n  }\n\n  /* Run callbacks for any marks that we have leftover after we ran our of\n   * bytes. There should be at most one of these set, so it's OK to invoke\n   * them in series (unset marks will not result in callbacks).\n   *\n   * We use the NOADVANCE() variety of callbacks here because 'p' has already\n   * overflowed 'data' and this allows us to correct for the off-by-one that\n   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'\n   * value that's in-bounds).\n   */\n\n  assert(((header_field_mark ? 1 : 0) +\n          (header_value_mark ? 1 : 0) +\n          (url_mark ? 1 : 0)  +\n          (body_mark ? 1 : 0) +\n          (status_mark ? 1 : 0)) <= 1);\n\n  CALLBACK_DATA_NOADVANCE(header_field);\n  CALLBACK_DATA_NOADVANCE(header_value);\n  CALLBACK_DATA_NOADVANCE(url);\n  CALLBACK_DATA_NOADVANCE(body);\n  CALLBACK_DATA_NOADVANCE(status);\n\n  RETURN(len);\n\nerror:\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {\n    SET_ERRNO(HPE_UNKNOWN);\n  }\n\n  RETURN(p - data);\n}\n\n\n/* Does the parser need to see an EOF to find the end of the message? */\nint\nhttp_message_needs_eof (const http_parser *parser)\n{\n  if (parser->type == HTTP_REQUEST) {\n    return 0;\n  }\n\n  /* See RFC 2616 section 4.4 */\n  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */\n      parser->status_code == 204 ||     /* No Content */\n      parser->status_code == 304 ||     /* Not Modified */\n      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */\n    return 0;\n  }\n\n  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {\n    return 0;\n  }\n\n  return 1;\n}\n\n\nint\nhttp_should_keep_alive (const http_parser *parser)\n{\n  if (parser->http_major > 0 && parser->http_minor > 0) {\n    /* HTTP/1.1 */\n    if (parser->flags & F_CONNECTION_CLOSE) {\n      return 0;\n    }\n  } else {\n    /* HTTP/1.0 or earlier */\n    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {\n      return 0;\n    }\n  }\n\n  return !http_message_needs_eof(parser);\n}\n\n\nconst char *\nhttp_method_str (enum http_method m)\n{\n  return ELEM_AT(method_strings, m, \"<unknown>\");\n}\n\n\nvoid\nhttp_parser_init (http_parser *parser, enum http_parser_type t)\n{\n  void *data = parser->data; /* preserve application data */\n  memset(parser, 0, sizeof(*parser));\n  parser->data = data;\n  parser->type = t;\n  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));\n  parser->http_errno = HPE_OK;\n}\n\nvoid\nhttp_parser_settings_init(http_parser_settings *settings)\n{\n  memset(settings, 0, sizeof(*settings));\n}\n\nconst char *\nhttp_errno_name(enum http_errno err) {\n  assert(((size_t) err) <\n      (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].name;\n}\n\nconst char *\nhttp_errno_description(enum http_errno err) {\n  assert(((size_t) err) <\n      (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0])));\n  return http_strerror_tab[err].description;\n}\n\nstatic enum http_host_state\nhttp_parse_host_char(enum http_host_state s, const char ch) {\n  switch(s) {\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      if (ch == '@') {\n        return s_http_host_start;\n      }\n\n      if (IS_USERINFO_CHAR(ch)) {\n        return s_http_userinfo;\n      }\n      break;\n\n    case s_http_host_start:\n      if (ch == '[') {\n        return s_http_host_v6_start;\n      }\n\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n      break;\n\n    case s_http_host:\n      if (IS_HOST_CHAR(ch)) {\n        return s_http_host;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_end:\n      if (ch == ':') {\n        return s_http_host_port_start;\n      }\n\n      break;\n\n    case s_http_host_v6:\n      if (ch == ']') {\n        return s_http_host_v6_end;\n      }\n\n    /* FALLTHROUGH */\n    case s_http_host_v6_start:\n      if (IS_HEX(ch) || ch == ':' || ch == '.') {\n        return s_http_host_v6;\n      }\n\n      break;\n\n    case s_http_host_port:\n    case s_http_host_port_start:\n      if (IS_NUM(ch)) {\n        return s_http_host_port;\n      }\n\n      break;\n\n    default:\n      break;\n  }\n  return s_http_host_dead;\n}\n\nstatic int\nhttp_parse_host(const char * buf, struct http_parser_url *u, int found_at) {\n  assert(u->field_set & (1 << UF_HOST));\n  enum http_host_state s;\n\n  const char *p;\n  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;\n\n  u->field_data[UF_HOST].len = 0;\n\n  s = found_at ? s_http_userinfo_start : s_http_host_start;\n\n  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {\n    enum http_host_state new_s = http_parse_host_char(s, *p);\n\n    if (new_s == s_http_host_dead) {\n      return 1;\n    }\n\n    switch(new_s) {\n      case s_http_host:\n        if (s != s_http_host) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_v6:\n        if (s != s_http_host_v6) {\n          u->field_data[UF_HOST].off = p - buf;\n        }\n        u->field_data[UF_HOST].len++;\n        break;\n\n      case s_http_host_port:\n        if (s != s_http_host_port) {\n          u->field_data[UF_PORT].off = p - buf;\n          u->field_data[UF_PORT].len = 0;\n          u->field_set |= (1 << UF_PORT);\n        }\n        u->field_data[UF_PORT].len++;\n        break;\n\n      case s_http_userinfo:\n        if (s != s_http_userinfo) {\n          u->field_data[UF_USERINFO].off = p - buf ;\n          u->field_data[UF_USERINFO].len = 0;\n          u->field_set |= (1 << UF_USERINFO);\n        }\n        u->field_data[UF_USERINFO].len++;\n        break;\n\n      default:\n        break;\n    }\n    s = new_s;\n  }\n\n  /* Make sure we don't end somewhere unexpected */\n  switch (s) {\n    case s_http_host_start:\n    case s_http_host_v6_start:\n    case s_http_host_v6:\n    case s_http_host_port_start:\n    case s_http_userinfo:\n    case s_http_userinfo_start:\n      return 1;\n    default:\n      break;\n  }\n\n  return 0;\n}\n\nint\nhttp_parser_parse_url(const char *buf, size_t buflen, int is_connect,\n                      struct http_parser_url *u)\n{\n  enum state s;\n  const char *p;\n  enum http_parser_url_fields uf, old_uf;\n  int found_at = 0;\n\n  u->port = u->field_set = 0;\n  s = is_connect ? s_req_server_start : s_req_spaces_before_url;\n  old_uf = UF_MAX;\n\n  for (p = buf; p < buf + buflen; p++) {\n    s = parse_url_char(s, *p);\n\n    /* Figure out the next field that we're operating on */\n    switch (s) {\n      case s_dead:\n        return 1;\n\n      /* Skip delimeters */\n      case s_req_schema_slash:\n      case s_req_schema_slash_slash:\n      case s_req_server_start:\n      case s_req_query_string_start:\n      case s_req_fragment_start:\n        continue;\n\n      case s_req_schema:\n        uf = UF_SCHEMA;\n        break;\n\n      case s_req_server_with_at:\n        found_at = 1;\n\n      /* FALLTROUGH */\n      case s_req_server:\n        uf = UF_HOST;\n        break;\n\n      case s_req_path:\n        uf = UF_PATH;\n        break;\n\n      case s_req_query_string:\n        uf = UF_QUERY;\n        break;\n\n      case s_req_fragment:\n        uf = UF_FRAGMENT;\n        break;\n\n      default:\n        assert(!\"Unexpected state\");\n        return 1;\n    }\n\n    /* Nothing's changed; soldier on */\n    if (uf == old_uf) {\n      u->field_data[uf].len++;\n      continue;\n    }\n\n    u->field_data[uf].off = p - buf;\n    u->field_data[uf].len = 1;\n\n    u->field_set |= (1 << uf);\n    old_uf = uf;\n  }\n\n  /* host must be present if there is a schema */\n  /* parsing http:///toto will fail */\n  if ((u->field_set & (1 << UF_SCHEMA)) &&\n      (u->field_set & (1 << UF_HOST)) == 0) {\n    return 1;\n  }\n\n  if (u->field_set & (1 << UF_HOST)) {\n    if (http_parse_host(buf, u, found_at) != 0) {\n      return 1;\n    }\n  }\n\n  /* CONNECT requests can only contain \"hostname:port\" */\n  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {\n    return 1;\n  }\n\n  if (u->field_set & (1 << UF_PORT)) {\n    /* Don't bother with endp; we've already validated the string */\n    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);\n\n    /* Ports have a max value of 2^16 */\n    if (v > 0xffff) {\n      return 1;\n    }\n\n    u->port = (uint16_t) v;\n  }\n\n  return 0;\n}\n\nvoid\nhttp_parser_pause(http_parser *parser, int paused) {\n  /* Users should only be pausing/unpausing a parser that is not in an error\n   * state. In non-debug builds, there's not much that we can do about this\n   * other than ignore it.\n   */\n  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||\n      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {\n    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);\n  } else {\n    assert(0 && \"Attempting to pause parser in error state\");\n  }\n}\n\nint\nhttp_body_is_final(const struct http_parser *parser) {\n    return parser->state == s_message_done;\n}\n\nunsigned long\nhttp_parser_version(void) {\n  return HTTP_PARSER_VERSION_MAJOR * 0x10000 |\n         HTTP_PARSER_VERSION_MINOR * 0x00100 |\n         HTTP_PARSER_VERSION_PATCH * 0x00001;\n}\n"
  },
  {
    "path": "src/http-parser/http_parser.gyp",
    "content": "# This file is used with the GYP meta build system.\n# http://code.google.com/p/gyp/\n# To build try this:\n#   svn co http://gyp.googlecode.com/svn/trunk gyp\n#   ./gyp/gyp -f make --depth=`pwd` http_parser.gyp \n#   ./out/Debug/test \n{\n  'target_defaults': {\n    'default_configuration': 'Debug',\n    'configurations': {\n      # TODO: hoist these out and put them somewhere common, because\n      #       RuntimeLibrary MUST MATCH across the entire project\n      'Debug': {\n        'defines': [ 'DEBUG', '_DEBUG' ],\n        'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],\n        'msvs_settings': {\n          'VCCLCompilerTool': {\n            'RuntimeLibrary': 1, # static debug\n          },\n        },\n      },\n      'Release': {\n        'defines': [ 'NDEBUG' ],\n        'cflags': [ '-Wall', '-Wextra', '-O3' ],\n        'msvs_settings': {\n          'VCCLCompilerTool': {\n            'RuntimeLibrary': 0, # static release\n          },\n        },\n      }\n    },\n    'msvs_settings': {\n      'VCCLCompilerTool': {\n      },\n      'VCLibrarianTool': {\n      },\n      'VCLinkerTool': {\n        'GenerateDebugInformation': 'true',\n      },\n    },\n    'conditions': [\n      ['OS == \"win\"', {\n        'defines': [\n          'WIN32'\n        ],\n      }]\n    ],\n  },\n\n  'targets': [\n    {\n      'target_name': 'http_parser',\n      'type': 'static_library',\n      'include_dirs': [ '.' ],\n      'direct_dependent_settings': {\n        'defines': [ 'HTTP_PARSER_STRICT=0' ],\n        'include_dirs': [ '.' ],\n      },\n      'defines': [ 'HTTP_PARSER_STRICT=0' ],\n      'sources': [ './http_parser.c', ],\n      'conditions': [\n        ['OS==\"win\"', {\n          'msvs_settings': {\n            'VCCLCompilerTool': {\n              # Compile as C++. http_parser.c is actually C99, but C++ is\n              # close enough in this case.\n              'CompileAs': 2,\n            },\n          },\n        }]\n      ],\n    },\n\n    {\n      'target_name': 'http_parser_strict',\n      'type': 'static_library',\n      'include_dirs': [ '.' ],\n      'direct_dependent_settings': {\n        'defines': [ 'HTTP_PARSER_STRICT=1' ],\n        'include_dirs': [ '.' ],\n      },\n      'defines': [ 'HTTP_PARSER_STRICT=1' ],\n      'sources': [ './http_parser.c', ],\n      'conditions': [\n        ['OS==\"win\"', {\n          'msvs_settings': {\n            'VCCLCompilerTool': {\n              # Compile as C++. http_parser.c is actually C99, but C++ is\n              # close enough in this case.\n              'CompileAs': 2,\n            },\n          },\n        }]\n      ],\n    },\n\n    {\n      'target_name': 'test-nonstrict',\n      'type': 'executable',\n      'dependencies': [ 'http_parser' ],\n      'sources': [ 'test.c' ]\n    },\n\n    {\n      'target_name': 'test-strict',\n      'type': 'executable',\n      'dependencies': [ 'http_parser_strict' ],\n      'sources': [ 'test.c' ]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/http-parser/http_parser.h",
    "content": "/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#ifndef http_parser_h\n#define http_parser_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Also update SONAME in the Makefile whenever you change these. */\n#define HTTP_PARSER_VERSION_MAJOR 2\n#define HTTP_PARSER_VERSION_MINOR 5\n#define HTTP_PARSER_VERSION_PATCH 0\n\n#include <sys/types.h>\n#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)\n#include <BaseTsd.h>\n#include <stddef.h>\ntypedef __int8 int8_t;\ntypedef unsigned __int8 uint8_t;\ntypedef __int16 int16_t;\ntypedef unsigned __int16 uint16_t;\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n#include <stdint.h>\n#endif\n\n/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run\n * faster\n */\n#ifndef HTTP_PARSER_STRICT\n# define HTTP_PARSER_STRICT 1\n#endif\n\n/* Maximium header size allowed. If the macro is not defined\n * before including this header then the default is used. To\n * change the maximum header size, define the macro in the build\n * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove\n * the effective limit on the size of the header, define the macro\n * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)\n */\n#ifndef HTTP_MAX_HEADER_SIZE\n# define HTTP_MAX_HEADER_SIZE (80*1024)\n#endif\n\ntypedef struct http_parser http_parser;\ntypedef struct http_parser_settings http_parser_settings;\n\n\n/* Callbacks should return non-zero to indicate an error. The parser will\n * then halt execution.\n *\n * The one exception is on_headers_complete. In a HTTP_RESPONSE parser\n * returning '1' from on_headers_complete will tell the parser that it\n * should not expect a body. This is used when receiving a response to a\n * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:\n * chunked' headers that indicate the presence of a body.\n *\n * http_data_cb does not return data chunks. It will be called arbitrarily\n * many times for each string. E.G. you might get 10 callbacks for \"on_url\"\n * each providing just a few characters more data.\n */\ntypedef int (*http_data_cb) (http_parser*, const char *at, size_t length);\ntypedef int (*http_cb) (http_parser*);\n\n\n/* Request Methods */\n#define HTTP_METHOD_MAP(XX)         \\\n  XX(0,  DELETE,      DELETE)       \\\n  XX(1,  GET,         GET)          \\\n  XX(2,  HEAD,        HEAD)         \\\n  XX(3,  POST,        POST)         \\\n  XX(4,  PUT,         PUT)          \\\n  /* pathological */                \\\n  XX(5,  CONNECT,     CONNECT)      \\\n  XX(6,  OPTIONS,     OPTIONS)      \\\n  XX(7,  TRACE,       TRACE)        \\\n  /* webdav */                      \\\n  XX(8,  COPY,        COPY)         \\\n  XX(9,  LOCK,        LOCK)         \\\n  XX(10, MKCOL,       MKCOL)        \\\n  XX(11, MOVE,        MOVE)         \\\n  XX(12, PROPFIND,    PROPFIND)     \\\n  XX(13, PROPPATCH,   PROPPATCH)    \\\n  XX(14, SEARCH,      SEARCH)       \\\n  XX(15, UNLOCK,      UNLOCK)       \\\n  /* subversion */                  \\\n  XX(16, REPORT,      REPORT)       \\\n  XX(17, MKACTIVITY,  MKACTIVITY)   \\\n  XX(18, CHECKOUT,    CHECKOUT)     \\\n  XX(19, MERGE,       MERGE)        \\\n  /* upnp */                        \\\n  XX(20, MSEARCH,     M-SEARCH)     \\\n  XX(21, NOTIFY,      NOTIFY)       \\\n  XX(22, SUBSCRIBE,   SUBSCRIBE)    \\\n  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)  \\\n  /* RFC-5789 */                    \\\n  XX(24, PATCH,       PATCH)        \\\n  XX(25, PURGE,       PURGE)        \\\n  /* CalDAV */                      \\\n  XX(26, MKCALENDAR,  MKCALENDAR)   \\\n\nenum http_method\n  {\n#define XX(num, name, string) HTTP_##name = num,\n  HTTP_METHOD_MAP(XX)\n#undef XX\n  };\n\n\nenum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };\n\n\n/* Flag values for http_parser.flags field */\nenum flags\n  { F_CHUNKED               = 1 << 0\n  , F_CONNECTION_KEEP_ALIVE = 1 << 1\n  , F_CONNECTION_CLOSE      = 1 << 2\n  , F_CONNECTION_UPGRADE    = 1 << 3\n  , F_TRAILING              = 1 << 4\n  , F_UPGRADE               = 1 << 5\n  , F_SKIPBODY              = 1 << 6\n  };\n\n\n/* Map for errno-related constants\n * \n * The provided argument should be a macro that takes 2 arguments.\n */\n#define HTTP_ERRNO_MAP(XX)                                           \\\n  /* No error */                                                     \\\n  XX(OK, \"success\")                                                  \\\n                                                                     \\\n  /* Callback-related errors */                                      \\\n  XX(CB_message_begin, \"the on_message_begin callback failed\")       \\\n  XX(CB_url, \"the on_url callback failed\")                           \\\n  XX(CB_header_field, \"the on_header_field callback failed\")         \\\n  XX(CB_header_value, \"the on_header_value callback failed\")         \\\n  XX(CB_headers_complete, \"the on_headers_complete callback failed\") \\\n  XX(CB_body, \"the on_body callback failed\")                         \\\n  XX(CB_message_complete, \"the on_message_complete callback failed\") \\\n  XX(CB_status, \"the on_status callback failed\")                     \\\n  XX(CB_chunk_header, \"the on_chunk_header callback failed\")         \\\n  XX(CB_chunk_complete, \"the on_chunk_complete callback failed\")     \\\n                                                                     \\\n  /* Parsing-related errors */                                       \\\n  XX(INVALID_EOF_STATE, \"stream ended at an unexpected time\")        \\\n  XX(HEADER_OVERFLOW,                                                \\\n     \"too many header bytes seen; overflow detected\")                \\\n  XX(CLOSED_CONNECTION,                                              \\\n     \"data received after completed connection: close message\")      \\\n  XX(INVALID_VERSION, \"invalid HTTP version\")                        \\\n  XX(INVALID_STATUS, \"invalid HTTP status code\")                     \\\n  XX(INVALID_METHOD, \"invalid HTTP method\")                          \\\n  XX(INVALID_URL, \"invalid URL\")                                     \\\n  XX(INVALID_HOST, \"invalid host\")                                   \\\n  XX(INVALID_PORT, \"invalid port\")                                   \\\n  XX(INVALID_PATH, \"invalid path\")                                   \\\n  XX(INVALID_QUERY_STRING, \"invalid query string\")                   \\\n  XX(INVALID_FRAGMENT, \"invalid fragment\")                           \\\n  XX(LF_EXPECTED, \"LF character expected\")                           \\\n  XX(INVALID_HEADER_TOKEN, \"invalid character in header\")            \\\n  XX(INVALID_CONTENT_LENGTH,                                         \\\n     \"invalid character in content-length header\")                   \\\n  XX(INVALID_CHUNK_SIZE,                                             \\\n     \"invalid character in chunk size header\")                       \\\n  XX(INVALID_CONSTANT, \"invalid constant string\")                    \\\n  XX(INVALID_INTERNAL_STATE, \"encountered unexpected internal state\")\\\n  XX(STRICT, \"strict mode assertion failed\")                         \\\n  XX(PAUSED, \"parser is paused\")                                     \\\n  XX(UNKNOWN, \"an unknown error occurred\")\n\n\n/* Define HPE_* values for each errno value above */\n#define HTTP_ERRNO_GEN(n, s) HPE_##n,\nenum http_errno {\n  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)\n};\n#undef HTTP_ERRNO_GEN\n\n\n/* Get an http_errno value from an http_parser */\n#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)\n\n\nstruct http_parser {\n  /** PRIVATE **/\n  unsigned int type : 2;         /* enum http_parser_type */\n  unsigned int flags : 7;        /* F_* values from 'flags' enum; semi-public */\n  unsigned int state : 7;        /* enum state from http_parser.c */\n  unsigned int header_state : 8; /* enum header_state from http_parser.c */\n  unsigned int index : 8;        /* index into current matcher */\n\n  uint32_t nread;          /* # bytes read in various scenarios */\n  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */\n\n  /** READ-ONLY **/\n  unsigned short http_major;\n  unsigned short http_minor;\n  unsigned int status_code : 16; /* responses only */\n  unsigned int method : 8;       /* requests only */\n  unsigned int http_errno : 7;\n\n  /* 1 = Upgrade header was present and the parser has exited because of that.\n   * 0 = No upgrade header present.\n   * Should be checked when http_parser_execute() returns in addition to\n   * error checking.\n   */\n  unsigned int upgrade : 1;\n\n  /** PUBLIC **/\n  void *data; /* A pointer to get hook to the \"connection\" or \"socket\" object */\n};\n\n\nstruct http_parser_settings {\n  http_cb      on_message_begin;\n  http_data_cb on_url;\n  http_data_cb on_status;\n  http_data_cb on_header_field;\n  http_data_cb on_header_value;\n  http_cb      on_headers_complete;\n  http_data_cb on_body;\n  http_cb      on_message_complete;\n  /* When on_chunk_header is called, the current chunk length is stored\n   * in parser->content_length.\n   */\n  http_cb      on_chunk_header;\n  http_cb      on_chunk_complete;\n};\n\n\nenum http_parser_url_fields\n  { UF_SCHEMA           = 0\n  , UF_HOST             = 1\n  , UF_PORT             = 2\n  , UF_PATH             = 3\n  , UF_QUERY            = 4\n  , UF_FRAGMENT         = 5\n  , UF_USERINFO         = 6\n  , UF_MAX              = 7\n  };\n\n\n/* Result structure for http_parser_parse_url().\n *\n * Callers should index into field_data[] with UF_* values iff field_set\n * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and\n * because we probably have padding left over), we convert any port to\n * a uint16_t.\n */\nstruct http_parser_url {\n  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */\n  uint16_t port;                /* Converted UF_PORT string */\n\n  struct {\n    uint16_t off;               /* Offset into buffer in which field starts */\n    uint16_t len;               /* Length of run in buffer */\n  } field_data[UF_MAX];\n};\n\n\n/* Returns the library version. Bits 16-23 contain the major version number,\n * bits 8-15 the minor version number and bits 0-7 the patch level.\n * Usage example:\n *\n *   unsigned long version = http_parser_version();\n *   unsigned major = (version >> 16) & 255;\n *   unsigned minor = (version >> 8) & 255;\n *   unsigned patch = version & 255;\n *   printf(\"http_parser v%u.%u.%u\\n\", major, minor, patch);\n */\nunsigned long http_parser_version(void);\n\nvoid http_parser_init(http_parser *parser, enum http_parser_type type);\n\n\n/* Initialize http_parser_settings members to 0\n */\nvoid http_parser_settings_init(http_parser_settings *settings);\n\n\n/* Executes the parser. Returns number of parsed bytes. Sets\n * `parser->http_errno` on error. */\nsize_t http_parser_execute(http_parser *parser,\n                           const http_parser_settings *settings,\n                           const char *data,\n                           size_t len);\n\n\n/* If http_should_keep_alive() in the on_headers_complete or\n * on_message_complete callback returns 0, then this should be\n * the last message on the connection.\n * If you are the server, respond with the \"Connection: close\" header.\n * If you are the client, close the connection.\n */\nint http_should_keep_alive(const http_parser *parser);\n\n/* Returns a string version of the HTTP method. */\nconst char *http_method_str(enum http_method m);\n\n/* Return a string name of the given error */\nconst char *http_errno_name(enum http_errno err);\n\n/* Return a string description of the given error */\nconst char *http_errno_description(enum http_errno err);\n\n/* Parse a URL; return nonzero on failure */\nint http_parser_parse_url(const char *buf, size_t buflen,\n                          int is_connect,\n                          struct http_parser_url *u);\n\n/* Pause or un-pause the parser; a nonzero value pauses */\nvoid http_parser_pause(http_parser *parser, int paused);\n\n/* Checks if this is the final chunk of the body. */\nint http_body_is_final(const http_parser *parser);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "src/http-parser/test.c",
    "content": "/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n#include \"http_parser.h\"\n#include <stdlib.h>\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h> /* rand */\n#include <string.h>\n#include <stdarg.h>\n\n#if defined(__APPLE__)\n# undef strlcat\n# undef strlncpy\n# undef strlcpy\n#endif  /* defined(__APPLE__) */\n\n#undef TRUE\n#define TRUE 1\n#undef FALSE\n#define FALSE 0\n\n#define MAX_HEADERS 13\n#define MAX_ELEMENT_SIZE 2048\n#define MAX_CHUNKS 16\n\n#define MIN(a,b) ((a) < (b) ? (a) : (b))\n\nstatic http_parser *parser;\n\nstruct message {\n  const char *name; // for debugging purposes\n  const char *raw;\n  enum http_parser_type type;\n  enum http_method method;\n  int status_code;\n  char response_status[MAX_ELEMENT_SIZE];\n  char request_path[MAX_ELEMENT_SIZE];\n  char request_url[MAX_ELEMENT_SIZE];\n  char fragment[MAX_ELEMENT_SIZE];\n  char query_string[MAX_ELEMENT_SIZE];\n  char body[MAX_ELEMENT_SIZE];\n  size_t body_size;\n  const char *host;\n  const char *userinfo;\n  uint16_t port;\n  int num_headers;\n  enum { NONE=0, FIELD, VALUE } last_header_element;\n  char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];\n  int should_keep_alive;\n\n  int num_chunks;\n  int num_chunks_complete;\n  int chunk_lengths[MAX_CHUNKS];\n\n  const char *upgrade; // upgraded body\n\n  unsigned short http_major;\n  unsigned short http_minor;\n\n  int message_begin_cb_called;\n  int headers_complete_cb_called;\n  int message_complete_cb_called;\n  int message_complete_on_eof;\n  int body_is_final;\n};\n\nstatic int currently_parsing_eof;\n\nstatic struct message messages[5];\nstatic int num_messages;\nstatic http_parser_settings *current_pause_parser;\n\n/* * R E Q U E S T S * */\nconst struct message requests[] =\n#define CURL_GET 0\n{ {.name= \"curl get\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /test HTTP/1.1\\r\\n\"\n         \"User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\\r\\n\"\n         \"Host: 0.0.0.0=5000\\r\\n\"\n         \"Accept: */*\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/test\"\n  ,.request_url= \"/test\"\n  ,.num_headers= 3\n  ,.headers=\n    { { \"User-Agent\", \"curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\" }\n    , { \"Host\", \"0.0.0.0=5000\" }\n    , { \"Accept\", \"*/*\" }\n    }\n  ,.body= \"\"\n  }\n\n#define FIREFOX_GET 1\n, {.name= \"firefox get\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /favicon.ico HTTP/1.1\\r\\n\"\n         \"Host: 0.0.0.0=5000\\r\\n\"\n         \"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\\r\\n\"\n         \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\n\"\n         \"Accept-Language: en-us,en;q=0.5\\r\\n\"\n         \"Accept-Encoding: gzip,deflate\\r\\n\"\n         \"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\\r\\n\"\n         \"Keep-Alive: 300\\r\\n\"\n         \"Connection: keep-alive\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/favicon.ico\"\n  ,.request_url= \"/favicon.ico\"\n  ,.num_headers= 8\n  ,.headers=\n    { { \"Host\", \"0.0.0.0=5000\" }\n    , { \"User-Agent\", \"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\" }\n    , { \"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\" }\n    , { \"Accept-Language\", \"en-us,en;q=0.5\" }\n    , { \"Accept-Encoding\", \"gzip,deflate\" }\n    , { \"Accept-Charset\", \"ISO-8859-1,utf-8;q=0.7,*;q=0.7\" }\n    , { \"Keep-Alive\", \"300\" }\n    , { \"Connection\", \"keep-alive\" }\n    }\n  ,.body= \"\"\n  }\n\n#define DUMBFUCK 2\n, {.name= \"dumbfuck\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /dumbfuck HTTP/1.1\\r\\n\"\n         \"aaaaaaaaaaaaa:++++++++++\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/dumbfuck\"\n  ,.request_url= \"/dumbfuck\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"aaaaaaaaaaaaa\",  \"++++++++++\" }\n    }\n  ,.body= \"\"\n  }\n\n#define FRAGMENT_IN_URI 3\n, {.name= \"fragment in url\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"page=1\"\n  ,.fragment= \"posts-17408\"\n  ,.request_path= \"/forums/1/topics/2375\"\n  /* XXX request url does include fragment? */\n  ,.request_url= \"/forums/1/topics/2375?page=1#posts-17408\"\n  ,.num_headers= 0\n  ,.body= \"\"\n  }\n\n#define GET_NO_HEADERS_NO_BODY 4\n, {.name= \"get no headers no body\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /get_no_headers_no_body/world HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE /* would need Connection: close */\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/get_no_headers_no_body/world\"\n  ,.request_url= \"/get_no_headers_no_body/world\"\n  ,.num_headers= 0\n  ,.body= \"\"\n  }\n\n#define GET_ONE_HEADER_NO_BODY 5\n, {.name= \"get one header no body\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /get_one_header_no_body HTTP/1.1\\r\\n\"\n         \"Accept: */*\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE /* would need Connection: close */\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/get_one_header_no_body\"\n  ,.request_url= \"/get_one_header_no_body\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Accept\" , \"*/*\" }\n    }\n  ,.body= \"\"\n  }\n\n#define GET_FUNKY_CONTENT_LENGTH 6\n, {.name= \"get funky content length body hello\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /get_funky_content_length_body_hello HTTP/1.0\\r\\n\"\n         \"conTENT-Length: 5\\r\\n\"\n         \"\\r\\n\"\n         \"HELLO\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/get_funky_content_length_body_hello\"\n  ,.request_url= \"/get_funky_content_length_body_hello\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"conTENT-Length\" , \"5\" }\n    }\n  ,.body= \"HELLO\"\n  }\n\n#define POST_IDENTITY_BODY_WORLD 7\n, {.name= \"post identity body world\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /post_identity_body_world?q=search#hey HTTP/1.1\\r\\n\"\n         \"Accept: */*\\r\\n\"\n         \"Transfer-Encoding: identity\\r\\n\"\n         \"Content-Length: 5\\r\\n\"\n         \"\\r\\n\"\n         \"World\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"q=search\"\n  ,.fragment= \"hey\"\n  ,.request_path= \"/post_identity_body_world\"\n  ,.request_url= \"/post_identity_body_world?q=search#hey\"\n  ,.num_headers= 3\n  ,.headers=\n    { { \"Accept\", \"*/*\" }\n    , { \"Transfer-Encoding\", \"identity\" }\n    , { \"Content-Length\", \"5\" }\n    }\n  ,.body= \"World\"\n  }\n\n#define POST_CHUNKED_ALL_YOUR_BASE 8\n, {.name= \"post - chunked body: all your base are belong to us\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /post_chunked_all_your_base HTTP/1.1\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"1e\\r\\nall your base are belong to us\\r\\n\"\n         \"0\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/post_chunked_all_your_base\"\n  ,.request_url= \"/post_chunked_all_your_base\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Transfer-Encoding\" , \"chunked\" }\n    }\n  ,.body= \"all your base are belong to us\"\n  ,.num_chunks_complete= 2\n  ,.chunk_lengths= { 0x1e }\n  }\n\n#define TWO_CHUNKS_MULT_ZERO_END 9\n, {.name= \"two chunks ; triple zero ending\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /two_chunks_mult_zero_end HTTP/1.1\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"5\\r\\nhello\\r\\n\"\n         \"6\\r\\n world\\r\\n\"\n         \"000\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/two_chunks_mult_zero_end\"\n  ,.request_url= \"/two_chunks_mult_zero_end\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Transfer-Encoding\", \"chunked\" }\n    }\n  ,.body= \"hello world\"\n  ,.num_chunks_complete= 3\n  ,.chunk_lengths= { 5, 6 }\n  }\n\n#define CHUNKED_W_TRAILING_HEADERS 10\n, {.name= \"chunked with trailing headers. blech.\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /chunked_w_trailing_headers HTTP/1.1\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"5\\r\\nhello\\r\\n\"\n         \"6\\r\\n world\\r\\n\"\n         \"0\\r\\n\"\n         \"Vary: *\\r\\n\"\n         \"Content-Type: text/plain\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/chunked_w_trailing_headers\"\n  ,.request_url= \"/chunked_w_trailing_headers\"\n  ,.num_headers= 3\n  ,.headers=\n    { { \"Transfer-Encoding\",  \"chunked\" }\n    , { \"Vary\", \"*\" }\n    , { \"Content-Type\", \"text/plain\" }\n    }\n  ,.body= \"hello world\"\n  ,.num_chunks_complete= 3\n  ,.chunk_lengths= { 5, 6 }\n  }\n\n#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11\n, {.name= \"with bullshit after the length\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /chunked_w_bullshit_after_length HTTP/1.1\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"5; ihatew3;whatthefuck=aretheseparametersfor\\r\\nhello\\r\\n\"\n         \"6; blahblah; blah\\r\\n world\\r\\n\"\n         \"0\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/chunked_w_bullshit_after_length\"\n  ,.request_url= \"/chunked_w_bullshit_after_length\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Transfer-Encoding\", \"chunked\" }\n    }\n  ,.body= \"hello world\"\n  ,.num_chunks_complete= 3\n  ,.chunk_lengths= { 5, 6 }\n  }\n\n#define WITH_QUOTES 12\n, {.name= \"with quotes\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /with_\\\"stupid\\\"_quotes?foo=\\\"bar\\\" HTTP/1.1\\r\\n\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"foo=\\\"bar\\\"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/with_\\\"stupid\\\"_quotes\"\n  ,.request_url= \"/with_\\\"stupid\\\"_quotes?foo=\\\"bar\\\"\"\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define APACHEBENCH_GET 13\n/* The server receiving this request SHOULD NOT wait for EOF\n * to know that content-length == 0.\n * How to represent this in a unit test? message_complete_on_eof\n * Compare with NO_CONTENT_LENGTH_RESPONSE.\n */\n, {.name = \"apachebench get\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /test HTTP/1.0\\r\\n\"\n         \"Host: 0.0.0.0:5000\\r\\n\"\n         \"User-Agent: ApacheBench/2.3\\r\\n\"\n         \"Accept: */*\\r\\n\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/test\"\n  ,.request_url= \"/test\"\n  ,.num_headers= 3\n  ,.headers= { { \"Host\", \"0.0.0.0:5000\" }\n             , { \"User-Agent\", \"ApacheBench/2.3\" }\n             , { \"Accept\", \"*/*\" }\n             }\n  ,.body= \"\"\n  }\n\n#define QUERY_URL_WITH_QUESTION_MARK_GET 14\n/* Some clients include '?' characters in query strings.\n */\n, {.name = \"query url with question mark\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /test.cgi?foo=bar?baz HTTP/1.1\\r\\n\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"foo=bar?baz\"\n  ,.fragment= \"\"\n  ,.request_path= \"/test.cgi\"\n  ,.request_url= \"/test.cgi?foo=bar?baz\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body= \"\"\n  }\n\n#define PREFIX_NEWLINE_GET 15\n/* Some clients, especially after a POST in a keep-alive connection,\n * will send an extra CRLF before the next request\n */\n, {.name = \"newline prefix get\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"\\r\\nGET /test HTTP/1.1\\r\\n\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/test\"\n  ,.request_url= \"/test\"\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define UPGRADE_REQUEST 16\n, {.name = \"upgrade request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /demo HTTP/1.1\\r\\n\"\n         \"Host: example.com\\r\\n\"\n         \"Connection: Upgrade\\r\\n\"\n         \"Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\\r\\n\"\n         \"Sec-WebSocket-Protocol: sample\\r\\n\"\n         \"Upgrade: WebSocket\\r\\n\"\n         \"Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\\r\\n\"\n         \"Origin: http://example.com\\r\\n\"\n         \"\\r\\n\"\n         \"Hot diggity dogg\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/demo\"\n  ,.request_url= \"/demo\"\n  ,.num_headers= 7\n  ,.upgrade=\"Hot diggity dogg\"\n  ,.headers= { { \"Host\", \"example.com\" }\n             , { \"Connection\", \"Upgrade\" }\n             , { \"Sec-WebSocket-Key2\", \"12998 5 Y3 1  .P00\" }\n             , { \"Sec-WebSocket-Protocol\", \"sample\" }\n             , { \"Upgrade\", \"WebSocket\" }\n             , { \"Sec-WebSocket-Key1\", \"4 @1  46546xW%0l 1 5\" }\n             , { \"Origin\", \"http://example.com\" }\n             }\n  ,.body= \"\"\n  }\n\n#define CONNECT_REQUEST 17\n, {.name = \"connect request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"CONNECT 0-home0.netscape.com:443 HTTP/1.0\\r\\n\"\n         \"User-agent: Mozilla/1.1N\\r\\n\"\n         \"Proxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\"\n         \"\\r\\n\"\n         \"some data\\r\\n\"\n         \"and yet even more data\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_CONNECT\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"0-home0.netscape.com:443\"\n  ,.num_headers= 2\n  ,.upgrade=\"some data\\r\\nand yet even more data\"\n  ,.headers= { { \"User-agent\", \"Mozilla/1.1N\" }\n             , { \"Proxy-authorization\", \"basic aGVsbG86d29ybGQ=\" }\n             }\n  ,.body= \"\"\n  }\n\n#define REPORT_REQ 18\n, {.name= \"report request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"REPORT /test HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_REPORT\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/test\"\n  ,.request_url= \"/test\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body= \"\"\n  }\n\n#define NO_HTTP_VERSION 19\n, {.name= \"request with no http version\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 0\n  ,.http_minor= 9\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body= \"\"\n  }\n\n#define MSEARCH_REQ 20\n, {.name= \"m-search request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"M-SEARCH * HTTP/1.1\\r\\n\"\n         \"HOST: 239.255.255.250:1900\\r\\n\"\n         \"MAN: \\\"ssdp:discover\\\"\\r\\n\"\n         \"ST: \\\"ssdp:all\\\"\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_MSEARCH\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"*\"\n  ,.request_url= \"*\"\n  ,.num_headers= 3\n  ,.headers= { { \"HOST\", \"239.255.255.250:1900\" }\n             , { \"MAN\", \"\\\"ssdp:discover\\\"\" }\n             , { \"ST\", \"\\\"ssdp:all\\\"\" }\n             }\n  ,.body= \"\"\n  }\n\n#define LINE_FOLDING_IN_HEADER 21\n, {.name= \"line folding in header value\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET / HTTP/1.1\\r\\n\"\n         \"Line1:   abc\\r\\n\"\n         \"\\tdef\\r\\n\"\n         \" ghi\\r\\n\"\n         \"\\t\\tjkl\\r\\n\"\n         \"  mno \\r\\n\"\n         \"\\t \\tqrs\\r\\n\"\n         \"Line2: \\t line2\\t\\r\\n\"\n         \"Line3:\\r\\n\"\n         \" line3\\r\\n\"\n         \"Line4: \\r\\n\"\n         \" \\r\\n\"\n         \"Connection:\\r\\n\"\n         \" close\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 5\n  ,.headers= { { \"Line1\", \"abc\\tdef ghi\\t\\tjkl  mno \\t \\tqrs\" }\n             , { \"Line2\", \"line2\\t\" }\n             , { \"Line3\", \"line3\" }\n             , { \"Line4\", \"\" }\n             , { \"Connection\", \"close\" },\n             }\n  ,.body= \"\"\n  }\n\n\n#define QUERY_TERMINATED_HOST 22\n, {.name= \"host terminated by a query string\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET http://hypnotoad.org?hail=all HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"hail=all\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"http://hypnotoad.org?hail=all\"\n  ,.host= \"hypnotoad.org\"\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define QUERY_TERMINATED_HOSTPORT 23\n, {.name= \"host:port terminated by a query string\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET http://hypnotoad.org:1234?hail=all HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"hail=all\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"http://hypnotoad.org:1234?hail=all\"\n  ,.host= \"hypnotoad.org\"\n  ,.port= 1234\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define SPACE_TERMINATED_HOSTPORT 24\n, {.name= \"host:port terminated by a space\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET http://hypnotoad.org:1234 HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"http://hypnotoad.org:1234\"\n  ,.host= \"hypnotoad.org\"\n  ,.port= 1234\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define PATCH_REQ 25\n, {.name = \"PATCH request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"PATCH /file.txt HTTP/1.1\\r\\n\"\n         \"Host: www.example.com\\r\\n\"\n         \"Content-Type: application/example\\r\\n\"\n         \"If-Match: \\\"e0023aa4e\\\"\\r\\n\"\n         \"Content-Length: 10\\r\\n\"\n         \"\\r\\n\"\n         \"cccccccccc\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_PATCH\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/file.txt\"\n  ,.request_url= \"/file.txt\"\n  ,.num_headers= 4\n  ,.headers= { { \"Host\", \"www.example.com\" }\n             , { \"Content-Type\", \"application/example\" }\n             , { \"If-Match\", \"\\\"e0023aa4e\\\"\" }\n             , { \"Content-Length\", \"10\" }\n             }\n  ,.body= \"cccccccccc\"\n  }\n\n#define CONNECT_CAPS_REQUEST 26\n, {.name = \"connect caps request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\\r\\n\"\n         \"User-agent: Mozilla/1.1N\\r\\n\"\n         \"Proxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_CONNECT\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"HOME0.NETSCAPE.COM:443\"\n  ,.num_headers= 2\n  ,.upgrade=\"\"\n  ,.headers= { { \"User-agent\", \"Mozilla/1.1N\" }\n             , { \"Proxy-authorization\", \"basic aGVsbG86d29ybGQ=\" }\n             }\n  ,.body= \"\"\n  }\n\n#if !HTTP_PARSER_STRICT\n#define UTF8_PATH_REQ 27\n, {.name= \"utf-8 path request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /δ¶/δt/pope?q=1#narf HTTP/1.1\\r\\n\"\n         \"Host: github.com\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"q=1\"\n  ,.fragment= \"narf\"\n  ,.request_path= \"/δ¶/δt/pope\"\n  ,.request_url= \"/δ¶/δt/pope?q=1#narf\"\n  ,.num_headers= 1\n  ,.headers= { {\"Host\", \"github.com\" }\n             }\n  ,.body= \"\"\n  }\n\n#define HOSTNAME_UNDERSCORE 28\n, {.name = \"hostname underscore\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"CONNECT home_0.netscape.com:443 HTTP/1.0\\r\\n\"\n         \"User-agent: Mozilla/1.1N\\r\\n\"\n         \"Proxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_CONNECT\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"\"\n  ,.request_url= \"home_0.netscape.com:443\"\n  ,.num_headers= 2\n  ,.upgrade=\"\"\n  ,.headers= { { \"User-agent\", \"Mozilla/1.1N\" }\n             , { \"Proxy-authorization\", \"basic aGVsbG86d29ybGQ=\" }\n             }\n  ,.body= \"\"\n  }\n#endif  /* !HTTP_PARSER_STRICT */\n\n/* see https://github.com/ry/http-parser/issues/47 */\n#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29\n, {.name = \"eat CRLF between requests, no \\\"Connection: close\\\" header\"\n  ,.raw= \"POST / HTTP/1.1\\r\\n\"\n         \"Host: www.example.com\\r\\n\"\n         \"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n         \"Content-Length: 4\\r\\n\"\n         \"\\r\\n\"\n         \"q=42\\r\\n\" /* note the trailing CRLF */\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 3\n  ,.upgrade= 0\n  ,.headers= { { \"Host\", \"www.example.com\" }\n             , { \"Content-Type\", \"application/x-www-form-urlencoded\" }\n             , { \"Content-Length\", \"4\" }\n             }\n  ,.body= \"q=42\"\n  }\n\n/* see https://github.com/ry/http-parser/issues/47 */\n#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30\n, {.name = \"eat CRLF between requests even if \\\"Connection: close\\\" is set\"\n  ,.raw= \"POST / HTTP/1.1\\r\\n\"\n         \"Host: www.example.com\\r\\n\"\n         \"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n         \"Content-Length: 4\\r\\n\"\n         \"Connection: close\\r\\n\"\n         \"\\r\\n\"\n         \"q=42\\r\\n\" /* note the trailing CRLF */\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 4\n  ,.upgrade= 0\n  ,.headers= { { \"Host\", \"www.example.com\" }\n             , { \"Content-Type\", \"application/x-www-form-urlencoded\" }\n             , { \"Content-Length\", \"4\" }\n             , { \"Connection\", \"close\" }\n             }\n  ,.body= \"q=42\"\n  }\n\n#define PURGE_REQ 31\n, {.name = \"PURGE request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"PURGE /file.txt HTTP/1.1\\r\\n\"\n         \"Host: www.example.com\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_PURGE\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/file.txt\"\n  ,.request_url= \"/file.txt\"\n  ,.num_headers= 1\n  ,.headers= { { \"Host\", \"www.example.com\" } }\n  ,.body= \"\"\n  }\n\n#define SEARCH_REQ 32\n, {.name = \"SEARCH request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"SEARCH / HTTP/1.1\\r\\n\"\n         \"Host: www.example.com\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_SEARCH\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 1\n  ,.headers= { { \"Host\", \"www.example.com\" } }\n  ,.body= \"\"\n  }\n\n#define PROXY_WITH_BASIC_AUTH 33\n, {.name= \"host:port and basic_auth\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.fragment= \"\"\n  ,.request_path= \"/toto\"\n  ,.request_url= \"http://a%12:b!&*$@hypnotoad.org:1234/toto\"\n  ,.host= \"hypnotoad.org\"\n  ,.userinfo= \"a%12:b!&*$\"\n  ,.port= 1234\n  ,.num_headers= 0\n  ,.headers= { }\n  ,.body= \"\"\n  }\n\n#define LINE_FOLDING_IN_HEADER_WITH_LF 34\n, {.name= \"line folding in header value\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET / HTTP/1.1\\n\"\n         \"Line1:   abc\\n\"\n         \"\\tdef\\n\"\n         \" ghi\\n\"\n         \"\\t\\tjkl\\n\"\n         \"  mno \\n\"\n         \"\\t \\tqrs\\n\"\n         \"Line2: \\t line2\\t\\n\"\n         \"Line3:\\n\"\n         \" line3\\n\"\n         \"Line4: \\n\"\n         \" \\n\"\n         \"Connection:\\n\"\n         \" close\\n\"\n         \"\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/\"\n  ,.request_url= \"/\"\n  ,.num_headers= 5\n  ,.headers= { { \"Line1\", \"abc\\tdef ghi\\t\\tjkl  mno \\t \\tqrs\" }\n             , { \"Line2\", \"line2\\t\" }\n             , { \"Line3\", \"line3\" }\n             , { \"Line4\", \"\" }\n             , { \"Connection\", \"close\" },\n             }\n  ,.body= \"\"\n  }\n\n#define CONNECTION_MULTI 35\n, {.name = \"multiple connection header values with folding\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /demo HTTP/1.1\\r\\n\"\n         \"Host: example.com\\r\\n\"\n         \"Connection: Something,\\r\\n\"\n         \" Upgrade, ,Keep-Alive\\r\\n\"\n         \"Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\\r\\n\"\n         \"Sec-WebSocket-Protocol: sample\\r\\n\"\n         \"Upgrade: WebSocket\\r\\n\"\n         \"Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\\r\\n\"\n         \"Origin: http://example.com\\r\\n\"\n         \"\\r\\n\"\n         \"Hot diggity dogg\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/demo\"\n  ,.request_url= \"/demo\"\n  ,.num_headers= 7\n  ,.upgrade=\"Hot diggity dogg\"\n  ,.headers= { { \"Host\", \"example.com\" }\n             , { \"Connection\", \"Something, Upgrade, ,Keep-Alive\" }\n             , { \"Sec-WebSocket-Key2\", \"12998 5 Y3 1  .P00\" }\n             , { \"Sec-WebSocket-Protocol\", \"sample\" }\n             , { \"Upgrade\", \"WebSocket\" }\n             , { \"Sec-WebSocket-Key1\", \"4 @1  46546xW%0l 1 5\" }\n             , { \"Origin\", \"http://example.com\" }\n             }\n  ,.body= \"\"\n  }\n\n#define CONNECTION_MULTI_LWS 36\n, {.name = \"multiple connection header values with folding and lws\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /demo HTTP/1.1\\r\\n\"\n         \"Connection: keep-alive, upgrade\\r\\n\"\n         \"Upgrade: WebSocket\\r\\n\"\n         \"\\r\\n\"\n         \"Hot diggity dogg\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/demo\"\n  ,.request_url= \"/demo\"\n  ,.num_headers= 2\n  ,.upgrade=\"Hot diggity dogg\"\n  ,.headers= { { \"Connection\", \"keep-alive, upgrade\" }\n             , { \"Upgrade\", \"WebSocket\" }\n             }\n  ,.body= \"\"\n  }\n\n#define CONNECTION_MULTI_LWS_CRLF 37\n, {.name = \"multiple connection header values with folding and lws\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"GET /demo HTTP/1.1\\r\\n\"\n         \"Connection: keep-alive, \\r\\n upgrade\\r\\n\"\n         \"Upgrade: WebSocket\\r\\n\"\n         \"\\r\\n\"\n         \"Hot diggity dogg\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_GET\n  ,.query_string= \"\"\n  ,.fragment= \"\"\n  ,.request_path= \"/demo\"\n  ,.request_url= \"/demo\"\n  ,.num_headers= 2\n  ,.upgrade=\"Hot diggity dogg\"\n  ,.headers= { { \"Connection\", \"keep-alive,  upgrade\" }\n             , { \"Upgrade\", \"WebSocket\" }\n             }\n  ,.body= \"\"\n  }\n\n#define UPGRADE_POST_REQUEST 38\n, {.name = \"upgrade post request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"POST /demo HTTP/1.1\\r\\n\"\n         \"Host: example.com\\r\\n\"\n         \"Connection: Upgrade\\r\\n\"\n         \"Upgrade: HTTP/2.0\\r\\n\"\n         \"Content-Length: 15\\r\\n\"\n         \"\\r\\n\"\n         \"sweet post body\"\n         \"Hot diggity dogg\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.method= HTTP_POST\n  ,.request_path= \"/demo\"\n  ,.request_url= \"/demo\"\n  ,.num_headers= 4\n  ,.upgrade=\"Hot diggity dogg\"\n  ,.headers= { { \"Host\", \"example.com\" }\n             , { \"Connection\", \"Upgrade\" }\n             , { \"Upgrade\", \"HTTP/2.0\" }\n             , { \"Content-Length\", \"15\" }\n             }\n  ,.body= \"sweet post body\"\n  }\n\n#define CONNECT_WITH_BODY_REQUEST 39\n, {.name = \"connect with body request\"\n  ,.type= HTTP_REQUEST\n  ,.raw= \"CONNECT foo.bar.com:443 HTTP/1.0\\r\\n\"\n         \"User-agent: Mozilla/1.1N\\r\\n\"\n         \"Proxy-authorization: basic aGVsbG86d29ybGQ=\\r\\n\"\n         \"Content-Length: 10\\r\\n\"\n         \"\\r\\n\"\n         \"blarfcicle\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.method= HTTP_CONNECT\n  ,.request_url= \"foo.bar.com:443\"\n  ,.num_headers= 3\n  ,.upgrade=\"blarfcicle\"\n  ,.headers= { { \"User-agent\", \"Mozilla/1.1N\" }\n             , { \"Proxy-authorization\", \"basic aGVsbG86d29ybGQ=\" }\n             , { \"Content-Length\", \"10\" }\n             }\n  ,.body= \"\"\n  }\n\n, {.name= NULL } /* sentinel */\n};\n\n/* * R E S P O N S E S * */\nconst struct message responses[] =\n#define GOOGLE_301 0\n{ {.name= \"google 301\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 301 Moved Permanently\\r\\n\"\n         \"Location: http://www.google.com/\\r\\n\"\n         \"Content-Type: text/html; charset=UTF-8\\r\\n\"\n         \"Date: Sun, 26 Apr 2009 11:11:49 GMT\\r\\n\"\n         \"Expires: Tue, 26 May 2009 11:11:49 GMT\\r\\n\"\n         \"X-$PrototypeBI-Version: 1.6.0.3\\r\\n\" /* $ char in header field */\n         \"Cache-Control: public, max-age=2592000\\r\\n\"\n         \"Server: gws\\r\\n\"\n         \"Content-Length:  219  \\r\\n\"\n         \"\\r\\n\"\n         \"<HTML><HEAD><meta http-equiv=\\\"content-type\\\" content=\\\"text/html;charset=utf-8\\\">\\n\"\n         \"<TITLE>301 Moved</TITLE></HEAD><BODY>\\n\"\n         \"<H1>301 Moved</H1>\\n\"\n         \"The document has moved\\n\"\n         \"<A HREF=\\\"http://www.google.com/\\\">here</A>.\\r\\n\"\n         \"</BODY></HTML>\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 301\n  ,.response_status= \"Moved Permanently\"\n  ,.num_headers= 8\n  ,.headers=\n    { { \"Location\", \"http://www.google.com/\" }\n    , { \"Content-Type\", \"text/html; charset=UTF-8\" }\n    , { \"Date\", \"Sun, 26 Apr 2009 11:11:49 GMT\" }\n    , { \"Expires\", \"Tue, 26 May 2009 11:11:49 GMT\" }\n    , { \"X-$PrototypeBI-Version\", \"1.6.0.3\" }\n    , { \"Cache-Control\", \"public, max-age=2592000\" }\n    , { \"Server\", \"gws\" }\n    , { \"Content-Length\", \"219  \" }\n    }\n  ,.body= \"<HTML><HEAD><meta http-equiv=\\\"content-type\\\" content=\\\"text/html;charset=utf-8\\\">\\n\"\n          \"<TITLE>301 Moved</TITLE></HEAD><BODY>\\n\"\n          \"<H1>301 Moved</H1>\\n\"\n          \"The document has moved\\n\"\n          \"<A HREF=\\\"http://www.google.com/\\\">here</A>.\\r\\n\"\n          \"</BODY></HTML>\\r\\n\"\n  }\n\n#define NO_CONTENT_LENGTH_RESPONSE 1\n/* The client should wait for the server's EOF. That is, when content-length\n * is not specified, and \"Connection: close\", the end of body is specified\n * by the EOF.\n * Compare with APACHEBENCH_GET\n */\n, {.name= \"no content-length response\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Date: Tue, 04 Aug 2009 07:59:32 GMT\\r\\n\"\n         \"Server: Apache\\r\\n\"\n         \"X-Powered-By: Servlet/2.5 JSP/2.1\\r\\n\"\n         \"Content-Type: text/xml; charset=utf-8\\r\\n\"\n         \"Connection: close\\r\\n\"\n         \"\\r\\n\"\n         \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\"\n         \"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\">\\n\"\n         \"  <SOAP-ENV:Body>\\n\"\n         \"    <SOAP-ENV:Fault>\\n\"\n         \"       <faultcode>SOAP-ENV:Client</faultcode>\\n\"\n         \"       <faultstring>Client Error</faultstring>\\n\"\n         \"    </SOAP-ENV:Fault>\\n\"\n         \"  </SOAP-ENV:Body>\\n\"\n         \"</SOAP-ENV:Envelope>\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 5\n  ,.headers=\n    { { \"Date\", \"Tue, 04 Aug 2009 07:59:32 GMT\" }\n    , { \"Server\", \"Apache\" }\n    , { \"X-Powered-By\", \"Servlet/2.5 JSP/2.1\" }\n    , { \"Content-Type\", \"text/xml; charset=utf-8\" }\n    , { \"Connection\", \"close\" }\n    }\n  ,.body= \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\"\n          \"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\">\\n\"\n          \"  <SOAP-ENV:Body>\\n\"\n          \"    <SOAP-ENV:Fault>\\n\"\n          \"       <faultcode>SOAP-ENV:Client</faultcode>\\n\"\n          \"       <faultstring>Client Error</faultstring>\\n\"\n          \"    </SOAP-ENV:Fault>\\n\"\n          \"  </SOAP-ENV:Body>\\n\"\n          \"</SOAP-ENV:Envelope>\"\n  }\n\n#define NO_HEADERS_NO_BODY_404 2\n, {.name= \"404 no headers no body\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 404 Not Found\\r\\n\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 404\n  ,.response_status= \"Not Found\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_REASON_PHRASE 3\n, {.name= \"301 no response phrase\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 301\\r\\n\\r\\n\"\n  ,.should_keep_alive = FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 301\n  ,.response_status= \"\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body= \"\"\n  }\n\n#define TRAILING_SPACE_ON_CHUNKED_BODY 4\n, {.name=\"200 trailing space on chunked body\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Content-Type: text/plain\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"25  \\r\\n\"\n         \"This is the data in the first chunk\\r\\n\"\n         \"\\r\\n\"\n         \"1C\\r\\n\"\n         \"and this is the second one\\r\\n\"\n         \"\\r\\n\"\n         \"0  \\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 2\n  ,.headers=\n    { {\"Content-Type\", \"text/plain\" }\n    , {\"Transfer-Encoding\", \"chunked\" }\n    }\n  ,.body_size = 37+28\n  ,.body =\n         \"This is the data in the first chunk\\r\\n\"\n         \"and this is the second one\\r\\n\"\n  ,.num_chunks_complete= 3\n  ,.chunk_lengths= { 0x25, 0x1c }\n  }\n\n#define NO_CARRIAGE_RET 5\n, {.name=\"no carriage ret\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\n\"\n         \"Content-Type: text/html; charset=utf-8\\n\"\n         \"Connection: close\\n\"\n         \"\\n\"\n         \"these headers are from http://news.ycombinator.com/\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 2\n  ,.headers=\n    { {\"Content-Type\", \"text/html; charset=utf-8\" }\n    , {\"Connection\", \"close\" }\n    }\n  ,.body= \"these headers are from http://news.ycombinator.com/\"\n  }\n\n#define PROXY_CONNECTION 6\n, {.name=\"proxy connection\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Content-Type: text/html; charset=UTF-8\\r\\n\"\n         \"Content-Length: 11\\r\\n\"\n         \"Proxy-Connection: close\\r\\n\"\n         \"Date: Thu, 31 Dec 2009 20:55:48 +0000\\r\\n\"\n         \"\\r\\n\"\n         \"hello world\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 4\n  ,.headers=\n    { {\"Content-Type\", \"text/html; charset=UTF-8\" }\n    , {\"Content-Length\", \"11\" }\n    , {\"Proxy-Connection\", \"close\" }\n    , {\"Date\", \"Thu, 31 Dec 2009 20:55:48 +0000\"}\n    }\n  ,.body= \"hello world\"\n  }\n\n#define UNDERSTORE_HEADER_KEY 7\n  // shown by\n  // curl -o /dev/null -v \"http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;\"\n, {.name=\"underscore header key\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Server: DCLK-AdSvr\\r\\n\"\n         \"Content-Type: text/xml\\r\\n\"\n         \"Content-Length: 0\\r\\n\"\n         \"DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\\r\\n\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 4\n  ,.headers=\n    { {\"Server\", \"DCLK-AdSvr\" }\n    , {\"Content-Type\", \"text/xml\" }\n    , {\"Content-Length\", \"0\" }\n    , {\"DCLK_imp\", \"v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\" }\n    }\n  ,.body= \"\"\n  }\n\n#define BONJOUR_MADAME_FR 8\n/* The client should not merge two headers fields when the first one doesn't\n * have a value.\n */\n, {.name= \"bonjourmadame.fr\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.0 301 Moved Permanently\\r\\n\"\n         \"Date: Thu, 03 Jun 2010 09:56:32 GMT\\r\\n\"\n         \"Server: Apache/2.2.3 (Red Hat)\\r\\n\"\n         \"Cache-Control: public\\r\\n\"\n         \"Pragma: \\r\\n\"\n         \"Location: http://www.bonjourmadame.fr/\\r\\n\"\n         \"Vary: Accept-Encoding\\r\\n\"\n         \"Content-Length: 0\\r\\n\"\n         \"Content-Type: text/html; charset=UTF-8\\r\\n\"\n         \"Connection: keep-alive\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.status_code= 301\n  ,.response_status= \"Moved Permanently\"\n  ,.num_headers= 9\n  ,.headers=\n    { { \"Date\", \"Thu, 03 Jun 2010 09:56:32 GMT\" }\n    , { \"Server\", \"Apache/2.2.3 (Red Hat)\" }\n    , { \"Cache-Control\", \"public\" }\n    , { \"Pragma\", \"\" }\n    , { \"Location\", \"http://www.bonjourmadame.fr/\" }\n    , { \"Vary\",  \"Accept-Encoding\" }\n    , { \"Content-Length\", \"0\" }\n    , { \"Content-Type\", \"text/html; charset=UTF-8\" }\n    , { \"Connection\", \"keep-alive\" }\n    }\n  ,.body= \"\"\n  }\n\n#define RES_FIELD_UNDERSCORE 9\n/* Should handle spaces in header fields */\n, {.name= \"field underscore\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Date: Tue, 28 Sep 2010 01:14:13 GMT\\r\\n\"\n         \"Server: Apache\\r\\n\"\n         \"Cache-Control: no-cache, must-revalidate\\r\\n\"\n         \"Expires: Mon, 26 Jul 1997 05:00:00 GMT\\r\\n\"\n         \".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\\r\\n\"\n         \"Vary: Accept-Encoding\\r\\n\"\n         \"_eep-Alive: timeout=45\\r\\n\" /* semantic value ignored */\n         \"_onnection: Keep-Alive\\r\\n\" /* semantic value ignored */\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"Content-Type: text/html\\r\\n\"\n         \"Connection: close\\r\\n\"\n         \"\\r\\n\"\n         \"0\\r\\n\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 11\n  ,.headers=\n    { { \"Date\", \"Tue, 28 Sep 2010 01:14:13 GMT\" }\n    , { \"Server\", \"Apache\" }\n    , { \"Cache-Control\", \"no-cache, must-revalidate\" }\n    , { \"Expires\", \"Mon, 26 Jul 1997 05:00:00 GMT\" }\n    , { \".et-Cookie\", \"PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\" }\n    , { \"Vary\", \"Accept-Encoding\" }\n    , { \"_eep-Alive\", \"timeout=45\" }\n    , { \"_onnection\", \"Keep-Alive\" }\n    , { \"Transfer-Encoding\", \"chunked\" }\n    , { \"Content-Type\", \"text/html\" }\n    , { \"Connection\", \"close\" }\n    }\n  ,.body= \"\"\n  ,.num_chunks_complete= 1\n  ,.chunk_lengths= {}\n  }\n\n#define NON_ASCII_IN_STATUS_LINE 10\n/* Should handle non-ASCII in status line */\n, {.name= \"non-ASCII in status line\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 500 Oriëntatieprobleem\\r\\n\"\n         \"Date: Fri, 5 Nov 2010 23:07:12 GMT+2\\r\\n\"\n         \"Content-Length: 0\\r\\n\"\n         \"Connection: close\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 500\n  ,.response_status= \"Oriëntatieprobleem\"\n  ,.num_headers= 3\n  ,.headers=\n    { { \"Date\", \"Fri, 5 Nov 2010 23:07:12 GMT+2\" }\n    , { \"Content-Length\", \"0\" }\n    , { \"Connection\", \"close\" }\n    }\n  ,.body= \"\"\n  }\n\n#define HTTP_VERSION_0_9 11\n/* Should handle HTTP/0.9 */\n, {.name= \"http version 0.9\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/0.9 200 OK\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 0\n  ,.http_minor= 9\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 0\n  ,.headers=\n    {}\n  ,.body= \"\"\n  }\n\n#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12\n/* The client should wait for the server's EOF. That is, when neither\n * content-length nor transfer-encoding is specified, the end of body\n * is specified by the EOF.\n */\n, {.name= \"neither content-length nor transfer-encoding response\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Content-Type: text/plain\\r\\n\"\n         \"\\r\\n\"\n         \"hello world\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Content-Type\", \"text/plain\" }\n    }\n  ,.body= \"hello world\"\n  }\n\n#define NO_BODY_HTTP10_KA_200 13\n, {.name= \"HTTP/1.0 with keep-alive and EOF-terminated 200 status\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.0 200 OK\\r\\n\"\n         \"Connection: keep-alive\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Connection\", \"keep-alive\" }\n    }\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_BODY_HTTP10_KA_204 14\n, {.name= \"HTTP/1.0 with keep-alive and a 204 status\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.0 204 No content\\r\\n\"\n         \"Connection: keep-alive\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 0\n  ,.status_code= 204\n  ,.response_status= \"No content\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Connection\", \"keep-alive\" }\n    }\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_BODY_HTTP11_KA_200 15\n, {.name= \"HTTP/1.1 with an EOF-terminated 200 status\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 0\n  ,.headers={}\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_BODY_HTTP11_KA_204 16\n, {.name= \"HTTP/1.1 with a 204 status\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 204 No content\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 204\n  ,.response_status= \"No content\"\n  ,.num_headers= 0\n  ,.headers={}\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_BODY_HTTP11_NOKA_204 17\n, {.name= \"HTTP/1.1 with a 204 status and keep-alive disabled\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 204 No content\\r\\n\"\n         \"Connection: close\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 204\n  ,.response_status= \"No content\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Connection\", \"close\" }\n    }\n  ,.body_size= 0\n  ,.body= \"\"\n  }\n\n#define NO_BODY_HTTP11_KA_CHUNKED_200 18\n, {.name= \"HTTP/1.1 with chunked endocing and a 200 response\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"0\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 1\n  ,.headers=\n    { { \"Transfer-Encoding\", \"chunked\" }\n    }\n  ,.body_size= 0\n  ,.body= \"\"\n  ,.num_chunks_complete= 1\n  }\n\n#if !HTTP_PARSER_STRICT\n#define SPACE_IN_FIELD_RES 19\n/* Should handle spaces in header fields */\n, {.name= \"field space\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 OK\\r\\n\"\n         \"Server: Microsoft-IIS/6.0\\r\\n\"\n         \"X-Powered-By: ASP.NET\\r\\n\"\n         \"en-US Content-Type: text/xml\\r\\n\" /* this is the problem */\n         \"Content-Type: text/xml\\r\\n\"\n         \"Content-Length: 16\\r\\n\"\n         \"Date: Fri, 23 Jul 2010 18:45:38 GMT\\r\\n\"\n         \"Connection: keep-alive\\r\\n\"\n         \"\\r\\n\"\n         \"<xml>hello</xml>\" /* fake body */\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"OK\"\n  ,.num_headers= 7\n  ,.headers=\n    { { \"Server\",  \"Microsoft-IIS/6.0\" }\n    , { \"X-Powered-By\", \"ASP.NET\" }\n    , { \"en-US Content-Type\", \"text/xml\" }\n    , { \"Content-Type\", \"text/xml\" }\n    , { \"Content-Length\", \"16\" }\n    , { \"Date\", \"Fri, 23 Jul 2010 18:45:38 GMT\" }\n    , { \"Connection\", \"keep-alive\" }\n    }\n  ,.body= \"<xml>hello</xml>\"\n  }\n#endif /* !HTTP_PARSER_STRICT */\n\n#define AMAZON_COM 20\n, {.name= \"amazon.com\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 301 MovedPermanently\\r\\n\"\n         \"Date: Wed, 15 May 2013 17:06:33 GMT\\r\\n\"\n         \"Server: Server\\r\\n\"\n         \"x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\\r\\n\"\n         \"p3p: policyref=\\\"http://www.amazon.com/w3c/p3p.xml\\\",CP=\\\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \\\"\\r\\n\"\n         \"x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\\r\\n\"\n         \"Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\\r\\n\"\n         \"Vary: Accept-Encoding,User-Agent\\r\\n\"\n         \"Content-Type: text/html; charset=ISO-8859-1\\r\\n\"\n         \"Transfer-Encoding: chunked\\r\\n\"\n         \"\\r\\n\"\n         \"1\\r\\n\"\n         \"\\n\\r\\n\"\n         \"0\\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= TRUE\n  ,.message_complete_on_eof= FALSE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 301\n  ,.response_status= \"MovedPermanently\"\n  ,.num_headers= 9\n  ,.headers= { { \"Date\", \"Wed, 15 May 2013 17:06:33 GMT\" }\n             , { \"Server\", \"Server\" }\n             , { \"x-amz-id-1\", \"0GPHKXSJQ826RK7GZEB2\" }\n             , { \"p3p\", \"policyref=\\\"http://www.amazon.com/w3c/p3p.xml\\\",CP=\\\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \\\"\" }\n             , { \"x-amz-id-2\", \"STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\" }\n             , { \"Location\", \"http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\" }\n             , { \"Vary\", \"Accept-Encoding,User-Agent\" }\n             , { \"Content-Type\", \"text/html; charset=ISO-8859-1\" }\n             , { \"Transfer-Encoding\", \"chunked\" }\n             }\n  ,.body= \"\\n\"\n  ,.num_chunks_complete= 2\n  ,.chunk_lengths= { 1 }\n  }\n\n#define EMPTY_REASON_PHRASE_AFTER_SPACE 20\n, {.name= \"empty reason phrase after space\"\n  ,.type= HTTP_RESPONSE\n  ,.raw= \"HTTP/1.1 200 \\r\\n\"\n         \"\\r\\n\"\n  ,.should_keep_alive= FALSE\n  ,.message_complete_on_eof= TRUE\n  ,.http_major= 1\n  ,.http_minor= 1\n  ,.status_code= 200\n  ,.response_status= \"\"\n  ,.num_headers= 0\n  ,.headers= {}\n  ,.body= \"\"\n  }\n\n, {.name= NULL } /* sentinel */\n};\n\n/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so\n * define it ourselves.\n */\nsize_t\nstrnlen(const char *s, size_t maxlen)\n{\n  const char *p;\n\n  p = memchr(s, '\\0', maxlen);\n  if (p == NULL)\n    return maxlen;\n\n  return p - s;\n}\n\nsize_t\nstrlncat(char *dst, size_t len, const char *src, size_t n)\n{\n  size_t slen;\n  size_t dlen;\n  size_t rlen;\n  size_t ncpy;\n\n  slen = strnlen(src, n);\n  dlen = strnlen(dst, len);\n\n  if (dlen < len) {\n    rlen = len - dlen;\n    ncpy = slen < rlen ? slen : (rlen - 1);\n    memcpy(dst + dlen, src, ncpy);\n    dst[dlen + ncpy] = '\\0';\n  }\n\n  assert(len > slen + dlen);\n  return slen + dlen;\n}\n\nsize_t\nstrlcat(char *dst, const char *src, size_t len)\n{\n  return strlncat(dst, len, src, (size_t) -1);\n}\n\nsize_t\nstrlncpy(char *dst, size_t len, const char *src, size_t n)\n{\n  size_t slen;\n  size_t ncpy;\n\n  slen = strnlen(src, n);\n\n  if (len > 0) {\n    ncpy = slen < len ? slen : (len - 1);\n    memcpy(dst, src, ncpy);\n    dst[ncpy] = '\\0';\n  }\n\n  assert(len > slen);\n  return slen;\n}\n\nsize_t\nstrlcpy(char *dst, const char *src, size_t len)\n{\n  return strlncpy(dst, len, src, (size_t) -1);\n}\n\nint\nrequest_url_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  strlncat(messages[num_messages].request_url,\n           sizeof(messages[num_messages].request_url),\n           buf,\n           len);\n  return 0;\n}\n\nint\nheader_field_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  struct message *m = &messages[num_messages];\n\n  if (m->last_header_element != FIELD)\n    m->num_headers++;\n\n  strlncat(m->headers[m->num_headers-1][0],\n           sizeof(m->headers[m->num_headers-1][0]),\n           buf,\n           len);\n\n  m->last_header_element = FIELD;\n\n  return 0;\n}\n\nint\nheader_value_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  struct message *m = &messages[num_messages];\n\n  strlncat(m->headers[m->num_headers-1][1],\n           sizeof(m->headers[m->num_headers-1][1]),\n           buf,\n           len);\n\n  m->last_header_element = VALUE;\n\n  return 0;\n}\n\nvoid\ncheck_body_is_final (const http_parser *p)\n{\n  if (messages[num_messages].body_is_final) {\n    fprintf(stderr, \"\\n\\n *** Error http_body_is_final() should return 1 \"\n                    \"on last on_body callback call \"\n                    \"but it doesn't! ***\\n\\n\");\n    assert(0);\n    abort();\n  }\n  messages[num_messages].body_is_final = http_body_is_final(p);\n}\n\nint\nbody_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  strlncat(messages[num_messages].body,\n           sizeof(messages[num_messages].body),\n           buf,\n           len);\n  messages[num_messages].body_size += len;\n  check_body_is_final(p);\n // printf(\"body_cb: '%s'\\n\", requests[num_messages].body);\n  return 0;\n}\n\nint\ncount_body_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  assert(buf);\n  messages[num_messages].body_size += len;\n  check_body_is_final(p);\n  return 0;\n}\n\nint\nmessage_begin_cb (http_parser *p)\n{\n  assert(p == parser);\n  messages[num_messages].message_begin_cb_called = TRUE;\n  return 0;\n}\n\nint\nheaders_complete_cb (http_parser *p)\n{\n  assert(p == parser);\n  messages[num_messages].method = parser->method;\n  messages[num_messages].status_code = parser->status_code;\n  messages[num_messages].http_major = parser->http_major;\n  messages[num_messages].http_minor = parser->http_minor;\n  messages[num_messages].headers_complete_cb_called = TRUE;\n  messages[num_messages].should_keep_alive = http_should_keep_alive(parser);\n  return 0;\n}\n\nint\nmessage_complete_cb (http_parser *p)\n{\n  assert(p == parser);\n  if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))\n  {\n    fprintf(stderr, \"\\n\\n *** Error http_should_keep_alive() should have same \"\n                    \"value in both on_message_complete and on_headers_complete \"\n                    \"but it doesn't! ***\\n\\n\");\n    assert(0);\n    abort();\n  }\n\n  if (messages[num_messages].body_size &&\n      http_body_is_final(p) &&\n      !messages[num_messages].body_is_final)\n  {\n    fprintf(stderr, \"\\n\\n *** Error http_body_is_final() should return 1 \"\n                    \"on last on_body callback call \"\n                    \"but it doesn't! ***\\n\\n\");\n    assert(0);\n    abort();\n  }\n\n  messages[num_messages].message_complete_cb_called = TRUE;\n\n  messages[num_messages].message_complete_on_eof = currently_parsing_eof;\n\n  num_messages++;\n  return 0;\n}\n\nint\nresponse_status_cb (http_parser *p, const char *buf, size_t len)\n{\n  assert(p == parser);\n  strlncat(messages[num_messages].response_status,\n           sizeof(messages[num_messages].response_status),\n           buf,\n           len);\n  return 0;\n}\n\nint\nchunk_header_cb (http_parser *p)\n{\n  assert(p == parser);\n  int chunk_idx = messages[num_messages].num_chunks;\n  messages[num_messages].num_chunks++;\n  if (chunk_idx < MAX_CHUNKS) {\n    messages[num_messages].chunk_lengths[chunk_idx] = p->content_length;\n  }\n\n  return 0;\n}\n\nint\nchunk_complete_cb (http_parser *p)\n{\n  assert(p == parser);\n\n  /* Here we want to verify that each chunk_header_cb is matched by a\n   * chunk_complete_cb, so not only should the total number of calls to\n   * both callbacks be the same, but they also should be interleaved\n   * properly */\n  assert(messages[num_messages].num_chunks ==\n         messages[num_messages].num_chunks_complete + 1);\n\n  messages[num_messages].num_chunks_complete++;\n  return 0;\n}\n\n/* These dontcall_* callbacks exist so that we can verify that when we're\n * paused, no additional callbacks are invoked */\nint\ndontcall_message_begin_cb (http_parser *p)\n{\n  if (p) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_message_begin() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_header_field_cb (http_parser *p, const char *buf, size_t len)\n{\n  if (p || buf || len) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_header_field() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_header_value_cb (http_parser *p, const char *buf, size_t len)\n{\n  if (p || buf || len) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_header_value() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_request_url_cb (http_parser *p, const char *buf, size_t len)\n{\n  if (p || buf || len) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_request_url() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_body_cb (http_parser *p, const char *buf, size_t len)\n{\n  if (p || buf || len) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_body_cb() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_headers_complete_cb (http_parser *p)\n{\n  if (p) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_headers_complete() called on paused \"\n                  \"parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_message_complete_cb (http_parser *p)\n{\n  if (p) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_message_complete() called on paused \"\n                  \"parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_response_status_cb (http_parser *p, const char *buf, size_t len)\n{\n  if (p || buf || len) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_status() called on paused parser ***\\n\\n\");\n  abort();\n}\n\nint\ndontcall_chunk_header_cb (http_parser *p)\n{\n  if (p) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_chunk_header() called on paused parser ***\\n\\n\");\n  exit(1);\n}\n\nint\ndontcall_chunk_complete_cb (http_parser *p)\n{\n  if (p) { } // gcc\n  fprintf(stderr, \"\\n\\n*** on_chunk_complete() \"\n          \"called on paused parser ***\\n\\n\");\n  exit(1);\n}\n\nstatic http_parser_settings settings_dontcall =\n  {.on_message_begin = dontcall_message_begin_cb\n  ,.on_header_field = dontcall_header_field_cb\n  ,.on_header_value = dontcall_header_value_cb\n  ,.on_url = dontcall_request_url_cb\n  ,.on_status = dontcall_response_status_cb\n  ,.on_body = dontcall_body_cb\n  ,.on_headers_complete = dontcall_headers_complete_cb\n  ,.on_message_complete = dontcall_message_complete_cb\n  ,.on_chunk_header = dontcall_chunk_header_cb\n  ,.on_chunk_complete = dontcall_chunk_complete_cb\n  };\n\n/* These pause_* callbacks always pause the parser and just invoke the regular\n * callback that tracks content. Before returning, we overwrite the parser\n * settings to point to the _dontcall variety so that we can verify that\n * the pause actually did, you know, pause. */\nint\npause_message_begin_cb (http_parser *p)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return message_begin_cb(p);\n}\n\nint\npause_header_field_cb (http_parser *p, const char *buf, size_t len)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return header_field_cb(p, buf, len);\n}\n\nint\npause_header_value_cb (http_parser *p, const char *buf, size_t len)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return header_value_cb(p, buf, len);\n}\n\nint\npause_request_url_cb (http_parser *p, const char *buf, size_t len)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return request_url_cb(p, buf, len);\n}\n\nint\npause_body_cb (http_parser *p, const char *buf, size_t len)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return body_cb(p, buf, len);\n}\n\nint\npause_headers_complete_cb (http_parser *p)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return headers_complete_cb(p);\n}\n\nint\npause_message_complete_cb (http_parser *p)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return message_complete_cb(p);\n}\n\nint\npause_response_status_cb (http_parser *p, const char *buf, size_t len)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return response_status_cb(p, buf, len);\n}\n\nint\npause_chunk_header_cb (http_parser *p)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return chunk_header_cb(p);\n}\n\nint\npause_chunk_complete_cb (http_parser *p)\n{\n  http_parser_pause(p, 1);\n  *current_pause_parser = settings_dontcall;\n  return chunk_complete_cb(p);\n}\n\nstatic http_parser_settings settings_pause =\n  {.on_message_begin = pause_message_begin_cb\n  ,.on_header_field = pause_header_field_cb\n  ,.on_header_value = pause_header_value_cb\n  ,.on_url = pause_request_url_cb\n  ,.on_status = pause_response_status_cb\n  ,.on_body = pause_body_cb\n  ,.on_headers_complete = pause_headers_complete_cb\n  ,.on_message_complete = pause_message_complete_cb\n  ,.on_chunk_header = pause_chunk_header_cb\n  ,.on_chunk_complete = pause_chunk_complete_cb\n  };\n\nstatic http_parser_settings settings =\n  {.on_message_begin = message_begin_cb\n  ,.on_header_field = header_field_cb\n  ,.on_header_value = header_value_cb\n  ,.on_url = request_url_cb\n  ,.on_status = response_status_cb\n  ,.on_body = body_cb\n  ,.on_headers_complete = headers_complete_cb\n  ,.on_message_complete = message_complete_cb\n  ,.on_chunk_header = chunk_header_cb\n  ,.on_chunk_complete = chunk_complete_cb\n  };\n\nstatic http_parser_settings settings_count_body =\n  {.on_message_begin = message_begin_cb\n  ,.on_header_field = header_field_cb\n  ,.on_header_value = header_value_cb\n  ,.on_url = request_url_cb\n  ,.on_status = response_status_cb\n  ,.on_body = count_body_cb\n  ,.on_headers_complete = headers_complete_cb\n  ,.on_message_complete = message_complete_cb\n  ,.on_chunk_header = chunk_header_cb\n  ,.on_chunk_complete = chunk_complete_cb\n  };\n\nstatic http_parser_settings settings_null =\n  {.on_message_begin = 0\n  ,.on_header_field = 0\n  ,.on_header_value = 0\n  ,.on_url = 0\n  ,.on_status = 0\n  ,.on_body = 0\n  ,.on_headers_complete = 0\n  ,.on_message_complete = 0\n  ,.on_chunk_header = 0\n  ,.on_chunk_complete = 0\n  };\n\nvoid\nparser_init (enum http_parser_type type)\n{\n  num_messages = 0;\n\n  assert(parser == NULL);\n\n  parser = malloc(sizeof(http_parser));\n\n  http_parser_init(parser, type);\n\n  memset(&messages, 0, sizeof messages);\n\n}\n\nvoid\nparser_free ()\n{\n  assert(parser);\n  free(parser);\n  parser = NULL;\n}\n\nsize_t parse (const char *buf, size_t len)\n{\n  size_t nparsed;\n  currently_parsing_eof = (len == 0);\n  nparsed = http_parser_execute(parser, &settings, buf, len);\n  return nparsed;\n}\n\nsize_t parse_count_body (const char *buf, size_t len)\n{\n  size_t nparsed;\n  currently_parsing_eof = (len == 0);\n  nparsed = http_parser_execute(parser, &settings_count_body, buf, len);\n  return nparsed;\n}\n\nsize_t parse_pause (const char *buf, size_t len)\n{\n  size_t nparsed;\n  http_parser_settings s = settings_pause;\n\n  currently_parsing_eof = (len == 0);\n  current_pause_parser = &s;\n  nparsed = http_parser_execute(parser, current_pause_parser, buf, len);\n  return nparsed;\n}\n\nstatic inline int\ncheck_str_eq (const struct message *m,\n              const char *prop,\n              const char *expected,\n              const char *found) {\n  if ((expected == NULL) != (found == NULL)) {\n    printf(\"\\n*** Error: %s in '%s' ***\\n\\n\", prop, m->name);\n    printf(\"expected %s\\n\", (expected == NULL) ? \"NULL\" : expected);\n    printf(\"   found %s\\n\", (found == NULL) ? \"NULL\" : found);\n    return 0;\n  }\n  if (expected != NULL && 0 != strcmp(expected, found)) {\n    printf(\"\\n*** Error: %s in '%s' ***\\n\\n\", prop, m->name);\n    printf(\"expected '%s'\\n\", expected);\n    printf(\"   found '%s'\\n\", found);\n    return 0;\n  }\n  return 1;\n}\n\nstatic inline int\ncheck_num_eq (const struct message *m,\n              const char *prop,\n              int expected,\n              int found) {\n  if (expected != found) {\n    printf(\"\\n*** Error: %s in '%s' ***\\n\\n\", prop, m->name);\n    printf(\"expected %d\\n\", expected);\n    printf(\"   found %d\\n\", found);\n    return 0;\n  }\n  return 1;\n}\n\n#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \\\n  if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0\n\n#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \\\n  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0\n\n#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \\\ndo {                                                                 \\\n  char ubuf[256];                                                    \\\n                                                                     \\\n  if ((u)->field_set & (1 << (fn))) {                                \\\n    memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off,   \\\n      (u)->field_data[(fn)].len);                                    \\\n    ubuf[(u)->field_data[(fn)].len] = '\\0';                          \\\n  } else {                                                           \\\n    ubuf[0] = '\\0';                                                  \\\n  }                                                                  \\\n                                                                     \\\n  check_str_eq(expected, #prop, expected->prop, ubuf);               \\\n} while(0)\n\nint\nmessage_eq (int index, const struct message *expected)\n{\n  int i;\n  struct message *m = &messages[index];\n\n  MESSAGE_CHECK_NUM_EQ(expected, m, http_major);\n  MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);\n\n  if (expected->type == HTTP_REQUEST) {\n    MESSAGE_CHECK_NUM_EQ(expected, m, method);\n  } else {\n    MESSAGE_CHECK_NUM_EQ(expected, m, status_code);\n    MESSAGE_CHECK_STR_EQ(expected, m, response_status);\n  }\n\n  MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);\n  MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);\n\n  assert(m->message_begin_cb_called);\n  assert(m->headers_complete_cb_called);\n  assert(m->message_complete_cb_called);\n\n\n  MESSAGE_CHECK_STR_EQ(expected, m, request_url);\n\n  /* Check URL components; we can't do this w/ CONNECT since it doesn't\n   * send us a well-formed URL.\n   */\n  if (*m->request_url && m->method != HTTP_CONNECT) {\n    struct http_parser_url u;\n\n    if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {\n      fprintf(stderr, \"\\n\\n*** failed to parse URL %s ***\\n\\n\",\n        m->request_url);\n      abort();\n    }\n\n    if (expected->host) {\n      MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);\n    }\n\n    if (expected->userinfo) {\n      MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);\n    }\n\n    m->port = (u.field_set & (1 << UF_PORT)) ?\n      u.port : 0;\n\n    MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);\n    MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);\n    MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);\n    MESSAGE_CHECK_NUM_EQ(expected, m, port);\n  }\n\n  if (expected->body_size) {\n    MESSAGE_CHECK_NUM_EQ(expected, m, body_size);\n  } else {\n    MESSAGE_CHECK_STR_EQ(expected, m, body);\n  }\n\n  assert(m->num_chunks == m->num_chunks_complete);\n  MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete);\n  for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) {\n    MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]);\n  }\n\n  MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);\n\n  int r;\n  for (i = 0; i < m->num_headers; i++) {\n    r = check_str_eq(expected, \"header field\", expected->headers[i][0], m->headers[i][0]);\n    if (!r) return 0;\n    r = check_str_eq(expected, \"header value\", expected->headers[i][1], m->headers[i][1]);\n    if (!r) return 0;\n  }\n\n  MESSAGE_CHECK_STR_EQ(expected, m, upgrade);\n\n  return 1;\n}\n\n/* Given a sequence of varargs messages, return the number of them that the\n * parser should successfully parse, taking into account that upgraded\n * messages prevent all subsequent messages from being parsed.\n */\nsize_t\ncount_parsed_messages(const size_t nmsgs, ...) {\n  size_t i;\n  va_list ap;\n\n  va_start(ap, nmsgs);\n\n  for (i = 0; i < nmsgs; i++) {\n    struct message *m = va_arg(ap, struct message *);\n\n    if (m->upgrade) {\n      va_end(ap);\n      return i + 1;\n    }\n  }\n\n  va_end(ap);\n  return nmsgs;\n}\n\n/* Given a sequence of bytes and the number of these that we were able to\n * parse, verify that upgrade bodies are correct.\n */\nvoid\nupgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {\n  va_list ap;\n  size_t i;\n  size_t off = 0;\n \n  va_start(ap, nmsgs);\n\n  for (i = 0; i < nmsgs; i++) {\n    struct message *m = va_arg(ap, struct message *);\n\n    off += strlen(m->raw);\n\n    if (m->upgrade) {\n      off -= strlen(m->upgrade);\n\n      /* Check the portion of the response after its specified upgrade */\n      if (!check_str_eq(m, \"upgrade\", body + off, body + nread)) {\n        abort();\n      }\n\n      /* Fix up the response so that message_eq() will verify the beginning\n       * of the upgrade */\n      *(body + nread + strlen(m->upgrade)) = '\\0';\n      messages[num_messages -1 ].upgrade = body + nread;\n\n      va_end(ap);\n      return;\n    }\n  }\n\n  va_end(ap);\n  printf(\"\\n\\n*** Error: expected a message with upgrade ***\\n\");\n\n  abort();\n}\n\nstatic void\nprint_error (const char *raw, size_t error_location)\n{\n  fprintf(stderr, \"\\n*** %s ***\\n\\n\",\n          http_errno_description(HTTP_PARSER_ERRNO(parser)));\n\n  int this_line = 0, char_len = 0;\n  size_t i, j, len = strlen(raw), error_location_line = 0;\n  for (i = 0; i < len; i++) {\n    if (i == error_location) this_line = 1;\n    switch (raw[i]) {\n      case '\\r':\n        char_len = 2;\n        fprintf(stderr, \"\\\\r\");\n        break;\n\n      case '\\n':\n        fprintf(stderr, \"\\\\n\\n\");\n\n        if (this_line) goto print;\n\n        error_location_line = 0;\n        continue;\n\n      default:\n        char_len = 1;\n        fputc(raw[i], stderr);\n        break;\n    }\n    if (!this_line) error_location_line += char_len;\n  }\n\n  fprintf(stderr, \"[eof]\\n\");\n\n print:\n  for (j = 0; j < error_location_line; j++) {\n    fputc(' ', stderr);\n  }\n  fprintf(stderr, \"^\\n\\nerror location: %u\\n\", (unsigned int)error_location);\n}\n\nvoid\ntest_preserve_data (void)\n{\n  char my_data[] = \"application-specific data\";\n  http_parser parser;\n  parser.data = my_data;\n  http_parser_init(&parser, HTTP_REQUEST);\n  if (parser.data != my_data) {\n    printf(\"\\n*** parser.data not preserved accross http_parser_init ***\\n\\n\");\n    abort();\n  }\n}\n\nstruct url_test {\n  const char *name;\n  const char *url;\n  int is_connect;\n  struct http_parser_url u;\n  int rv;\n};\n\nconst struct url_test url_tests[] =\n{ {.name=\"proxy request\"\n  ,.url=\"http://hostname/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  7,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 15,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"proxy request with port\"\n  ,.url=\"http://hostname:444/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)\n    ,.port=444\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  7,  8 } /* UF_HOST */\n      ,{ 16,  3 } /* UF_PORT */\n      ,{ 19,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"CONNECT request\"\n  ,.url=\"hostname:443\"\n  ,.is_connect=1\n  ,.u=\n    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)\n    ,.port=443\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  0,  8 } /* UF_HOST */\n      ,{  9,  3 } /* UF_PORT */\n      ,{  0,  0 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"CONNECT request but not connect\"\n  ,.url=\"hostname:443\"\n  ,.is_connect=0\n  ,.rv=1\n  }\n\n, {.name=\"proxy ipv6 request\"\n  ,.url=\"http://[1:2::3:4]/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  8,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 17,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"proxy ipv6 request with port\"\n  ,.url=\"http://[1:2::3:4]:67/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)\n    ,.port=67\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  8,  8 } /* UF_HOST */\n      ,{ 18,  2 } /* UF_PORT */\n      ,{ 20,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"CONNECT ipv6 address\"\n  ,.url=\"[1:2::3:4]:443\"\n  ,.is_connect=1\n  ,.u=\n    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)\n    ,.port=443\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  1,  8 } /* UF_HOST */\n      ,{ 11,  3 } /* UF_PORT */\n      ,{  0,  0 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"ipv4 in ipv6 address\"\n  ,.url=\"http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  8, 37 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 46,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"extra ? in query string\"\n  ,.url=\"http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,\"\n  \"fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,\"\n  \"fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  7, 10 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 17, 12 } /* UF_PATH */\n      ,{ 30,187 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"space URL encoded\"\n  ,.url=\"/toto.html?toto=a%20b\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)\n    ,.port=0\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  0,  0 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{  0, 10 } /* UF_PATH */\n      ,{ 11, 10 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n\n, {.name=\"URL fragment\"\n  ,.url=\"/toto.html#titi\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)\n    ,.port=0\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  0,  0 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{  0, 10 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{ 11,  4 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"complex URL fragment\"\n  ,.url=\"http://www.webmasterworld.com/r.cgi?f=21&d=8405&url=\"\n    \"http://www.example.com/index.html?foo=bar&hello=world#midpage\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\\\n      (1<<UF_FRAGMENT)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  7, 22 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 29,  6 } /* UF_PATH */\n      ,{ 36, 69 } /* UF_QUERY */\n      ,{106,  7 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"complex URL from node js url parser doc\"\n  ,.url=\"http://host.com:8080/p/a/t/h?query=string#hash\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\\\n      (1<<UF_QUERY) | (1<<UF_FRAGMENT)\n    ,.port=8080\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  7,  8 } /* UF_HOST */\n      ,{ 16,  4 } /* UF_PORT */\n      ,{ 20,  8 } /* UF_PATH */\n      ,{ 29, 12 } /* UF_QUERY */\n      ,{ 42,  4 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"complex URL with basic auth from node js url parser doc\"\n  ,.url=\"http://a:b@host.com:8080/p/a/t/h?query=string#hash\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\\\n      (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)\n    ,.port=8080\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{ 11,  8 } /* UF_HOST */\n      ,{ 20,  4 } /* UF_PORT */\n      ,{ 24,  8 } /* UF_PATH */\n      ,{ 33, 12 } /* UF_QUERY */\n      ,{ 46,  4 } /* UF_FRAGMENT */\n      ,{  7,  3 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"double @\"\n  ,.url=\"http://a:b@@hostname:443/\"\n  ,.is_connect=0\n  ,.rv=1\n  }\n\n, {.name=\"proxy empty host\"\n  ,.url=\"http://:443/\"\n  ,.is_connect=0\n  ,.rv=1\n  }\n\n, {.name=\"proxy empty port\"\n  ,.url=\"http://hostname:/\"\n  ,.is_connect=0\n  ,.rv=1\n  }\n\n, {.name=\"CONNECT with basic auth\"\n  ,.url=\"a:b@hostname:443\"\n  ,.is_connect=1\n  ,.rv=1\n  }\n\n, {.name=\"CONNECT empty host\"\n  ,.url=\":443\"\n  ,.is_connect=1\n  ,.rv=1\n  }\n\n, {.name=\"CONNECT empty port\"\n  ,.url=\"hostname:\"\n  ,.is_connect=1\n  ,.rv=1\n  }\n\n, {.name=\"CONNECT with extra bits\"\n  ,.url=\"hostname:443/\"\n  ,.is_connect=1\n  ,.rv=1\n  }\n\n, {.name=\"space in URL\"\n  ,.url=\"/foo bar/\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy basic auth with space url encoded\"\n  ,.url=\"http://a%20:b@host.com/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{ 14,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 22,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  7,  6 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"carriage return in URL\"\n  ,.url=\"/foo\\rbar/\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy double : in URL\"\n  ,.url=\"http://hostname::443/\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy basic auth with double :\"\n  ,.url=\"http://a::b@host.com/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{ 12,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 20,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  7,  4 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"line feed in URL\"\n  ,.url=\"/foo\\nbar/\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy empty basic auth\"\n  ,.url=\"http://@hostname/fo\"\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{  8,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 16,  3 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n, {.name=\"proxy line feed in hostname\"\n  ,.url=\"http://host\\name/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy % in hostname\"\n  ,.url=\"http://host%name/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy ; in hostname\"\n  ,.url=\"http://host;ame/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy basic auth with unreservedchars\"\n  ,.url=\"http://a!;-_!=+$@host.com/\"\n  ,.is_connect=0\n  ,.u=\n    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)\n    ,.port=0\n    ,.field_data=\n      {{  0,  4 } /* UF_SCHEMA */\n      ,{ 17,  8 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{ 25,  1 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  7,  9 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"proxy only empty basic auth\"\n  ,.url=\"http://@/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy only basic auth\"\n  ,.url=\"http://toto@/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy emtpy hostname\"\n  ,.url=\"http:///fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"proxy = in URL\"\n  ,.url=\"http://host=ame/fo\"\n  ,.rv=1 /* s_dead */\n  }\n\n#if HTTP_PARSER_STRICT\n\n, {.name=\"tab in URL\"\n  ,.url=\"/foo\\tbar/\"\n  ,.rv=1 /* s_dead */\n  }\n\n, {.name=\"form feed in URL\"\n  ,.url=\"/foo\\fbar/\"\n  ,.rv=1 /* s_dead */\n  }\n\n#else /* !HTTP_PARSER_STRICT */\n\n, {.name=\"tab in URL\"\n  ,.url=\"/foo\\tbar/\"\n  ,.u=\n    {.field_set=(1 << UF_PATH)\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  0,  0 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{  0,  9 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n\n, {.name=\"form feed in URL\"\n  ,.url=\"/foo\\fbar/\"\n  ,.u=\n    {.field_set=(1 << UF_PATH)\n    ,.field_data=\n      {{  0,  0 } /* UF_SCHEMA */\n      ,{  0,  0 } /* UF_HOST */\n      ,{  0,  0 } /* UF_PORT */\n      ,{  0,  9 } /* UF_PATH */\n      ,{  0,  0 } /* UF_QUERY */\n      ,{  0,  0 } /* UF_FRAGMENT */\n      ,{  0,  0 } /* UF_USERINFO */\n      }\n    }\n  ,.rv=0\n  }\n#endif\n};\n\nvoid\ndump_url (const char *url, const struct http_parser_url *u)\n{\n  unsigned int i;\n\n  printf(\"\\tfield_set: 0x%x, port: %u\\n\", u->field_set, u->port);\n  for (i = 0; i < UF_MAX; i++) {\n    if ((u->field_set & (1 << i)) == 0) {\n      printf(\"\\tfield_data[%u]: unset\\n\", i);\n      continue;\n    }\n\n    printf(\"\\tfield_data[%u]: off: %u len: %u part: \\\"%.*s\\n\\\"\",\n           i,\n           u->field_data[i].off,\n           u->field_data[i].len,\n           u->field_data[i].len,\n           url + u->field_data[i].off);\n  }\n}\n\nvoid\ntest_parse_url (void)\n{\n  struct http_parser_url u;\n  const struct url_test *test;\n  unsigned int i;\n  int rv;\n\n  for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {\n    test = &url_tests[i];\n    memset(&u, 0, sizeof(u));\n\n    rv = http_parser_parse_url(test->url,\n                               strlen(test->url),\n                               test->is_connect,\n                               &u);\n\n    if (test->rv == 0) {\n      if (rv != 0) {\n        printf(\"\\n*** http_parser_parse_url(\\\"%s\\\") \\\"%s\\\" test failed, \"\n               \"unexpected rv %d ***\\n\\n\", test->url, test->name, rv);\n        abort();\n      }\n\n      if (memcmp(&u, &test->u, sizeof(u)) != 0) {\n        printf(\"\\n*** http_parser_parse_url(\\\"%s\\\") \\\"%s\\\" failed ***\\n\",\n               test->url, test->name);\n\n        printf(\"target http_parser_url:\\n\");\n        dump_url(test->url, &test->u);\n        printf(\"result http_parser_url:\\n\");\n        dump_url(test->url, &u);\n\n        abort();\n      }\n    } else {\n      /* test->rv != 0 */\n      if (rv == 0) {\n        printf(\"\\n*** http_parser_parse_url(\\\"%s\\\") \\\"%s\\\" test failed, \"\n               \"unexpected rv %d ***\\n\\n\", test->url, test->name, rv);\n        abort();\n      }\n    }\n  }\n}\n\nvoid\ntest_method_str (void)\n{\n  assert(0 == strcmp(\"GET\", http_method_str(HTTP_GET)));\n  assert(0 == strcmp(\"<unknown>\", http_method_str(1337)));\n}\n\nvoid\ntest_message (const struct message *message)\n{\n  size_t raw_len = strlen(message->raw);\n  size_t msg1len;\n  for (msg1len = 0; msg1len < raw_len; msg1len++) {\n    parser_init(message->type);\n\n    size_t read;\n    const char *msg1 = message->raw;\n    const char *msg2 = msg1 + msg1len;\n    size_t msg2len = raw_len - msg1len;\n\n    if (msg1len) {\n      read = parse(msg1, msg1len);\n\n      if (message->upgrade && parser->upgrade && num_messages > 0) {\n        messages[num_messages - 1].upgrade = msg1 + read;\n        goto test;\n      }\n\n      if (read != msg1len) {\n        print_error(msg1, read);\n        abort();\n      }\n    }\n\n\n    read = parse(msg2, msg2len);\n\n    if (message->upgrade && parser->upgrade) {\n      messages[num_messages - 1].upgrade = msg2 + read;\n      goto test;\n    }\n\n    if (read != msg2len) {\n      print_error(msg2, read);\n      abort();\n    }\n\n    read = parse(NULL, 0);\n\n    if (read != 0) {\n      print_error(message->raw, read);\n      abort();\n    }\n\n  test:\n\n    if (num_messages != 1) {\n      printf(\"\\n*** num_messages != 1 after testing '%s' ***\\n\\n\", message->name);\n      abort();\n    }\n\n    if(!message_eq(0, message)) abort();\n\n    parser_free();\n  }\n}\n\nvoid\ntest_message_count_body (const struct message *message)\n{\n  parser_init(message->type);\n\n  size_t read;\n  size_t l = strlen(message->raw);\n  size_t i, toread;\n  size_t chunk = 4024;\n\n  for (i = 0; i < l; i+= chunk) {\n    toread = MIN(l-i, chunk);\n    read = parse_count_body(message->raw + i, toread);\n    if (read != toread) {\n      print_error(message->raw, read);\n      abort();\n    }\n  }\n\n\n  read = parse_count_body(NULL, 0);\n  if (read != 0) {\n    print_error(message->raw, read);\n    abort();\n  }\n\n  if (num_messages != 1) {\n    printf(\"\\n*** num_messages != 1 after testing '%s' ***\\n\\n\", message->name);\n    abort();\n  }\n\n  if(!message_eq(0, message)) abort();\n\n  parser_free();\n}\n\nvoid\ntest_simple (const char *buf, enum http_errno err_expected)\n{\n  parser_init(HTTP_REQUEST);\n\n  enum http_errno err;\n\n  parse(buf, strlen(buf));\n  err = HTTP_PARSER_ERRNO(parser);\n  parse(NULL, 0);\n\n  parser_free();\n\n  /* In strict mode, allow us to pass with an unexpected HPE_STRICT as\n   * long as the caller isn't expecting success.\n   */\n#if HTTP_PARSER_STRICT\n  if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {\n#else\n  if (err_expected != err) {\n#endif\n    fprintf(stderr, \"\\n*** test_simple expected %s, but saw %s ***\\n\\n%s\\n\",\n        http_errno_name(err_expected), http_errno_name(err), buf);\n    abort();\n  }\n}\n\nvoid\ntest_header_overflow_error (int req)\n{\n  http_parser parser;\n  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);\n  size_t parsed;\n  const char *buf;\n  buf = req ? \"GET / HTTP/1.1\\r\\n\" : \"HTTP/1.0 200 OK\\r\\n\";\n  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));\n  assert(parsed == strlen(buf));\n\n  buf = \"header-key: header-value\\r\\n\";\n  size_t buflen = strlen(buf);\n\n  int i;\n  for (i = 0; i < 10000; i++) {\n    parsed = http_parser_execute(&parser, &settings_null, buf, buflen);\n    if (parsed != buflen) {\n      //fprintf(stderr, \"error found on iter %d\\n\", i);\n      assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);\n      return;\n    }\n  }\n\n  fprintf(stderr, \"\\n*** Error expected but none in header overflow test ***\\n\");\n  abort();\n}\n\n\nvoid\ntest_header_nread_value ()\n{\n  http_parser parser;\n  http_parser_init(&parser, HTTP_REQUEST);\n  size_t parsed;\n  const char *buf;\n  buf = \"GET / HTTP/1.1\\r\\nheader: value\\nhdr: value\\r\\n\";\n  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));\n  assert(parsed == strlen(buf));\n\n  assert(parser.nread == strlen(buf));\n}\n\n\nstatic void\ntest_content_length_overflow (const char *buf, size_t buflen, int expect_ok)\n{\n  http_parser parser;\n  http_parser_init(&parser, HTTP_RESPONSE);\n  http_parser_execute(&parser, &settings_null, buf, buflen);\n\n  if (expect_ok)\n    assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);\n  else\n    assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);\n}\n\nvoid\ntest_header_content_length_overflow_error (void)\n{\n#define X(size)                                                               \\\n  \"HTTP/1.1 200 OK\\r\\n\"                                                       \\\n  \"Content-Length: \" #size \"\\r\\n\"                                             \\\n  \"\\r\\n\"\n  const char a[] = X(1844674407370955160);  /* 2^64 / 10 - 1 */\n  const char b[] = X(18446744073709551615); /* 2^64-1 */\n  const char c[] = X(18446744073709551616); /* 2^64   */\n#undef X\n  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */\n  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */\n  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */\n}\n\nvoid\ntest_chunk_content_length_overflow_error (void)\n{\n#define X(size)                                                               \\\n    \"HTTP/1.1 200 OK\\r\\n\"                                                     \\\n    \"Transfer-Encoding: chunked\\r\\n\"                                          \\\n    \"\\r\\n\"                                                                    \\\n    #size \"\\r\\n\"                                                              \\\n    \"...\"\n  const char a[] = X(FFFFFFFFFFFFFFE);   /* 2^64 / 16 - 1 */\n  const char b[] = X(FFFFFFFFFFFFFFFF);  /* 2^64-1 */\n  const char c[] = X(10000000000000000); /* 2^64   */\n#undef X\n  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */\n  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */\n  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */\n}\n\nvoid\ntest_no_overflow_long_body (int req, size_t length)\n{\n  http_parser parser;\n  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);\n  size_t parsed;\n  size_t i;\n  char buf1[3000];\n  size_t buf1len = sprintf(buf1, \"%s\\r\\nConnection: Keep-Alive\\r\\nContent-Length: %lu\\r\\n\\r\\n\",\n      req ? \"POST / HTTP/1.0\" : \"HTTP/1.0 200 OK\", (unsigned long)length);\n  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);\n  if (parsed != buf1len)\n    goto err;\n\n  for (i = 0; i < length; i++) {\n    char foo = 'a';\n    parsed = http_parser_execute(&parser, &settings_null, &foo, 1);\n    if (parsed != 1)\n      goto err;\n  }\n\n  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);\n  if (parsed != buf1len) goto err;\n  return;\n\n err:\n  fprintf(stderr,\n          \"\\n*** error in test_no_overflow_long_body %s of length %lu ***\\n\",\n          req ? \"REQUEST\" : \"RESPONSE\",\n          (unsigned long)length);\n  abort();\n}\n\nvoid\ntest_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)\n{\n  int message_count = count_parsed_messages(3, r1, r2, r3);\n\n  char total[ strlen(r1->raw)\n            + strlen(r2->raw)\n            + strlen(r3->raw)\n            + 1\n            ];\n  total[0] = '\\0';\n\n  strcat(total, r1->raw);\n  strcat(total, r2->raw);\n  strcat(total, r3->raw);\n\n  parser_init(r1->type);\n\n  size_t read;\n\n  read = parse(total, strlen(total));\n\n  if (parser->upgrade) {\n    upgrade_message_fix(total, read, 3, r1, r2, r3);\n    goto test;\n  }\n\n  if (read != strlen(total)) {\n    print_error(total, read);\n    abort();\n  }\n\n  read = parse(NULL, 0);\n\n  if (read != 0) {\n    print_error(total, read);\n    abort();\n  }\n\ntest:\n\n  if (message_count != num_messages) {\n    fprintf(stderr, \"\\n\\n*** Parser didn't see 3 messages only %d *** \\n\", num_messages);\n    abort();\n  }\n\n  if (!message_eq(0, r1)) abort();\n  if (message_count > 1 && !message_eq(1, r2)) abort();\n  if (message_count > 2 && !message_eq(2, r3)) abort();\n\n  parser_free();\n}\n\n/* SCAN through every possible breaking to make sure the\n * parser can handle getting the content in any chunks that\n * might come from the socket\n */\nvoid\ntest_scan (const struct message *r1, const struct message *r2, const struct message *r3)\n{\n  char total[80*1024] = \"\\0\";\n  char buf1[80*1024] = \"\\0\";\n  char buf2[80*1024] = \"\\0\";\n  char buf3[80*1024] = \"\\0\";\n\n  strcat(total, r1->raw);\n  strcat(total, r2->raw);\n  strcat(total, r3->raw);\n\n  size_t read;\n\n  int total_len = strlen(total);\n\n  int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;\n  int ops = 0 ;\n\n  size_t buf1_len, buf2_len, buf3_len;\n  int message_count = count_parsed_messages(3, r1, r2, r3);\n\n  int i,j,type_both;\n  for (type_both = 0; type_both < 2; type_both ++ ) {\n    for (j = 2; j < total_len; j ++ ) {\n      for (i = 1; i < j; i ++ ) {\n\n        if (ops % 1000 == 0)  {\n          printf(\"\\b\\b\\b\\b%3.0f%%\", 100 * (float)ops /(float)total_ops);\n          fflush(stdout);\n        }\n        ops += 1;\n\n        parser_init(type_both ? HTTP_BOTH : r1->type);\n\n        buf1_len = i;\n        strlncpy(buf1, sizeof(buf1), total, buf1_len);\n        buf1[buf1_len] = 0;\n\n        buf2_len = j - i;\n        strlncpy(buf2, sizeof(buf1), total+i, buf2_len);\n        buf2[buf2_len] = 0;\n\n        buf3_len = total_len - j;\n        strlncpy(buf3, sizeof(buf1), total+j, buf3_len);\n        buf3[buf3_len] = 0;\n\n        read = parse(buf1, buf1_len);\n\n        if (parser->upgrade) goto test;\n\n        if (read != buf1_len) {\n          print_error(buf1, read);\n          goto error;\n        }\n\n        read += parse(buf2, buf2_len);\n\n        if (parser->upgrade) goto test;\n\n        if (read != buf1_len + buf2_len) {\n          print_error(buf2, read);\n          goto error;\n        }\n\n        read += parse(buf3, buf3_len);\n\n        if (parser->upgrade) goto test;\n\n        if (read != buf1_len + buf2_len + buf3_len) {\n          print_error(buf3, read);\n          goto error;\n        }\n\n        parse(NULL, 0);\n\ntest:\n        if (parser->upgrade) {\n          upgrade_message_fix(total, read, 3, r1, r2, r3);\n        }\n\n        if (message_count != num_messages) {\n          fprintf(stderr, \"\\n\\nParser didn't see %d messages only %d\\n\",\n            message_count, num_messages);\n          goto error;\n        }\n\n        if (!message_eq(0, r1)) {\n          fprintf(stderr, \"\\n\\nError matching messages[0] in test_scan.\\n\");\n          goto error;\n        }\n\n        if (message_count > 1 && !message_eq(1, r2)) {\n          fprintf(stderr, \"\\n\\nError matching messages[1] in test_scan.\\n\");\n          goto error;\n        }\n\n        if (message_count > 2 && !message_eq(2, r3)) {\n          fprintf(stderr, \"\\n\\nError matching messages[2] in test_scan.\\n\");\n          goto error;\n        }\n\n        parser_free();\n      }\n    }\n  }\n  puts(\"\\b\\b\\b\\b100%\");\n  return;\n\n error:\n  fprintf(stderr, \"i=%d  j=%d\\n\", i, j);\n  fprintf(stderr, \"buf1 (%u) %s\\n\\n\", (unsigned int)buf1_len, buf1);\n  fprintf(stderr, \"buf2 (%u) %s\\n\\n\", (unsigned int)buf2_len , buf2);\n  fprintf(stderr, \"buf3 (%u) %s\\n\", (unsigned int)buf3_len, buf3);\n  abort();\n}\n\n// user required to free the result\n// string terminated by \\0\nchar *\ncreate_large_chunked_message (int body_size_in_kb, const char* headers)\n{\n  int i;\n  size_t wrote = 0;\n  size_t headers_len = strlen(headers);\n  size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;\n  char * buf = malloc(bufsize);\n\n  memcpy(buf, headers, headers_len);\n  wrote += headers_len;\n\n  for (i = 0; i < body_size_in_kb; i++) {\n    // write 1kb chunk into the body.\n    memcpy(buf + wrote, \"400\\r\\n\", 5);\n    wrote += 5;\n    memset(buf + wrote, 'C', 1024);\n    wrote += 1024;\n    strcpy(buf + wrote, \"\\r\\n\");\n    wrote += 2;\n  }\n\n  memcpy(buf + wrote, \"0\\r\\n\\r\\n\", 6);\n  wrote += 6;\n  assert(wrote == bufsize);\n\n  return buf;\n}\n\n/* Verify that we can pause parsing at any of the bytes in the\n * message and still get the result that we're expecting. */\nvoid\ntest_message_pause (const struct message *msg)\n{\n  char *buf = (char*) msg->raw;\n  size_t buflen = strlen(msg->raw);\n  size_t nread;\n\n  parser_init(msg->type);\n\n  do {\n    nread = parse_pause(buf, buflen);\n\n    // We can only set the upgrade buffer once we've gotten our message\n    // completion callback.\n    if (messages[0].message_complete_cb_called &&\n        msg->upgrade &&\n        parser->upgrade) {\n      messages[0].upgrade = buf + nread;\n      goto test;\n    }\n\n    if (nread < buflen) {\n\n      // Not much do to if we failed a strict-mode check\n      if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {\n        parser_free();\n        return;\n      }\n\n      assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);\n    }\n\n    buf += nread;\n    buflen -= nread;\n    http_parser_pause(parser, 0);\n  } while (buflen > 0);\n\n  nread = parse_pause(NULL, 0);\n  assert (nread == 0);\n\ntest:\n  if (num_messages != 1) {\n    printf(\"\\n*** num_messages != 1 after testing '%s' ***\\n\\n\", msg->name);\n    abort();\n  }\n\n  if(!message_eq(0, msg)) abort();\n\n  parser_free();\n}\n\nint\nmain (void)\n{\n  parser = NULL;\n  int i, j, k;\n  int request_count;\n  int response_count;\n  unsigned long version;\n  unsigned major;\n  unsigned minor;\n  unsigned patch;\n\n  version = http_parser_version();\n  major = (version >> 16) & 255;\n  minor = (version >> 8) & 255;\n  patch = version & 255;\n  printf(\"http_parser v%u.%u.%u (0x%06lx)\\n\", major, minor, patch, version);\n\n  printf(\"sizeof(http_parser) = %u\\n\", (unsigned int)sizeof(http_parser));\n\n  for (request_count = 0; requests[request_count].name; request_count++);\n  for (response_count = 0; responses[response_count].name; response_count++);\n\n  //// API\n  test_preserve_data();\n  test_parse_url();\n  test_method_str();\n\n  //// NREAD\n  test_header_nread_value();\n\n  //// OVERFLOW CONDITIONS\n\n  test_header_overflow_error(HTTP_REQUEST);\n  test_no_overflow_long_body(HTTP_REQUEST, 1000);\n  test_no_overflow_long_body(HTTP_REQUEST, 100000);\n\n  test_header_overflow_error(HTTP_RESPONSE);\n  test_no_overflow_long_body(HTTP_RESPONSE, 1000);\n  test_no_overflow_long_body(HTTP_RESPONSE, 100000);\n\n  test_header_content_length_overflow_error();\n  test_chunk_content_length_overflow_error();\n\n  //// RESPONSES\n\n  for (i = 0; i < response_count; i++) {\n    test_message(&responses[i]);\n  }\n\n  for (i = 0; i < response_count; i++) {\n    test_message_pause(&responses[i]);\n  }\n\n  for (i = 0; i < response_count; i++) {\n    if (!responses[i].should_keep_alive) continue;\n    for (j = 0; j < response_count; j++) {\n      if (!responses[j].should_keep_alive) continue;\n      for (k = 0; k < response_count; k++) {\n        test_multiple3(&responses[i], &responses[j], &responses[k]);\n      }\n    }\n  }\n\n  test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);\n  test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);\n\n  // test very large chunked response\n  {\n    char * msg = create_large_chunked_message(31337,\n      \"HTTP/1.0 200 OK\\r\\n\"\n      \"Transfer-Encoding: chunked\\r\\n\"\n      \"Content-Type: text/plain\\r\\n\"\n      \"\\r\\n\");\n    struct message large_chunked =\n      {.name= \"large chunked\"\n      ,.type= HTTP_RESPONSE\n      ,.raw= msg\n      ,.should_keep_alive= FALSE\n      ,.message_complete_on_eof= FALSE\n      ,.http_major= 1\n      ,.http_minor= 0\n      ,.status_code= 200\n      ,.response_status= \"OK\"\n      ,.num_headers= 2\n      ,.headers=\n        { { \"Transfer-Encoding\", \"chunked\" }\n        , { \"Content-Type\", \"text/plain\" }\n        }\n      ,.body_size= 31337*1024\n      ,.num_chunks_complete= 31338\n      };\n    for (i = 0; i < MAX_CHUNKS; i++) {\n      large_chunked.chunk_lengths[i] = 1024;\n    }\n    test_message_count_body(&large_chunked);\n    free(msg);\n  }\n\n\n\n  printf(\"response scan 1/2      \");\n  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]\n           , &responses[NO_BODY_HTTP10_KA_204]\n           , &responses[NO_REASON_PHRASE]\n           );\n\n  printf(\"response scan 2/2      \");\n  test_scan( &responses[BONJOUR_MADAME_FR]\n           , &responses[UNDERSTORE_HEADER_KEY]\n           , &responses[NO_CARRIAGE_RET]\n           );\n\n  puts(\"responses okay\");\n\n\n  /// REQUESTS\n\n  test_simple(\"GET / HTP/1.1\\r\\n\\r\\n\", HPE_INVALID_VERSION);\n\n  // Well-formed but incomplete\n  test_simple(\"GET / HTTP/1.1\\r\\n\"\n              \"Content-Type: text/plain\\r\\n\"\n              \"Content-Length: 6\\r\\n\"\n              \"\\r\\n\"\n              \"fooba\",\n              HPE_OK);\n\n  static const char *all_methods[] = {\n    \"DELETE\",\n    \"GET\",\n    \"HEAD\",\n    \"POST\",\n    \"PUT\",\n    //\"CONNECT\", //CONNECT can't be tested like other methods, it's a tunnel\n    \"OPTIONS\",\n    \"TRACE\",\n    \"COPY\",\n    \"LOCK\",\n    \"MKCOL\",\n    \"MOVE\",\n    \"PROPFIND\",\n    \"PROPPATCH\",\n    \"UNLOCK\",\n    \"REPORT\",\n    \"MKACTIVITY\",\n    \"CHECKOUT\",\n    \"MERGE\",\n    \"M-SEARCH\",\n    \"NOTIFY\",\n    \"SUBSCRIBE\",\n    \"UNSUBSCRIBE\",\n    \"PATCH\",\n    0 };\n  const char **this_method;\n  for (this_method = all_methods; *this_method; this_method++) {\n    char buf[200];\n    sprintf(buf, \"%s / HTTP/1.1\\r\\n\\r\\n\", *this_method);\n    test_simple(buf, HPE_OK);\n  }\n\n  static const char *bad_methods[] = {\n      \"ASDF\",\n      \"C******\",\n      \"COLA\",\n      \"GEM\",\n      \"GETA\",\n      \"M****\",\n      \"MKCOLA\",\n      \"PROPPATCHA\",\n      \"PUN\",\n      \"PX\",\n      \"SA\",\n      \"hello world\",\n      0 };\n  for (this_method = bad_methods; *this_method; this_method++) {\n    char buf[200];\n    sprintf(buf, \"%s / HTTP/1.1\\r\\n\\r\\n\", *this_method);\n    test_simple(buf, HPE_INVALID_METHOD);\n  }\n\n  // illegal header field name line folding\n  test_simple(\"GET / HTTP/1.1\\r\\n\"\n              \"name\\r\\n\"\n              \" : value\\r\\n\"\n              \"\\r\\n\",\n              HPE_INVALID_HEADER_TOKEN);\n\n  const char *dumbfuck2 =\n    \"GET / HTTP/1.1\\r\\n\"\n    \"X-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\\r\\n\"\n    \"\\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\\r\\n\"\n    \"\\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\\r\\n\"\n    \"\\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\\r\\n\"\n    \"\\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\\r\\n\"\n    \"\\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\\r\\n\"\n    \"\\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\\r\\n\"\n    \"\\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\\r\\n\"\n    \"\\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\\r\\n\"\n    \"\\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\\r\\n\"\n    \"\\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\\r\\n\"\n    \"\\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\\r\\n\"\n    \"\\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\\r\\n\"\n    \"\\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\\r\\n\"\n    \"\\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\\r\\n\"\n    \"\\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\\r\\n\"\n    \"\\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\\r\\n\"\n    \"\\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\\r\\n\"\n    \"\\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\\r\\n\"\n    \"\\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\\r\\n\"\n    \"\\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\\r\\n\"\n    \"\\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\\r\\n\"\n    \"\\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\\r\\n\"\n    \"\\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\\r\\n\"\n    \"\\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\\r\\n\"\n    \"\\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\\r\\n\"\n    \"\\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\\r\\n\"\n    \"\\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\\r\\n\"\n    \"\\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\\r\\n\"\n    \"\\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\\r\\n\"\n    \"\\tRA==\\r\\n\"\n    \"\\t-----END CERTIFICATE-----\\r\\n\"\n    \"\\r\\n\";\n  test_simple(dumbfuck2, HPE_OK);\n\n  const char *corrupted_connection =\n    \"GET / HTTP/1.1\\r\\n\"\n    \"Host: www.example.com\\r\\n\"\n    \"Connection\\r\\033\\065\\325eep-Alive\\r\\n\"\n    \"Accept-Encoding: gzip\\r\\n\"\n    \"\\r\\n\";\n  test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN);\n\n  const char *corrupted_header_name =\n    \"GET / HTTP/1.1\\r\\n\"\n    \"Host: www.example.com\\r\\n\"\n    \"X-Some-Header\\r\\033\\065\\325eep-Alive\\r\\n\"\n    \"Accept-Encoding: gzip\\r\\n\"\n    \"\\r\\n\";\n  test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN);\n\n#if 0\n  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body\n  // until EOF.\n  //\n  // no content-length\n  // error if there is a body without content length\n  const char *bad_get_no_headers_no_body = \"GET /bad_get_no_headers_no_body/world HTTP/1.1\\r\\n\"\n                                           \"Accept: */*\\r\\n\"\n                                           \"\\r\\n\"\n                                           \"HELLO\";\n  test_simple(bad_get_no_headers_no_body, 0);\n#endif\n  /* TODO sending junk and large headers gets rejected */\n\n\n  /* check to make sure our predefined requests are okay */\n  for (i = 0; requests[i].name; i++) {\n    test_message(&requests[i]);\n  }\n\n  for (i = 0; i < request_count; i++) {\n    test_message_pause(&requests[i]);\n  }\n\n  for (i = 0; i < request_count; i++) {\n    if (!requests[i].should_keep_alive) continue;\n    for (j = 0; j < request_count; j++) {\n      if (!requests[j].should_keep_alive) continue;\n      for (k = 0; k < request_count; k++) {\n        test_multiple3(&requests[i], &requests[j], &requests[k]);\n      }\n    }\n  }\n\n  printf(\"request scan 1/4      \");\n  test_scan( &requests[GET_NO_HEADERS_NO_BODY]\n           , &requests[GET_ONE_HEADER_NO_BODY]\n           , &requests[GET_NO_HEADERS_NO_BODY]\n           );\n\n  printf(\"request scan 2/4      \");\n  test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]\n           , &requests[POST_IDENTITY_BODY_WORLD]\n           , &requests[GET_FUNKY_CONTENT_LENGTH]\n           );\n\n  printf(\"request scan 3/4      \");\n  test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]\n           , &requests[CHUNKED_W_TRAILING_HEADERS]\n           , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]\n           );\n\n  printf(\"request scan 4/4      \");\n  test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]\n           , &requests[PREFIX_NEWLINE_GET ]\n           , &requests[CONNECT_REQUEST]\n           );\n\n  puts(\"requests okay\");\n\n  return 0;\n}\n"
  },
  {
    "path": "src/shrpx-unittest.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include <stdio.h>\n#include <string.h>\n#include <CUnit/Basic.h>\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n/* include test cases' include files here */\n#include \"shrpx_ssl_test.h\"\n\nstatic int init_suite1(void)\n{\n  return 0;\n}\n\nstatic int clean_suite1(void)\n{\n  return 0;\n}\n\n\nint main(int argc, char* argv[])\n{\n   CU_pSuite pSuite = NULL;\n   unsigned int num_tests_failed;\n\n   OpenSSL_add_all_algorithms();\n   SSL_load_error_strings();\n   SSL_library_init();\n\n   /* initialize the CUnit test registry */\n   if (CUE_SUCCESS != CU_initialize_registry())\n      return CU_get_error();\n\n   /* add a suite to the registry */\n   pSuite = CU_add_suite(\"shrpx_TestSuite\", init_suite1, clean_suite1);\n   if (NULL == pSuite) {\n      CU_cleanup_registry();\n      return CU_get_error();\n   }\n\n   /* add the tests to the suite */\n   if(!CU_add_test(pSuite, \"ssl_create_lookup_tree\",\n                   shrpx::test_shrpx_ssl_create_lookup_tree) ||\n      !CU_add_test(pSuite, \"ssl_cert_lookup_tree_add_cert_from_file\",\n                   shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file)) {\n     CU_cleanup_registry();\n     return CU_get_error();\n   }\n\n   /* Run all tests using the CUnit Basic interface */\n   CU_basic_set_mode(CU_BRM_VERBOSE);\n   CU_basic_run_tests();\n   num_tests_failed = CU_get_number_of_tests_failed();\n   CU_cleanup_registry();\n   if(CU_get_error() == CUE_SUCCESS) {\n     return num_tests_failed;\n   } else {\n     printf(\"CUnit Error: %s\\n\", CU_get_error_msg());\n     return CU_get_error();\n   }\n}\n"
  },
  {
    "path": "src/shrpx.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx.h\"\n\n#ifdef __sgi\n#define daemon _daemonize\n#endif // __sgi\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <signal.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <unistd.h>\n#include <getopt.h>\n#include <syslog.h>\n\n#include <limits>\n#include <cstdlib>\n#include <iostream>\n#include <fstream>\n#include <vector>\n#include <cstring>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#include <event2/listener.h>\n\n#include <spdylay/spdylay.h>\n\n#include \"shrpx_config.h\"\n#include \"shrpx_listen_handler.h\"\n#include \"shrpx_ssl.h\"\n\nnamespace shrpx {\n\nnamespace {\nvoid ssl_acceptcb(evconnlistener *listener, int fd,\n                  sockaddr *addr, int addrlen, void *arg)\n{\n  ListenHandler *handler = static_cast<ListenHandler*>(arg);\n  handler->accept_connection(fd, addr, addrlen);\n}\n} // namespace\n\nnamespace {\nbool is_ipv6_numeric_addr(const char *host)\n{\n  uint8_t dst[16];\n  return inet_pton(AF_INET6, host, dst) == 1;\n}\n} // namespace\n\nnamespace {\nint resolve_hostname(sockaddr_union *addr, size_t *addrlen,\n                     const char *hostname, uint16_t port, int family)\n{\n  addrinfo hints;\n  int rv;\n  char service[10];\n\n  snprintf(service, sizeof(service), \"%u\", port);\n  memset(&hints, 0, sizeof(addrinfo));\n\n  hints.ai_family = family;\n  hints.ai_socktype = SOCK_STREAM;\n#ifdef AI_ADDRCONFIG\n  hints.ai_flags |= AI_ADDRCONFIG;\n#endif // AI_ADDRCONFIG\n  addrinfo *res;\n\n  rv = getaddrinfo(hostname, service, &hints, &res);\n  if(rv != 0) {\n    LOG(FATAL) << \"Unable to resolve address for \" << hostname\n               << \": \" << gai_strerror(rv);\n    return -1;\n  }\n\n  char host[NI_MAXHOST];\n  rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host),\n                  0, 0, NI_NUMERICHOST);\n  if(rv == 0) {\n    if(LOG_ENABLED(INFO)) {\n      LOG(INFO) << \"Address resolution for \" << hostname << \" succeeded: \"\n                << host;\n    }\n  } else {\n    LOG(FATAL) << \"Address resolution for \" << hostname << \" failed: \"\n               << gai_strerror(rv);\n    return -1;\n  }\n  memcpy(addr, res->ai_addr, res->ai_addrlen);\n  *addrlen = res->ai_addrlen;\n  freeaddrinfo(res);\n  return 0;\n}\n} // namespace\n\nnamespace {\nvoid evlistener_errorcb(evconnlistener *listener, void *ptr)\n{\n  LOG(ERROR) << \"Accepting incoming connection failed\";\n}\n} // namespace\n\nnamespace {\nevconnlistener* create_evlistener(ListenHandler *handler, int family)\n{\n  // TODO Listen both IPv4 and IPv6\n  addrinfo hints;\n  int fd = -1;\n  int r;\n  char service[10];\n  snprintf(service, sizeof(service), \"%u\", get_config()->port);\n  memset(&hints, 0, sizeof(addrinfo));\n  hints.ai_family = family;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE;\n#ifdef AI_ADDRCONFIG\n  hints.ai_flags |= AI_ADDRCONFIG;\n#endif // AI_ADDRCONFIG\n\n  addrinfo *res, *rp;\n  r = getaddrinfo(get_config()->host, service, &hints, &res);\n  if(r != 0) {\n    if(LOG_ENABLED(INFO)) {\n      LOG(INFO) << \"Unable to get IPv\" << (family == AF_INET ? \"4\" : \"6\")\n                << \" address for \" << get_config()->host << \": \"\n                << gai_strerror(r);\n    }\n    return NULL;\n  }\n  for(rp = res; rp; rp = rp->ai_next) {\n    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    if(fd == -1) {\n      continue;\n    }\n    int val = 1;\n    if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,\n                  static_cast<socklen_t>(sizeof(val))) == -1) {\n      close(fd);\n      continue;\n    }\n    evutil_make_socket_nonblocking(fd);\n#ifdef IPV6_V6ONLY\n    if(family == AF_INET6) {\n      if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,\n                    static_cast<socklen_t>(sizeof(val))) == -1) {\n        close(fd);\n        continue;\n      }\n    }\n#endif // IPV6_V6ONLY\n    if(bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) {\n      break;\n    }\n    close(fd);\n  }\n  if(rp) {\n    char host[NI_MAXHOST];\n    r = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host),\n                        0, 0, NI_NUMERICHOST);\n    if(r == 0) {\n      if(LOG_ENABLED(INFO)) {\n        LOG(INFO) << \"Listening on \" << host << \", port \"\n                  << get_config()->port;\n      }\n    } else {\n      LOG(FATAL) << gai_strerror(r);\n      DIE();\n    }\n  }\n  freeaddrinfo(res);\n  if(rp == 0) {\n    if(LOG_ENABLED(INFO)) {\n      LOG(INFO) << \"Listening \" << (family == AF_INET ? \"IPv4\" : \"IPv6\")\n                << \" socket failed\";\n    }\n    return 0;\n  }\n\n  evconnlistener *evlistener = evconnlistener_new\n    (handler->get_evbase(),\n     ssl_acceptcb,\n     handler,\n     LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,\n     get_config()->backlog,\n     fd);\n  evconnlistener_set_error_cb(evlistener, evlistener_errorcb);\n  return evlistener;\n}\n} // namespace\n\nnamespace {\nvoid drop_privileges()\n{\n  if(getuid() == 0 && get_config()->uid != 0) {\n    if(setgid(get_config()->gid) != 0) {\n      LOG(FATAL) << \"Could not change gid: \" << strerror(errno);\n      exit(EXIT_FAILURE);\n    }\n    if(setuid(get_config()->uid) != 0) {\n      LOG(FATAL) << \"Could not change uid: \" << strerror(errno);\n      exit(EXIT_FAILURE);\n    }\n    if(setuid(0) != -1) {\n      LOG(FATAL) << \"Still have root privileges?\";\n      exit(EXIT_FAILURE);\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid save_pid()\n{\n  std::ofstream out(get_config()->pid_file, std::ios::binary);\n  out << getpid() << \"\\n\";\n  out.close();\n  if(!out) {\n    LOG(ERROR) << \"Could not save PID to file \" << get_config()->pid_file;\n    exit(EXIT_FAILURE);\n  }\n}\n} // namespace\n\nnamespace {\nint event_loop()\n{\n  event_base *evbase = event_base_new();\n  SSL_CTX *sv_ssl_ctx, *cl_ssl_ctx;\n\n  if(get_config()->client_mode) {\n    sv_ssl_ctx = 0;\n    cl_ssl_ctx = get_config()->spdy_downstream_no_tls ?\n      0 : ssl::create_ssl_client_context();\n  } else {\n    sv_ssl_ctx = get_config()->spdy_upstream_no_tls ?\n      0 : get_config()->default_ssl_ctx;\n    cl_ssl_ctx = get_config()->spdy_bridge &&\n      !get_config()->spdy_downstream_no_tls ?\n      ssl::create_ssl_client_context() : 0;\n  }\n\n  ListenHandler *listener_handler = new ListenHandler(evbase, sv_ssl_ctx,\n                                                      cl_ssl_ctx);\n  if(get_config()->daemon) {\n#ifdef __sgi\n    if(daemon(0, 0, 0, 0) == -1) {\n#else // !__sgi\n    if(daemon(0, 0) == -1) {\n#endif // !__sgi\n      LOG(FATAL) << \"Failed to daemonize: \" << strerror(errno);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if(get_config()->pid_file) {\n    save_pid();\n  }\n\n  evconnlistener *evlistener6, *evlistener4;\n  evlistener6 = create_evlistener(listener_handler, AF_INET6);\n  evlistener4 = create_evlistener(listener_handler, AF_INET);\n  if(!evlistener6 && !evlistener4) {\n    LOG(FATAL) << \"Failed to listen on address \"\n               << get_config()->host << \", port \" << get_config()->port;\n    exit(EXIT_FAILURE);\n  }\n\n  // ListenHandler loads private key, and we listen on a priveleged port.\n  // After that, we drop the root privileges if needed.\n  drop_privileges();\n\n  if(get_config()->num_worker > 1) {\n    listener_handler->create_worker_thread(get_config()->num_worker);\n  } else if(get_config()->downstream_proto == PROTO_SPDY) {\n    listener_handler->create_spdy_session();\n  }\n\n  if(LOG_ENABLED(INFO)) {\n    LOG(INFO) << \"Entering event loop\";\n  }\n  event_base_loop(evbase, 0);\n  if(evlistener4) {\n    evconnlistener_free(evlistener4);\n  }\n  if(evlistener6) {\n    evconnlistener_free(evlistener6);\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\n// Returns true if regular file or symbolic link |path| exists.\nbool conf_exists(const char *path)\n{\n  struct stat buf;\n  int rv = stat(path, &buf);\n  return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK));\n}\n} // namespace\n\nnamespace {\nconst char *DEFAULT_TLS_PROTO_LIST = \"TLSv1.2,TLSv1.1,TLSv1.0\";\n} // namespace\n\nnamespace {\nvoid fill_default_config()\n{\n  memset(mod_config(), 0, sizeof(*mod_config()));\n\n  mod_config()->verbose = false;\n  mod_config()->daemon = false;\n\n  mod_config()->server_name = \"shrpx spdylay/\" SPDYLAY_VERSION;\n  set_config_str(&mod_config()->host, \"0.0.0.0\");\n  mod_config()->port = 3000;\n  mod_config()->private_key_file = 0;\n  mod_config()->private_key_passwd = 0;\n  mod_config()->cert_file = 0;\n\n  // Read timeout for SPDY upstream connection\n  mod_config()->spdy_upstream_read_timeout.tv_sec = 180;\n  mod_config()->spdy_upstream_read_timeout.tv_usec = 0;\n\n  // Read timeout for non-SPDY upstream connection\n  mod_config()->upstream_read_timeout.tv_sec = 180;\n  mod_config()->upstream_read_timeout.tv_usec = 0;\n\n  // Write timeout for SPDY/non-SPDY upstream connection\n  mod_config()->upstream_write_timeout.tv_sec = 60;\n  mod_config()->upstream_write_timeout.tv_usec = 0;\n\n  // Read/Write timeouts for downstream connection\n  mod_config()->downstream_read_timeout.tv_sec = 900;\n  mod_config()->downstream_read_timeout.tv_usec = 0;\n  mod_config()->downstream_write_timeout.tv_sec = 60;\n  mod_config()->downstream_write_timeout.tv_usec = 0;\n\n  // Timeout for pooled (idle) connections\n  mod_config()->downstream_idle_read_timeout.tv_sec = 60;\n\n  // window bits for SPDY upstream/downstream connection. 2**16 =\n  // 64KiB, which is SPDY/3 default.\n  mod_config()->spdy_upstream_window_bits = 16;\n  mod_config()->spdy_downstream_window_bits = 16;\n  // SPDY/3.1 has connection-level flow control. The default window\n  // size is 64KB.\n  mod_config()->spdy_upstream_connection_window_bits = 16;\n  mod_config()->spdy_downstream_connection_window_bits = 16;\n\n  mod_config()->spdy_upstream_no_tls = false;\n  mod_config()->spdy_upstream_version = SPDYLAY_PROTO_SPDY3_1;\n  mod_config()->spdy_downstream_no_tls = false;\n  mod_config()->spdy_downstream_version = SPDYLAY_PROTO_SPDY3_1;\n\n  set_config_str(&mod_config()->downstream_host, \"127.0.0.1\");\n  mod_config()->downstream_port = 80;\n  mod_config()->downstream_hostport = 0;\n  mod_config()->downstream_addrlen = 0;\n\n  mod_config()->num_worker = 1;\n  mod_config()->spdy_max_concurrent_streams = 100;\n  mod_config()->add_x_forwarded_for = false;\n  mod_config()->no_via = false;\n  mod_config()->accesslog = false;\n  set_config_str(&mod_config()->conf_path, \"/etc/shrpx/shrpx.conf\");\n  mod_config()->syslog = false;\n  mod_config()->syslog_facility = LOG_DAEMON;\n  mod_config()->use_syslog = false;\n  // Default accept() backlog\n  mod_config()->backlog = 256;\n  mod_config()->ciphers = 0;\n  mod_config()->honor_cipher_order = false;\n  mod_config()->spdy_proxy = false;\n  mod_config()->spdy_bridge = false;\n  mod_config()->client_proxy = false;\n  mod_config()->client = false;\n  mod_config()->client_mode = false;\n  mod_config()->insecure = false;\n  mod_config()->cacert = 0;\n  mod_config()->pid_file = 0;\n  mod_config()->uid = 0;\n  mod_config()->gid = 0;\n  mod_config()->backend_ipv4 = false;\n  mod_config()->backend_ipv6 = false;\n  mod_config()->tty = isatty(fileno(stderr));\n  mod_config()->cert_tree = 0;\n  mod_config()->downstream_http_proxy_userinfo = 0;\n  mod_config()->downstream_http_proxy_host = 0;\n  mod_config()->downstream_http_proxy_port = 0;\n  mod_config()->downstream_http_proxy_addrlen = 0;\n  mod_config()->rate_limit_cfg = 0;\n  mod_config()->read_rate = 1024*1024;\n  mod_config()->read_burst = 4*1024*1024;\n  mod_config()->write_rate = 0;\n  mod_config()->write_burst = 0;\n  mod_config()->worker_read_rate = 0;\n  mod_config()->worker_read_burst = 0;\n  mod_config()->worker_write_rate = 0;\n  mod_config()->worker_write_burst = 0;\n  mod_config()->verify_client = false;\n  mod_config()->verify_client_cacert = 0;\n  mod_config()->client_private_key_file = 0;\n  mod_config()->client_cert_file = 0;\n}\n} // namespace\n\nnamespace {\nsize_t get_rate_limit(size_t rate_limit)\n{\n  if(rate_limit == 0) {\n    return EV_RATE_LIMIT_MAX;\n  } else {\n    return rate_limit;\n  }\n}\n} // namespace\n\nnamespace {\nvoid print_version(std::ostream& out)\n{\n  out << get_config()->server_name << std::endl;\n}\n} // namespace\n\nnamespace {\nvoid print_usage(std::ostream& out)\n{\n  out << \"Usage: shrpx [-Dh] [-s|--client|-p] [-b <HOST,PORT>]\\n\"\n      << \"             [-f <HOST,PORT>] [-n <CORES>] [-c <NUM>] [-L <LEVEL>]\\n\"\n      << \"             [OPTIONS...] [<PRIVATE_KEY> <CERT>]\\n\"\n      << \"\\n\"\n      << \"A reverse proxy for SPDY/HTTPS.\\n\"\n      << std::endl;\n}\n} // namespace\n\nnamespace {\nvoid print_help(std::ostream& out)\n{\n  print_usage(out);\n  out << \"Positional arguments:\\n\"\n      << \"    <PRIVATE_KEY>      Set path to server's private key. Required\\n\"\n      << \"                       unless either -p or --client is specified.\\n\"\n      << \"    <CERT>             Set path to server's certificate. Required\\n\"\n      << \"                       unless either -p or --client is specified.\\n\"\n      << \"\\n\"\n      << \"OPTIONS:\\n\"\n      << \"\\n\"\n      << \"  Connections:\\n\"\n      << \"    -b, --backend=<HOST,PORT>\\n\"\n      << \"                       Set backend host and port.\\n\"\n      << \"                       Default: '\"\n      << get_config()->downstream_host << \",\"\n      << get_config()->downstream_port << \"'\\n\"\n      << \"    -f, --frontend=<HOST,PORT>\\n\"\n      << \"                       Set frontend host and port.\\n\"\n      << \"                       Default: '\"\n      << get_config()->host << \",\" << get_config()->port << \"'\\n\"\n      << \"    --backlog=<NUM>    Set listen backlog size.\\n\"\n      << \"                       Default: \"\n      << get_config()->backlog << \"\\n\"\n      << \"    --backend-ipv4     Resolve backend hostname to IPv4 address\\n\"\n      << \"                       only.\\n\"\n      << \"    --backend-ipv6     Resolve backend hostname to IPv6 address\\n\"\n      << \"                       only.\\n\"\n      << \"\\n\"\n      << \"  Performance:\\n\"\n      << \"    -n, --workers=<CORES>\\n\"\n      << \"                       Set the number of worker threads.\\n\"\n      << \"                       Default: \"\n      << get_config()->num_worker << \"\\n\"\n      << \"    --read-rate=<RATE> Set maximum average read rate on frontend\\n\"\n      << \"                       connection. Setting 0 to this option means\\n\"\n      << \"                       read rate is unlimited.\\n\"\n      << \"                       Default: \"\n      << get_config()->read_rate << \"\\n\"\n      << \"    --read-burst=<SIZE>\\n\"\n      << \"                       Set maximum read burst size on frontend\\n\"\n      << \"                       connection. Setting 0 to this option means\\n\"\n      << \"                       read burst size is unlimited.\\n\"\n      << \"                       Default: \"\n      << get_config()->read_burst << \"\\n\"\n      << \"    --write-rate=<RATE>\\n\"\n      << \"                       Set maximum average write rate on frontend\\n\"\n      << \"                       connection. Setting 0 to this option means\\n\"\n      << \"                       write rate is unlimited.\\n\"\n      << \"                       Default: \"\n      << get_config()->write_rate << \"\\n\"\n      << \"    --write-burst=<SIZE>\\n\"\n      << \"                       Set maximum write burst size on frontend\\n\"\n      << \"                       connection. Setting 0 to this option means\\n\"\n      << \"                       write burst size is unlimited.\\n\"\n      << \"                       Default: \"\n      << get_config()->write_burst << \"\\n\"\n      << \"  --worker-read-rate=<RATE>\\n\"\n      << \"                     Set maximum average read rate on frontend\\n\"\n      << \"                     connection per worker. Setting 0 to this\\n\"\n      << \"                     option means read rate is unlimited.\\n\"\n      << \"                     Default: \"\n      << get_config()->worker_read_rate << \"\\n\"\n      << \"  --worker-read-burst=<SIZE>\\n\"\n      << \"                     Set maximum read burst size on frontend\\n\"\n      << \"                     connection per worker. Setting 0 to this\\n\"\n      << \"                     option means read burst size is unlimited.\\n\"\n      << \"                     Default: \"\n      << get_config()->worker_read_burst << \"\\n\"\n      << \"  --worker-write-rate=<RATE>\\n\"\n      << \"                     Set maximum average write rate on frontend\\n\"\n      << \"                     connection per worker. Setting 0 to this\\n\"\n      << \"                     option means write rate is unlimited.\\n\"\n      << \"                     Default: \"\n      << get_config()->worker_write_rate << \"\\n\"\n      << \"  --worker-write-burst=<SIZE>\\n\"\n      << \"                     Set maximum write burst size on frontend\\n\"\n      << \"                     connection per worker. Setting 0 to this\\n\"\n      << \"                     option means write burst size is unlimited.\\n\"\n      << \"                     Default: \"\n      << get_config()->worker_write_burst << \"\\n\"\n      << \"\\n\"\n      << \"  Timeout:\\n\"\n      << \"    --frontend-spdy-read-timeout=<SEC>\\n\"\n      << \"                       Specify read timeout for SPDY frontend\\n\"\n      << \"                       connection. Default: \"\n      << get_config()->spdy_upstream_read_timeout.tv_sec << \"\\n\"\n      << \"    --frontend-read-timeout=<SEC>\\n\"\n      << \"                       Specify read timeout for non-SPDY frontend\\n\"\n      << \"                       connection. Default: \"\n      << get_config()->upstream_read_timeout.tv_sec << \"\\n\"\n      << \"    --frontend-write-timeout=<SEC>\\n\"\n      << \"                       Specify write timeout for both SPDY and\\n\"\n      << \"                       non-SPDY frontends.\\n\"\n      << \"                       connection. Default: \"\n      << get_config()->upstream_write_timeout.tv_sec << \"\\n\"\n      << \"    --backend-read-timeout=<SEC>\\n\"\n      << \"                       Specify read timeout for backend connection.\\n\"\n      << \"                       Default: \"\n      << get_config()->downstream_read_timeout.tv_sec << \"\\n\"\n      << \"    --backend-write-timeout=<SEC>\\n\"\n      << \"                       Specify write timeout for backend\\n\"\n      << \"                       connection. Default: \"\n      << get_config()->downstream_write_timeout.tv_sec << \"\\n\"\n      << \"    --backend-keep-alive-timeout=<SEC>\\n\"\n      << \"                       Specify keep-alive timeout for backend\\n\"\n      << \"                       connection. Default: \"\n      << get_config()->downstream_idle_read_timeout.tv_sec << \"\\n\"\n      << \"    --backend-http-proxy-uri=<URI>\\n\"\n      << \"                       Specify proxy URI in the form\\n\"\n      << \"                       http://[<USER>:<PASS>@]<PROXY>:<PORT>. If\\n\"\n      << \"                       a proxy requires authentication, specify\\n\"\n      << \"                       <USER> and <PASS>. Note that they must be\\n\"\n      << \"                       properly percent-encoded. This proxy is used\\n\"\n      << \"                       when the backend connection is SPDY. First,\\n\"\n      << \"                       make a CONNECT request to the proxy and\\n\"\n      << \"                       it connects to the backend on behalf of\\n\"\n      << \"                       shrpx. This forms tunnel. After that, shrpx\\n\"\n      << \"                       performs SSL/TLS handshake with the\\n\"\n      << \"                       downstream through the tunnel. The timeouts\\n\"\n      << \"                       when connecting and making CONNECT request\\n\"\n      << \"                       can be specified by --backend-read-timeout\\n\"\n      << \"                       and --backend-write-timeout options.\\n\"\n      << \"\\n\"\n      << \"  SSL/TLS:\\n\"\n      << \"    --ciphers=<SUITE>  Set allowed cipher list. The format of the\\n\"\n      << \"                       string is described in OpenSSL ciphers(1).\\n\"\n      << \"                       If this option is used, --honor-cipher-order\\n\"\n      << \"                       is implicitly enabled.\\n\"\n      << \"    --honor-cipher-order\\n\"\n      << \"                       Honor server cipher order, giving the\\n\"\n      << \"                       ability to mitigate BEAST attacks.\\n\"\n      << \"    -k, --insecure     When used with -p or --client, don't verify\\n\"\n      << \"                       backend server's certificate.\\n\"\n      << \"    --cacert=<PATH>    When used with -p or --client, set path to\\n\"\n      << \"                       trusted CA certificate file.\\n\"\n      << \"                       The file must be in PEM format. It can\\n\"\n      << \"                       contain multiple certificates. If the\\n\"\n      << \"                       linked OpenSSL is configured to load system\\n\"\n      << \"                       wide certificates, they are loaded\\n\"\n      << \"                       at startup regardless of this option.\\n\"\n      << \"    --private-key-passwd-file=<FILEPATH>\\n\"\n      << \"                       Path to file that contains password for the\\n\"\n      << \"                       server's private key. If none is given and\\n\"\n      << \"                       the private key is password protected it'll\\n\"\n      << \"                       be requested interactively.\\n\"\n      << \"    --subcert=<KEYPATH>:<CERTPATH>\\n\"\n      << \"                       Specify additional certificate and private\\n\"\n      << \"                       key file. Shrpx will choose certificates\\n\"\n      << \"                       based on the hostname indicated by client\\n\"\n      << \"                       using TLS SNI extension. This option can be\\n\"\n      << \"                       used multiple times.\\n\"\n      << \"    --backend-tls-sni-field=<HOST>\\n\"\n      << \"                       Explicitly set the content of the TLS SNI\\n\"\n      << \"                       extension.  This will default to the backend\\n\"\n      << \"                       HOST name.\\n\"\n      << \"    --dh-param-file=<PATH>\\n\"\n      << \"                       Path to file that contains DH parameters in\\n\"\n      << \"                       PEM format. Without this option, DHE cipher\\n\"\n      << \"                       suites are not available.\\n\"\n      << \"    --verify-client    Require and verify client certificate.\\n\"\n      << \"    --verify-client-cacert=<PATH>\\n\"\n      << \"                       Path to file that contains CA certificates\\n\"\n      << \"                       to verify client certificate.\\n\"\n      << \"                       The file must be in PEM format. It can\\n\"\n      << \"                       contain multiple certificates.\\n\"\n      << \"    --client-private-key-file=<PATH>\\n\"\n      << \"                       Path to file that contains client private\\n\"\n      << \"                       key used in backend client authentication.\\n\"\n      << \"    --client-cert-file=<PATH>\\n\"\n      << \"                       Path to file that contains client\\n\"\n      << \"                       certificate used in backend client\\n\"\n      << \"                       authentication.\\n\"\n      << \"    --tls-proto-list=<LIST>\\n\"\n      << \"                       Comma delimited list of SSL/TLS protocol to\\n\"\n      << \"                       be enabled.\\n\"\n      << \"                       The following protocols are available:\\n\"\n      << \"                       TLSv1.2, TLSv1.1, TLSv1.0, SSLv3\\n\"\n      << \"                       The name matching is done in case-insensitive\\n\"\n      << \"                       manner.\\n\"\n      << \"                       The parameter must be delimited by a single\\n\"\n      << \"                       comma only and any white spaces are treated\\n\"\n      << \"                       as a part of protocol string.\\n\"\n      << \"                       Default: \" << DEFAULT_TLS_PROTO_LIST << \"\\n\"\n      << \"\\n\"\n      << \"  SPDY:\\n\"\n      << \"    -c, --spdy-max-concurrent-streams=<NUM>\\n\"\n      << \"                       Set the maximum number of the concurrent\\n\"\n      << \"                       streams in one SPDY session.\\n\"\n      << \"                       Default: \"\n      << get_config()->spdy_max_concurrent_streams << \"\\n\"\n      << \"    --frontend-spdy-window-bits=<N>\\n\"\n      << \"                       Sets the per-stream initial window size of\\n\"\n      << \"                       SPDY frontend connection to 2**<N>.\\n\"\n      << \"                       Default: \"\n      << get_config()->spdy_upstream_window_bits << \"\\n\"\n      << \"    --frontend-spdy-connection-window-bits=<N>\\n\"\n      << \"                       Sets the per-connection window size of SPDY\\n\"\n      << \"                       frontend connection to 2**<N>.\\n\"\n      << \"                       Default: \"\n      << get_config()->spdy_upstream_connection_window_bits << \"\\n\"\n      << \"    --frontend-spdy-no-tls\\n\"\n      << \"                       Disable SSL/TLS on frontend SPDY\\n\"\n      << \"                       connections. SPDY protocol must be specified\\n\"\n      << \"                       using --frontend-spdy-proto. This option\\n\"\n      << \"                       also disables frontend HTTP/1.1.\\n\"\n      << \"    --frontend-spdy-proto\\n\"\n      << \"                       Specify SPDY protocol used in frontend\\n\"\n      << \"                       connection if --frontend-spdy-no-tls is\\n\"\n      << \"                       used. Default: spdy/3.1\\n\"\n      << \"    --backend-spdy-window-bits=<N>\\n\"\n      << \"                       Sets the per-stream initial window size of\\n\"\n      << \"                       SPDY backend connection to 2**<N>.\\n\"\n      << \"                       Default: \"\n      << get_config()->spdy_downstream_window_bits << \"\\n\"\n      << \"    --backend-spdy-connection-window-bits=<N>\\n\"\n      << \"                       Sets the per-connection window size of SPDY\\n\"\n      << \"                       backend connection to 2**<N>.\\n\"\n      << \"                       Default: \"\n      << get_config()->spdy_downstream_connection_window_bits << \"\\n\"\n      << \"    --backend-spdy-no-tls\\n\"\n      << \"                       Disable SSL/TLS on backend SPDY connections.\\n\"\n      << \"                       SPDY protocol must be specified using\\n\"\n      << \"                       --backend-spdy-proto\\n\"\n      << \"    --backend-spdy-proto\\n\"\n      << \"                       Specify SPDY protocol used in backend\\n\"\n      << \"                       connection if --backend-spdy-no-tls is used.\\n\"\n      << \"                       Default: spdy/3.1\\n\"\n      << \"\\n\"\n      << \"  Mode:\\n\"\n      << \"    -s, --spdy-proxy   Enable secure SPDY proxy mode.\\n\"\n      << \"    --spdy-bridge      Communicate with the backend in SPDY. Thus\\n\"\n      << \"                       the incoming SPDY/HTTPS connections are\\n\"\n      << \"                       converted to SPDY connection and relayed to\\n\"\n      << \"                       the backend. See --backend-http-proxy-uri\\n\"\n      << \"                       option if you are behind the proxy and want\\n\"\n      << \"                       to connect to the outside SPDY proxy.\\n\"\n      << \"    --client           Instead of accepting SPDY/HTTPS connection,\\n\"\n      << \"                       accept HTTP connection and communicate with\\n\"\n      << \"                       backend server in SPDY. To use shrpx as\\n\"\n      << \"                       a forward proxy, use -p option instead.\\n\"\n      << \"    -p, --client-proxy Like --client option, but it also requires\\n\"\n      << \"                       the request path from frontend must be\\n\"\n      << \"                       an absolute URI, suitable for use as a\\n\"\n      << \"                       forward proxy.\\n\"\n      << \"\\n\"\n      << \"  Logging:\\n\"\n      << \"    -L, --log-level=<LEVEL>\\n\"\n      << \"                       Set the severity level of log output.\\n\"\n      << \"                       INFO, WARNING, ERROR and FATAL.\\n\"\n      << \"                       Default: WARNING\\n\"\n      << \"    --accesslog        Print simple accesslog to stderr.\\n\"\n      << \"    --syslog           Send log messages to syslog.\\n\"\n      << \"    --syslog-facility=<FACILITY>\\n\"\n      << \"                       Set syslog facility.\\n\"\n      << \"                       Default: \"\n      << str_syslog_facility(get_config()->syslog_facility) << \"\\n\"\n      << \"\\n\"\n      << \"  Misc:\\n\"\n      << \"    --add-x-forwarded-for\\n\"\n      << \"                       Append X-Forwarded-For header field to the\\n\"\n      << \"                       downstream request.\\n\"\n      << \"    --no-via           Don't append to Via header field. If Via\\n\"\n      << \"                       header field is received, it is left\\n\"\n      << \"                       unaltered.\\n\"\n      << \"    -D, --daemon       Run in a background. If -D is used, the\\n\"\n      << \"                       current working directory is changed to '/'.\\n\"\n      << \"    --pid-file=<PATH>  Set path to save PID of this program.\\n\"\n      << \"    --user=<USER>      Run this program as USER. This option is\\n\"\n      << \"                       intended to be used to drop root privileges.\\n\"\n      << \"    --conf=<PATH>      Load configuration from PATH.\\n\"\n      << \"                       Default: \"\n      << get_config()->conf_path << \"\\n\"\n      << \"    -v, --version      Print version and exit.\\n\"\n      << \"    -h, --help         Print this help and exit.\\n\"\n      << std::endl;\n}\n} // namespace\n\nint main(int argc, char **argv)\n{\n  Log::set_severity_level(WARNING);\n  create_config();\n  fill_default_config();\n\n  std::vector<std::pair<const char*, const char*> > cmdcfgs;\n  while(1) {\n    static int flag = 0;\n    static option long_options[] = {\n      {\"daemon\", no_argument, 0, 'D' },\n      {\"log-level\", required_argument, 0, 'L' },\n      {\"backend\", required_argument, 0, 'b' },\n      {\"spdy-max-concurrent-streams\", required_argument, 0, 'c' },\n      {\"frontend\", required_argument, 0, 'f' },\n      {\"help\", no_argument, 0, 'h' },\n      {\"insecure\", no_argument, 0, 'k' },\n      {\"workers\", required_argument, 0, 'n' },\n      {\"client-proxy\", no_argument, 0, 'p' },\n      {\"spdy-proxy\", no_argument, 0, 's' },\n      {\"version\", no_argument, 0, 'v' },\n      {\"add-x-forwarded-for\", no_argument, &flag, 1 },\n      {\"frontend-spdy-read-timeout\", required_argument, &flag, 2 },\n      {\"frontend-read-timeout\", required_argument, &flag, 3 },\n      {\"frontend-write-timeout\", required_argument, &flag, 4 },\n      {\"backend-read-timeout\", required_argument, &flag, 5 },\n      {\"backend-write-timeout\", required_argument, &flag, 6 },\n      {\"accesslog\", no_argument, &flag, 7 },\n      {\"backend-keep-alive-timeout\", required_argument, &flag, 8 },\n      {\"frontend-spdy-window-bits\", required_argument, &flag, 9 },\n      {\"pid-file\", required_argument, &flag, 10 },\n      {\"user\", required_argument, &flag, 11 },\n      {\"conf\", required_argument, &flag, 12 },\n      {\"syslog\", no_argument, &flag, 13 },\n      {\"syslog-facility\", required_argument, &flag, 14 },\n      {\"backlog\", required_argument, &flag, 15 },\n      {\"ciphers\", required_argument, &flag, 16 },\n      {\"client\", no_argument, &flag, 17 },\n      {\"backend-spdy-window-bits\", required_argument, &flag, 18 },\n      {\"cacert\", required_argument, &flag, 19 },\n      {\"backend-ipv4\", no_argument, &flag, 20 },\n      {\"backend-ipv6\", no_argument, &flag, 21 },\n      {\"private-key-passwd-file\", required_argument, &flag, 22},\n      {\"no-via\", no_argument, &flag, 23},\n      {\"subcert\", required_argument, &flag, 24},\n      {\"spdy-bridge\", no_argument, &flag, 25},\n      {\"backend-http-proxy-uri\", required_argument, &flag, 26},\n      {\"backend-spdy-no-tls\", no_argument, &flag, 27},\n      {\"backend-spdy-proto\", required_argument, &flag, 28},\n      {\"frontend-spdy-no-tls\", no_argument, &flag, 29},\n      {\"frontend-spdy-proto\", required_argument, &flag, 30},\n      {\"backend-tls-sni-field\", required_argument, &flag, 31},\n      {\"honor-cipher-order\", no_argument, &flag, 32},\n      {\"dh-param-file\", required_argument, &flag, 33},\n      {\"read-rate\", required_argument, &flag, 34},\n      {\"read-burst\", required_argument, &flag, 35},\n      {\"write-rate\", required_argument, &flag, 36},\n      {\"write-burst\", required_argument, &flag, 37},\n      {\"verify-client\", no_argument, &flag, 38},\n      {\"verify-client-cacert\", required_argument, &flag, 39},\n      {\"frontend-spdy-connection-window-bits\", required_argument, &flag, 40},\n      {\"backend-spdy-connection-window-bits\", required_argument, &flag, 41},\n      {\"tls-proto-list\", required_argument, &flag, 42},\n      {\"client-private-key-file\", required_argument, &flag, 43},\n      {\"client-cert-file\", required_argument, &flag, 44},\n      {\"worker-read-rate\", required_argument, &flag, 45},\n      {\"worker-read-burst\", required_argument, &flag, 46},\n      {\"worker-write-rate\", required_argument, &flag, 47},\n      {\"worker-write-burst\", required_argument, &flag, 48},\n      {0, 0, 0, 0 }\n    };\n    int option_index = 0;\n    int c = getopt_long(argc, argv, \"DL:b:c:f:hkn:psv\", long_options,\n                        &option_index);\n    if(c == -1) {\n      break;\n    }\n    switch(c) {\n    case 'D':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_DAEMON, \"yes\"));\n      break;\n    case 'L':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_LOG_LEVEL, optarg));\n      break;\n    case 'b':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND, optarg));\n      break;\n    case 'c':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SPDY_MAX_CONCURRENT_STREAMS,\n                                       optarg));\n      break;\n    case 'f':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND, optarg));\n      break;\n    case 'h':\n      print_help(std::cout);\n      exit(EXIT_SUCCESS);\n    case 'k':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_INSECURE, \"yes\"));\n      break;\n    case 'n':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WORKERS, optarg));\n      break;\n    case 'p':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CLIENT_PROXY, \"yes\"));\n      break;\n    case 's':\n      cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SPDY_PROXY, \"yes\"));\n      break;\n    case 'v':\n      print_version(std::cout);\n      exit(EXIT_SUCCESS);\n    case '?':\n      exit(EXIT_FAILURE);\n    case 0:\n      switch(flag) {\n      case 1:\n        // --add-x-forwarded-for\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_ADD_X_FORWARDED_FOR,\n                                         \"yes\"));\n        break;\n      case 2:\n        // --frontend-spdy-read-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_SPDY_READ_TIMEOUT,\n                                         optarg));\n        break;\n      case 3:\n        // --frontend-read-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_READ_TIMEOUT,\n                                         optarg));\n        break;\n      case 4:\n        // --frontend-write-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT,\n                                         optarg));\n        break;\n      case 5:\n        // --backend-read-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_READ_TIMEOUT,\n                                         optarg));\n        break;\n      case 6:\n        // --backend-write-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_WRITE_TIMEOUT,\n                                         optarg));\n        break;\n      case 7:\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_ACCESSLOG, \"yes\"));\n        break;\n      case 8:\n        // --backend-keep-alive-timeout\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT,\n                                         optarg));\n        break;\n      case 9:\n        // --frontend-spdy-window-bits\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS,\n                                         optarg));\n        break;\n      case 10:\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_PID_FILE, optarg));\n        break;\n      case 11:\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_USER, optarg));\n        break;\n      case 12:\n        // --conf\n        set_config_str(&mod_config()->conf_path, optarg);\n        break;\n      case 13:\n        // --syslog\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SYSLOG, \"yes\"));\n        break;\n      case 14:\n        // --syslog-facility\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SYSLOG_FACILITY, optarg));\n        break;\n      case 15:\n        // --backlog\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKLOG, optarg));\n        break;\n      case 16:\n        // --ciphers\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CIPHERS, optarg));\n        break;\n      case 17:\n        // --client\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CLIENT, \"yes\"));\n        break;\n      case 18:\n        // --backend-spdy-window-bits\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_SPDY_WINDOW_BITS,\n                                         optarg));\n        break;\n      case 19:\n        // --cacert\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CACERT, optarg));\n        break;\n      case 20:\n        // --backend-ipv4\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_IPV4, \"yes\"));\n        break;\n      case 21:\n        // --backend-ipv6\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_IPV6, \"yes\"));\n        break;\n      case 22:\n        // --private-key-passwd-file\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE,\n                                         optarg));\n        break;\n      case 23:\n        // --no-via\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_NO_VIA, \"yes\"));\n        break;\n      case 24:\n        // --subcert\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SUBCERT, optarg));\n        break;\n      case 25:\n        // --spdy-bridge\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_SPDY_BRIDGE, \"yes\"));\n        break;\n      case 26:\n        // --backend-http-proxy-uri\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_HTTP_PROXY_URI,\n                                         optarg));\n        break;\n      case 27:\n        // --backend-spdy-no-tls\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_SPDY_NO_TLS,\n                                         \"yes\"));\n        break;\n      case 28:\n        // --backend-spdy-proto\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_SPDY_PROTO,\n                                         optarg));\n        break;\n      case 29:\n        // --frontend-spdy-no-tls\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_SPDY_NO_TLS,\n                                         \"yes\"));\n        break;\n      case 30:\n        // --frontend-spdy-proto\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_FRONTEND_SPDY_PROTO,\n                                         optarg));\n        break;\n      case 31:\n        // --backend-tls-sni-field\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,\n                                         optarg));\n        break;\n      case 32:\n        // --honor-cipher-order\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_HONOR_CIPHER_ORDER,\n                                         \"yes\"));\n        break;\n      case 33:\n        // --dh-param-file\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_DH_PARAM_FILE, optarg));\n        break;\n      case 34:\n        // --read-rate\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_READ_RATE, optarg));\n        break;\n      case 35:\n        // --read-burst\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_READ_BURST, optarg));\n        break;\n      case 36:\n        // --write-rate\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WRITE_RATE, optarg));\n        break;\n      case 37:\n        // --write-burst\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WRITE_BURST, optarg));\n        break;\n      case 38:\n        // --verify-client\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_VERIFY_CLIENT, \"yes\"));\n        break;\n      case 39:\n        // --verify-client-cacert\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_VERIFY_CLIENT_CACERT,\n                                         optarg));\n        break;\n      case 40:\n        // --frontend-spdy-connection-window-bits\n        cmdcfgs.push_back(std::make_pair\n                          (SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS,\n                           optarg));\n        break;\n      case 41:\n        // --backend-spdy-connection-window-bits\n        cmdcfgs.push_back(std::make_pair\n                          (SHRPX_OPT_BACKEND_SPDY_CONNECTION_WINDOW_BITS,\n                           optarg));\n        break;\n      case 42:\n        // --tls-proto-list\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_TLS_PROTO_LIST, optarg));\n        break;\n      case 43:\n        // --client-private-key-file\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE,\n                                         optarg));\n        break;\n      case 44:\n        // --client-cert-file\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CLIENT_CERT_FILE, optarg));\n        break;\n      case 45:\n        // --worker-read-rate\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WORKER_READ_RATE, optarg));\n        break;\n      case 46:\n        // --worker-read-burst\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WORKER_READ_BURST, optarg));\n        break;\n      case 47:\n        // --worker-write-rate\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WORKER_WRITE_RATE, optarg));\n        break;\n      case 48:\n        // --worker-write-burst\n        cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WORKER_WRITE_BURST,\n                                         optarg));\n        break;\n      default:\n        break;\n      }\n      break;\n    default:\n      break;\n    }\n  }\n\n  // Initialize OpenSSL before parsing options because we create\n  // SSL_CTX there.\n  OpenSSL_add_all_algorithms();\n  SSL_load_error_strings();\n  SSL_library_init();\n  ssl::setup_ssl_lock();\n\n  if(conf_exists(get_config()->conf_path)) {\n    if(load_config(get_config()->conf_path) == -1) {\n      LOG(FATAL) << \"Failed to load configuration from \"\n                 << get_config()->conf_path;\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if(argc - optind >= 2) {\n    cmdcfgs.push_back(std::make_pair(SHRPX_OPT_PRIVATE_KEY_FILE,\n                                     argv[optind++]));\n    cmdcfgs.push_back(std::make_pair(SHRPX_OPT_CERTIFICATE_FILE,\n                                     argv[optind++]));\n  }\n\n  for(size_t i = 0, len = cmdcfgs.size(); i < len; ++i) {\n    if(parse_config(cmdcfgs[i].first, cmdcfgs[i].second) == -1) {\n      LOG(FATAL) << \"Failed to parse command-line argument.\";\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if(!get_config()->tls_proto_list) {\n    mod_config()->tls_proto_list = parse_config_str_list\n      (&mod_config()->tls_proto_list_len, DEFAULT_TLS_PROTO_LIST);\n  }\n\n  if(!get_config()->subcerts.empty()) {\n    mod_config()->cert_tree = ssl::cert_lookup_tree_new();\n  }\n\n  for(size_t i = 0; i < get_config()->subcerts.size(); ++i) {\n    const std::pair<std::string, std::string>& keycert =\n      get_config()->subcerts[i];\n    SSL_CTX *ssl_ctx = ssl::create_ssl_context(keycert.first.c_str(),\n                                               keycert.second.c_str());\n    if(ssl::cert_lookup_tree_add_cert_from_file\n       (get_config()->cert_tree, ssl_ctx, keycert.second.c_str()) == -1) {\n      LOG(FATAL) << \"Failed to add sub certificate.\";\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if(get_config()->cert_file && get_config()->private_key_file) {\n    mod_config()->default_ssl_ctx =\n      ssl::create_ssl_context(get_config()->private_key_file,\n                              get_config()->cert_file);\n    if(get_config()->cert_tree) {\n      if(ssl::cert_lookup_tree_add_cert_from_file(get_config()->cert_tree,\n                                                  get_config()->default_ssl_ctx,\n                                                  get_config()->cert_file)\n         == -1) {\n        LOG(FATAL) << \"Failed to parse command-line argument.\";\n        exit(EXIT_FAILURE);\n      }\n    }\n  }\n\n  if(get_config()->backend_ipv4 && get_config()->backend_ipv6) {\n    LOG(FATAL) << \"--backend-ipv4 and --backend-ipv6 cannot be used at the \"\n               << \"same time.\";\n    exit(EXIT_FAILURE);\n  }\n\n  if(get_config()->spdy_proxy + get_config()->spdy_bridge +\n     get_config()->client_proxy + get_config()->client > 1) {\n    LOG(FATAL) << \"--spdy-proxy, --spdy-bridge, --client-proxy and --client \"\n               << \"cannot be used at the same time.\";\n    exit(EXIT_FAILURE);\n  }\n\n  if(get_config()->client || get_config()->client_proxy) {\n    mod_config()->client_mode = true;\n  }\n\n  if(get_config()->client_mode || get_config()->spdy_bridge) {\n    mod_config()->downstream_proto = PROTO_SPDY;\n  } else {\n    mod_config()->downstream_proto = PROTO_HTTP;\n  }\n\n  if(!get_config()->client_mode && !get_config()->spdy_upstream_no_tls) {\n    if(!get_config()->private_key_file || !get_config()->cert_file) {\n      print_usage(std::cerr);\n      LOG(FATAL) << \"Too few arguments\";\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  char hostport[NI_MAXHOST+16];\n  bool downstream_ipv6_addr =\n    is_ipv6_numeric_addr(get_config()->downstream_host);\n  snprintf(hostport, sizeof(hostport), \"%s%s%s:%u\",\n           downstream_ipv6_addr ? \"[\" : \"\",\n           get_config()->downstream_host,\n           downstream_ipv6_addr ? \"]\" : \"\",\n           get_config()->downstream_port);\n  set_config_str(&mod_config()->downstream_hostport, hostport);\n\n  if(LOG_ENABLED(INFO)) {\n    LOG(INFO) << \"Resolving backend address\";\n  }\n  if(resolve_hostname(&mod_config()->downstream_addr,\n                      &mod_config()->downstream_addrlen,\n                      get_config()->downstream_host,\n                      get_config()->downstream_port,\n                      get_config()->backend_ipv4 ? AF_INET :\n                      (get_config()->backend_ipv6 ?\n                       AF_INET6 : AF_UNSPEC)) == -1) {\n    exit(EXIT_FAILURE);\n  }\n\n  if(get_config()->downstream_http_proxy_host) {\n    if(LOG_ENABLED(INFO)) {\n      LOG(INFO) << \"Resolving backend http proxy address\";\n    }\n    if(resolve_hostname(&mod_config()->downstream_http_proxy_addr,\n                        &mod_config()->downstream_http_proxy_addrlen,\n                        get_config()->downstream_http_proxy_host,\n                        get_config()->downstream_http_proxy_port,\n                        AF_UNSPEC) == -1) {\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  if(get_config()->syslog) {\n    openlog(\"shrpx\", LOG_NDELAY | LOG_NOWAIT | LOG_PID,\n            get_config()->syslog_facility);\n    mod_config()->use_syslog = true;\n  }\n\n  mod_config()->rate_limit_cfg = ev_token_bucket_cfg_new\n    (get_rate_limit(get_config()->read_rate),\n     get_rate_limit(get_config()->read_burst),\n     get_rate_limit(get_config()->write_rate),\n     get_rate_limit(get_config()->write_burst),\n     0);\n\n  mod_config()->worker_rate_limit_cfg = ev_token_bucket_cfg_new\n    (get_rate_limit(get_config()->worker_read_rate),\n     get_rate_limit(get_config()->worker_read_burst),\n     get_rate_limit(get_config()->worker_write_rate),\n     get_rate_limit(get_config()->worker_write_burst),\n     0);\n\n  struct sigaction act;\n  memset(&act, 0, sizeof(struct sigaction));\n  act.sa_handler = SIG_IGN;\n  sigaction(SIGPIPE, &act, 0);\n\n  event_loop();\n\n  ssl::teardown_ssl_lock();\n\n  return 0;\n}\n\n} // namespace shrpx\n\nint main(int argc, char **argv)\n{\n  return shrpx::main(argc, argv);\n}\n"
  },
  {
    "path": "src/shrpx.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_H\n#define SHRPX_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif // HAVE_CONFIG_H\n\n#include <cassert>\n\n#include \"shrpx_log.h\"\n\n#define DIE()                                   \\\n  assert(0);\n\n#define SHRPX_READ_WATERMARK (64*1024)\n\n#endif // SHRPX_H\n"
  },
  {
    "path": "src/shrpx_accesslog.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_accesslog.h\"\n\n#include <syslog.h>\n\n#include <ctime>\n#include <cstdio>\n#include <cstring>\n\n#include \"shrpx_config.h\"\n#include \"shrpx_downstream.h\"\n\nnamespace shrpx {\n\nnamespace {\nvoid get_datestr(char *buf)\n{\n  time_t now = time(0);\n  if(ctime_r(&now, buf) == 0) {\n    buf[0] = '\\0';\n  } else {\n    size_t len = strlen(buf);\n    if(len == 0) {\n      buf[0] = '\\0';\n    } else {\n      buf[strlen(buf)-1] = '\\0';\n    }\n  }\n}\n} // namespace\n\nvoid upstream_connect(const std::string& client_ip)\n{\n  char datestr[64];\n  get_datestr(datestr);\n  fprintf(stderr, \"%s [%s] ACCEPT\\n\", client_ip.c_str(), datestr);\n  fflush(stderr);\n  if(get_config()->use_syslog) {\n    syslog(LOG_INFO, \"%s ACCEPT\\n\", client_ip.c_str());\n  }\n}\n\nnamespace {\nconst char* status_code_color(int status_code)\n{\n  if(status_code <= 199) {\n    return \"\\033[1;36m\";\n  } else if(status_code <= 299) {\n    return \"\\033[1;32m\";\n  } else if(status_code <= 399) {\n    return \"\\033[1;34m\";\n  } else if(status_code <= 499) {\n    return \"\\033[1;31m\";\n  } else if(status_code <= 599) {\n    return \"\\033[1;35m\";\n  } else {\n    return \"\";\n  }\n}\n} // namespace\n\nvoid upstream_response(const std::string& client_ip, int status_code,\n                       Downstream *downstream)\n{\n  char datestr[64];\n  get_datestr(datestr);\n  if(downstream) {\n    fprintf(stderr, \"%s%s [%s] %d%s %d \\\"%s %s HTTP/%d.%d\\\"\\n\",\n            get_config()->tty ? status_code_color(status_code) : \"\",\n            client_ip.c_str(), datestr,\n            status_code,\n            get_config()->tty ? \"\\033[0m\" : \"\",\n            downstream->get_stream_id(),\n            downstream->get_request_method().c_str(),\n            downstream->get_request_path().c_str(),\n            downstream->get_request_major(),\n            downstream->get_request_minor());\n    fflush(stderr);\n    if(get_config()->use_syslog) {\n      syslog(LOG_INFO, \"%s %d %d \\\"%s %s HTTP/%u.%u\\\"\\n\",\n            client_ip.c_str(),\n            status_code,\n            downstream->get_stream_id(),\n            downstream->get_request_method().c_str(),\n            downstream->get_request_path().c_str(),\n            downstream->get_request_major(),\n            downstream->get_request_minor());\n    }\n  } else {\n    fprintf(stderr, \"%s%s [%s] %d%s 0 \\\"-\\\"\\n\",\n            get_config()->tty ? status_code_color(status_code) : \"\",\n            client_ip.c_str(), datestr,\n            status_code,\n            get_config()->tty ? \"\\033[0m\" : \"\");\n    if(get_config()->use_syslog) {\n      syslog(LOG_INFO, \"%s %d 0 \\\"-\\\"\\n\", client_ip.c_str(), status_code);\n    }\n    fflush(stderr);\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_accesslog.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_ACCESSLOG_H\n#define SHRPX_ACCESSLOG_H\n\n#include \"shrpx.h\"\n\n#include <stdint.h>\n\nnamespace shrpx {\n\nclass Downstream;\n\nvoid upstream_connect(const std::string& client_ip);\nvoid upstream_response(const std::string& client_ip, int status_code,\n                       Downstream *downstream);\n\n} // namespace shrpx\n\n#endif // SHRPX_LOG_H\n"
  },
  {
    "path": "src/shrpx_client_handler.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_client_handler.h\"\n\n#include <unistd.h>\n#include <cerrno>\n#include <cstring>\n\n#include \"shrpx_upstream.h\"\n#include \"shrpx_spdy_upstream.h\"\n#include \"shrpx_https_upstream.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_http_downstream_connection.h\"\n#include \"shrpx_spdy_downstream_connection.h\"\n#include \"shrpx_accesslog.h\"\n\nnamespace shrpx {\n\nnamespace {\nvoid upstream_readcb(bufferevent *bev, void *arg)\n{\n  ClientHandler *handler = static_cast<ClientHandler*>(arg);\n  int rv = handler->on_read();\n  if(rv != 0) {\n    delete handler;\n  }\n}\n} // namespace\n\nnamespace {\nvoid upstream_writecb(bufferevent *bev, void *arg)\n{\n  ClientHandler *handler = static_cast<ClientHandler*>(arg);\n  // We actually depend on write low-watermark == 0.\n  if(handler->get_outbuf_length() > 0) {\n    // Possibly because of deferred callback, we may get this callback\n    // when the output buffer is not empty.\n    return;\n  }\n  if(handler->get_should_close_after_write()) {\n    delete handler;\n    return;\n  }\n  Upstream *upstream = handler->get_upstream();\n  if(!upstream) {\n    return;\n  }\n  int rv = upstream->on_write();\n  if(rv != 0) {\n    delete handler;\n  }\n}\n} // namespace\n\nnamespace {\nvoid upstream_eventcb(bufferevent *bev, short events, void *arg)\n{\n  ClientHandler *handler = static_cast<ClientHandler*>(arg);\n  bool finish = false;\n  if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, handler) << \"EOF\";\n    }\n    finish = true;\n  }\n  if(events & BEV_EVENT_ERROR) {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, handler) << \"Network error: \"\n                          << evutil_socket_error_to_string\n        (EVUTIL_SOCKET_ERROR());\n    }\n    finish = true;\n  }\n  if(events & BEV_EVENT_TIMEOUT) {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, handler) << \"Time out\";\n    }\n    finish = true;\n  }\n  if(finish) {\n    delete handler;\n  } else {\n    if(events & BEV_EVENT_CONNECTED) {\n      handler->set_tls_handshake(true);\n      if(LOG_ENABLED(INFO)) {\n        CLOG(INFO, handler) << \"SSL/TLS handshake completed\";\n      }\n      handler->set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);\n      handler->validate_next_proto();\n      if(LOG_ENABLED(INFO)) {\n        if(SSL_session_reused(handler->get_ssl())) {\n          CLOG(INFO, handler) << \"SSL/TLS session reused\";\n        }\n      }\n      // At this point, input buffer is already filled with some\n      // bytes.  The read callback is not called until new data\n      // come. So consume input buffer here.\n      handler->get_upstream()->on_read();\n    }\n  }\n}\n} // namespace\n\nClientHandler::ClientHandler(bufferevent *bev,\n                             bufferevent_rate_limit_group *rate_limit_group,\n                             int fd, SSL *ssl, const char *ipaddr)\n  : ipaddr_(ipaddr),\n    bev_(bev),\n    ssl_(ssl),\n    reneg_shutdown_timerev_(0),\n    upstream_(0),\n    spdy_(0),\n    fd_(fd),\n    should_close_after_write_(false),\n    tls_handshake_(false),\n    tls_renegotiation_(false)\n{\n  int rv;\n\n  rv = bufferevent_set_rate_limit(bev_, get_config()->rate_limit_cfg);\n  if(rv == -1) {\n    CLOG(FATAL, this) << \"bufferevent_set_rate_limit() failed\";\n  }\n\n  rv = bufferevent_add_to_rate_limit_group(bev_, rate_limit_group);\n  if(rv == -1) {\n    CLOG(FATAL, this) << \"bufferevent_add_to_rate_limit_group() failed\";\n  }\n\n  bufferevent_enable(bev_, EV_READ | EV_WRITE);\n  bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);\n  set_upstream_timeouts(&get_config()->upstream_read_timeout,\n                        &get_config()->upstream_write_timeout);\n  if(ssl_) {\n    SSL_set_app_data(ssl_, reinterpret_cast<char*>(this));\n    set_bev_cb(0, upstream_writecb, upstream_eventcb);\n  } else {\n    if(get_config()->client_mode) {\n      // Client mode\n      upstream_ = new HttpsUpstream(this);\n    } else {\n      // no-TLS SPDY\n      upstream_ = new SpdyUpstream(get_config()->spdy_upstream_version, this);\n    }\n    set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);\n  }\n}\n\nClientHandler::~ClientHandler()\n{\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, this) << \"Deleting\";\n  }\n\n  if(reneg_shutdown_timerev_) {\n    event_free(reneg_shutdown_timerev_);\n  }\n\n  if(ssl_) {\n    SSL_set_app_data(ssl_, 0);\n    SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);\n    SSL_shutdown(ssl_);\n  }\n\n  bufferevent_remove_from_rate_limit_group(bev_);\n\n  bufferevent_disable(bev_, EV_READ | EV_WRITE);\n  bufferevent_free(bev_);\n\n  if(ssl_) {\n    SSL_free(ssl_);\n  }\n  shutdown(fd_, SHUT_WR);\n  close(fd_);\n  delete upstream_;\n  for(std::set<DownstreamConnection*>::iterator i = dconn_pool_.begin();\n      i != dconn_pool_.end(); ++i) {\n    delete *i;\n  }\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, this) << \"Deleted\";\n  }\n}\n\nUpstream* ClientHandler::get_upstream()\n{\n  return upstream_;\n}\n\nbufferevent* ClientHandler::get_bev() const\n{\n  return bev_;\n}\n\nevent_base* ClientHandler::get_evbase() const\n{\n  return bufferevent_get_base(bev_);\n}\n\nvoid ClientHandler::set_bev_cb\n(bufferevent_data_cb readcb, bufferevent_data_cb writecb,\n bufferevent_event_cb eventcb)\n{\n  bufferevent_setcb(bev_, readcb, writecb, eventcb, this);\n}\n\nvoid ClientHandler::set_upstream_timeouts(const timeval *read_timeout,\n                                          const timeval *write_timeout)\n{\n  bufferevent_set_timeouts(bev_, read_timeout, write_timeout);\n}\n\nint ClientHandler::validate_next_proto()\n{\n  const unsigned char *next_proto = 0;\n  unsigned int next_proto_len;\n  SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);\n  if(next_proto) {\n    if(LOG_ENABLED(INFO)) {\n      std::string proto(next_proto, next_proto+next_proto_len);\n      CLOG(INFO, this) << \"The negotiated next protocol: \" << proto;\n    }\n    uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len);\n    if(version) {\n      SpdyUpstream *spdy_upstream = new SpdyUpstream(version, this);\n      upstream_ = spdy_upstream;\n      return 0;\n    }\n  } else {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, this) << \"No proto negotiated.\";\n    }\n  }\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, this) << \"Use HTTP/1.1\";\n  }\n  HttpsUpstream *https_upstream = new HttpsUpstream(this);\n  upstream_ = https_upstream;\n  return 0;\n}\n\nint ClientHandler::on_read()\n{\n  return upstream_->on_read();\n}\n\nint ClientHandler::on_event()\n{\n  return upstream_->on_event();\n}\n\nconst std::string& ClientHandler::get_ipaddr() const\n{\n  return ipaddr_;\n}\n\nbool ClientHandler::get_should_close_after_write() const\n{\n  return should_close_after_write_;\n}\n\nvoid ClientHandler::set_should_close_after_write(bool f)\n{\n  should_close_after_write_ = f;\n}\n\nvoid ClientHandler::pool_downstream_connection(DownstreamConnection *dconn)\n{\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, this) << \"Pooling downstream connection DCONN:\" << dconn;\n  }\n  dconn_pool_.insert(dconn);\n}\n\nvoid ClientHandler::remove_downstream_connection(DownstreamConnection *dconn)\n{\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, this) << \"Removing downstream connection DCONN:\" << dconn\n                     << \" from pool\";\n  }\n  dconn_pool_.erase(dconn);\n}\n\nDownstreamConnection* ClientHandler::get_downstream_connection()\n{\n  if(dconn_pool_.empty()) {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, this) << \"Downstream connection pool is empty.\"\n                       << \" Create new one\";\n    }\n    if(spdy_) {\n      return new SpdyDownstreamConnection(this);\n    } else {\n      return new HttpDownstreamConnection(this);\n    }\n  } else {\n    DownstreamConnection *dconn = *dconn_pool_.begin();\n    dconn_pool_.erase(dconn);\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, this) << \"Reuse downstream connection DCONN:\" << dconn\n                       << \" from pool\";\n    }\n    return dconn;\n  }\n}\n\nsize_t ClientHandler::get_outbuf_length()\n{\n  return evbuffer_get_length(bufferevent_get_output(bev_));\n}\n\nSSL* ClientHandler::get_ssl() const\n{\n  return ssl_;\n}\n\nvoid ClientHandler::set_spdy_session(SpdySession *spdy)\n{\n  spdy_ = spdy;\n}\n\nSpdySession* ClientHandler::get_spdy_session() const\n{\n  return spdy_;\n}\n\n\n\nvoid ClientHandler::set_tls_handshake(bool f)\n{\n  tls_handshake_ = f;\n}\n\nbool ClientHandler::get_tls_handshake() const\n{\n  return tls_handshake_;\n}\n\nnamespace {\nvoid shutdown_cb(evutil_socket_t fd, short what, void *arg)\n{\n  ClientHandler *handler = static_cast<ClientHandler*>(arg);\n\n  if(LOG_ENABLED(INFO)) {\n    CLOG(INFO, handler) << \"Close connection due to TLS renegotiation\";\n  }\n\n  delete handler;\n}\n} // namespace\n\nvoid ClientHandler::set_tls_renegotiation(bool f)\n{\n  if(tls_renegotiation_ == false) {\n    if(LOG_ENABLED(INFO)) {\n      CLOG(INFO, this) << \"TLS renegotiation detected. \"\n                       << \"Start shutdown timer now.\";\n    }\n\n    reneg_shutdown_timerev_ = evtimer_new(get_evbase(), shutdown_cb, this);\n    event_priority_set(reneg_shutdown_timerev_, 0);\n\n    timeval timeout = {0, 0};\n\n    // TODO What to do if this failed?\n    evtimer_add(reneg_shutdown_timerev_, &timeout);\n  }\n\n  tls_renegotiation_ = f;\n}\n\nbool ClientHandler::get_tls_renegotiation() const\n{\n  return tls_renegotiation_;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_client_handler.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_CLIENT_HANDLER_H\n#define SHRPX_CLIENT_HANDLER_H\n\n#include \"shrpx.h\"\n\n#include <set>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\n#include <openssl/ssl.h>\n\nnamespace shrpx {\n\nclass Upstream;\nclass DownstreamConnection;\nclass SpdySession;\n\nclass ClientHandler {\npublic:\n  ClientHandler(bufferevent *bev,\n                bufferevent_rate_limit_group *rate_limit_group,\n                int fd, SSL *ssl, const char *ipaddr);\n  ~ClientHandler();\n  int on_read();\n  int on_event();\n  bufferevent* get_bev() const;\n  event_base* get_evbase() const;\n  void set_bev_cb(bufferevent_data_cb readcb, bufferevent_data_cb writecb,\n                  bufferevent_event_cb eventcb);\n  void set_upstream_timeouts(const timeval *read_timeout,\n                             const timeval *write_timeout);\n  int validate_next_proto();\n  const std::string& get_ipaddr() const;\n  bool get_should_close_after_write() const;\n  void set_should_close_after_write(bool f);\n  Upstream* get_upstream();\n\n  void pool_downstream_connection(DownstreamConnection *dconn);\n  void remove_downstream_connection(DownstreamConnection *dconn);\n  DownstreamConnection* get_downstream_connection();\n  size_t get_outbuf_length();\n  SSL* get_ssl() const;\n  void set_spdy_session(SpdySession *spdy);\n  SpdySession* get_spdy_session() const;\n  void set_tls_handshake(bool f);\n  bool get_tls_handshake() const;\n  void set_tls_renegotiation(bool f);\n  bool get_tls_renegotiation() const;\nprivate:\n  std::set<DownstreamConnection*> dconn_pool_;\n  std::string ipaddr_;\n  bufferevent *bev_;\n  SSL *ssl_;\n  event *reneg_shutdown_timerev_;\n  Upstream *upstream_;\n  // Shared SPDY session for each thread. NULL if backend is not\n  // SPDY. Not deleted by this object.\n  SpdySession *spdy_;\n  int fd_;\n  bool should_close_after_write_;\n  bool tls_handshake_;\n  bool tls_renegotiation_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_CLIENT_HANDLER_H\n"
  },
  {
    "path": "src/shrpx_config.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_config.h\"\n\n#include <pwd.h>\n#include <netdb.h>\n#include <syslog.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <cstring>\n#include <cerrno>\n#include <limits>\n#include <fstream>\n\n#include <spdylay/spdylay.h>\n\n#include \"shrpx_log.h\"\n#include \"shrpx_ssl.h\"\n#include \"shrpx_http.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nconst char SHRPX_OPT_PRIVATE_KEY_FILE[] = \"private-key-file\";\nconst char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = \"private-key-passwd-file\";\nconst char SHRPX_OPT_CERTIFICATE_FILE[] = \"certificate-file\";\nconst char SHRPX_OPT_DH_PARAM_FILE[] = \"dh-param-file\";\nconst char SHRPX_OPT_SUBCERT[] = \"subcert\";\n\nconst char SHRPX_OPT_BACKEND[] = \"backend\";\nconst char SHRPX_OPT_FRONTEND[] = \"frontend\";\nconst char SHRPX_OPT_WORKERS[] = \"workers\";\nconst char\nSHRPX_OPT_SPDY_MAX_CONCURRENT_STREAMS[] = \"spdy-max-concurrent-streams\";\nconst char SHRPX_OPT_LOG_LEVEL[] = \"log-level\";\nconst char SHRPX_OPT_DAEMON[] = \"daemon\";\nconst char SHRPX_OPT_SPDY_PROXY[] = \"spdy-proxy\";\nconst char SHRPX_OPT_SPDY_BRIDGE[] = \"spdy-bridge\";\nconst char SHRPX_OPT_CLIENT_PROXY[] = \"client-proxy\";\nconst char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = \"add-x-forwarded-for\";\nconst char SHRPX_OPT_NO_VIA[] = \"no-via\";\nconst char\nSHRPX_OPT_FRONTEND_SPDY_READ_TIMEOUT[] = \"frontend-spdy-read-timeout\";\nconst char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = \"frontend-read-timeout\";\nconst char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = \"frontend-write-timeout\";\nconst char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = \"backend-read-timeout\";\nconst char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = \"backend-write-timeout\";\nconst char SHRPX_OPT_ACCESSLOG[] = \"accesslog\";\nconst char\nSHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] = \"backend-keep-alive-timeout\";\nconst char SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS[] = \"frontend-spdy-window-bits\";\nconst char SHRPX_OPT_BACKEND_SPDY_WINDOW_BITS[] = \"backend-spdy-window-bits\";\nconst char SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS[] =\n  \"frontend-spdy-connection-window-bits\";\nconst char SHRPX_OPT_BACKEND_SPDY_CONNECTION_WINDOW_BITS[] =\n  \"backend-spdy-connection-window-bits\";\nconst char SHRPX_OPT_FRONTEND_SPDY_NO_TLS[] = \"frontend-spdy-no-tls\";\nconst char SHRPX_OPT_FRONTEND_SPDY_PROTO[] = \"frontend-spdy-proto\";\nconst char SHRPX_OPT_BACKEND_SPDY_NO_TLS[] = \"backend-spdy-no-tls\";\nconst char SHRPX_OPT_BACKEND_SPDY_PROTO[] = \"backend-spdy-proto\";\nconst char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = \"backend-tls-sni-field\";\nconst char SHRPX_OPT_PID_FILE[] = \"pid-file\";\nconst char SHRPX_OPT_USER[] = \"user\";\nconst char SHRPX_OPT_SYSLOG[] = \"syslog\";\nconst char SHRPX_OPT_SYSLOG_FACILITY[] = \"syslog-facility\";\nconst char SHRPX_OPT_BACKLOG[] = \"backlog\";\nconst char SHRPX_OPT_CIPHERS[] = \"ciphers\";\nconst char SHRPX_OPT_HONOR_CIPHER_ORDER[] = \"honor-cipher-order\";\nconst char SHRPX_OPT_CLIENT[] = \"client\";\nconst char SHRPX_OPT_INSECURE[] = \"insecure\";\nconst char SHRPX_OPT_CACERT[] = \"cacert\";\nconst char SHRPX_OPT_BACKEND_IPV4[] = \"backend-ipv4\";\nconst char SHRPX_OPT_BACKEND_IPV6[] = \"backend-ipv6\";\nconst char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = \"backend-http-proxy-uri\";\nconst char SHRPX_OPT_READ_RATE[] = \"read-rate\";\nconst char SHRPX_OPT_READ_BURST[] = \"read-burst\";\nconst char SHRPX_OPT_WRITE_RATE[] = \"write-rate\";\nconst char SHRPX_OPT_WRITE_BURST[] = \"write-burst\";\nconst char SHRPX_OPT_WORKER_READ_RATE[] = \"worker-read-rate\";\nconst char SHRPX_OPT_WORKER_READ_BURST[] = \"worker-read-burst\";\nconst char SHRPX_OPT_WORKER_WRITE_RATE[] = \"worker-write-rate\";\nconst char SHRPX_OPT_WORKER_WRITE_BURST[] = \"worker-write-burst\";\nconst char SHRPX_OPT_TLS_PROTO_LIST[] = \"tls-proto-list\";\nconst char SHRPX_OPT_VERIFY_CLIENT[] = \"verify-client\";\nconst char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = \"verify-client-cacert\";\nconst char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = \"client-private-key-file\";\nconst char SHRPX_OPT_CLIENT_CERT_FILE[] = \"client-cert-file\";\n\nnamespace {\nConfig *config = 0;\n} // namespace\n\nconst Config* get_config()\n{\n  return config;\n}\n\nConfig* mod_config()\n{\n  return config;\n}\n\nvoid create_config()\n{\n  config = new Config();\n}\n\nnamespace {\nint split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,\n                    const char *hostport)\n{\n  // host and port in |hostport| is separated by single ','.\n  const char *p = strchr(hostport, ',');\n  if(!p) {\n    LOG(ERROR) << \"Invalid host, port: \" << hostport;\n    return -1;\n  }\n  size_t len = p-hostport;\n  if(hostlen < len+1) {\n    LOG(ERROR) << \"Hostname too long: \" << hostport;\n    return -1;\n  }\n  memcpy(host, hostport, len);\n  host[len] = '\\0';\n\n  errno = 0;\n  unsigned long d = strtoul(p+1, 0, 10);\n  if(errno == 0 && 1 <= d && d <= std::numeric_limits<uint16_t>::max()) {\n    *port_ptr = d;\n    return 0;\n  } else {\n    LOG(ERROR) << \"Port is invalid: \" << p+1;\n    return -1;\n  }\n}\n} // namespace\n\nnamespace {\nbool is_secure(const char *filename)\n{\n  struct stat buf;\n  int rv = stat(filename, &buf);\n  if (rv == 0) {\n    if ((buf.st_mode & S_IRWXU) &&\n        !(buf.st_mode & S_IRWXG) &&\n        !(buf.st_mode & S_IRWXO)) {\n      return true;\n    }\n  }\n\n  return false;\n}\n} // namespace\n\nstd::string read_passwd_from_file(const char *filename)\n{\n  std::string line;\n\n  if (!is_secure(filename)) {\n    LOG(ERROR) << \"Private key passwd file \" << filename\n               << \" has insecure mode.\";\n    return line;\n  }\n\n  std::ifstream in(filename, std::ios::binary);\n  if(!in) {\n    LOG(ERROR) << \"Could not open key passwd file \" << filename;\n    return line;\n  }\n\n  std::getline(in, line);\n  return line;\n}\n\nvoid set_config_str(char **destp, const char *val)\n{\n  if(*destp) {\n    free(*destp);\n  }\n  *destp = strdup(val);\n}\n\nnamespace {\n// Parses |optarg| as SPDY NPN protocol string and returns SPDY\n// protocol version number. This function returns -1 on error.\nint parse_spdy_proto(const char *optarg)\n{\n  size_t len = strlen(optarg);\n  const unsigned char *proto;\n  proto = reinterpret_cast<const unsigned char*>(optarg);\n  uint16_t version = spdylay_npn_get_version(proto, len);\n  if(!version) {\n    LOG(ERROR) << \"Unsupported SPDY version: \" << optarg;\n    return -1;\n  }\n  return version;\n}\n} // namespace\n\nchar** parse_config_str_list(size_t *outlen, const char *s)\n{\n  size_t len = 1;\n  for(const char *first = s, *p = 0; (p = strchr(first, ','));\n      ++len, first = p + 1);\n  char **list = new char*[len];\n  char *first = strdup(s);\n  len = 0;\n  for(;;) {\n    char *p = strchr(first, ',');\n    if(!p) {\n      break;\n    }\n    list[len++] = first;\n    *p = '\\0';\n    first = p + 1;\n  }\n  list[len++] = first;\n  *outlen = len;\n  return list;\n}\n\nint parse_config(const char *opt, const char *optarg)\n{\n  char host[NI_MAXHOST];\n  uint16_t port;\n  if(util::strieq(opt, SHRPX_OPT_BACKEND)) {\n    if(split_host_port(host, sizeof(host), &port, optarg) == -1) {\n      return -1;\n    } else {\n      set_config_str(&mod_config()->downstream_host, host);\n      mod_config()->downstream_port = port;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND)) {\n    if(split_host_port(host, sizeof(host), &port, optarg) == -1) {\n      return -1;\n    } else {\n      set_config_str(&mod_config()->host, host);\n      mod_config()->port = port;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_WORKERS)) {\n    mod_config()->num_worker = strtol(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_SPDY_MAX_CONCURRENT_STREAMS)) {\n    mod_config()->spdy_max_concurrent_streams = strtol(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) {\n    if(Log::set_severity_level_by_name(optarg) == -1) {\n      LOG(ERROR) << \"Invalid severity level: \" << optarg;\n      return -1;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_DAEMON)) {\n    mod_config()->daemon = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_SPDY_PROXY)) {\n    mod_config()->spdy_proxy = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_SPDY_BRIDGE)) {\n    mod_config()->spdy_bridge = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) {\n    mod_config()->client_proxy = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) {\n    mod_config()->add_x_forwarded_for = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_NO_VIA)) {\n    mod_config()->no_via = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_READ_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->spdy_upstream_read_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->upstream_read_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->upstream_write_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->downstream_read_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->downstream_write_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_ACCESSLOG)) {\n    mod_config()->accesslog = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) {\n    timeval tv = {strtol(optarg, 0, 10), 0};\n    mod_config()->downstream_idle_read_timeout = tv;\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS) ||\n            util::strieq(opt, SHRPX_OPT_BACKEND_SPDY_WINDOW_BITS)) {\n    size_t *resp;\n    const char *optname;\n    if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS)) {\n      resp = &mod_config()->spdy_upstream_window_bits;\n      optname = SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS;\n    } else {\n      resp = &mod_config()->spdy_downstream_window_bits;\n      optname = SHRPX_OPT_BACKEND_SPDY_WINDOW_BITS;\n    }\n    errno = 0;\n    unsigned long int n = strtoul(optarg, 0, 10);\n    if(errno == 0 && n < 31) {\n      *resp = n;\n    } else {\n      LOG(ERROR) << \"--\" << optname\n                 << \" specify the integer in the range [0, 30], inclusive\";\n      return -1;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS) ||\n            util::strieq(opt, SHRPX_OPT_BACKEND_SPDY_CONNECTION_WINDOW_BITS)) {\n    size_t *resp;\n    const char *optname;\n    if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS)) {\n      resp = &mod_config()->spdy_upstream_connection_window_bits;\n      optname = SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS;\n    } else {\n      resp = &mod_config()->spdy_downstream_connection_window_bits;\n      optname = SHRPX_OPT_BACKEND_SPDY_CONNECTION_WINDOW_BITS;\n    }\n    errno = 0;\n    unsigned long int n = strtoul(optarg, 0, 10);\n    if(errno == 0 && n >= 16 && n < 31) {\n      *resp = n;\n    } else {\n      LOG(ERROR) << \"--\" << optname\n                 << \" specify the integer in the range [16, 30], inclusive\";\n      return -1;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_NO_TLS)) {\n    mod_config()->spdy_upstream_no_tls = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_FRONTEND_SPDY_PROTO)) {\n    int version = parse_spdy_proto(optarg);\n    if(version == -1) {\n      return -1;\n    } else {\n      mod_config()->spdy_upstream_version = version;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_SPDY_NO_TLS)) {\n    mod_config()->spdy_downstream_no_tls = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_SPDY_PROTO)) {\n    int version = parse_spdy_proto(optarg);\n    if(version == -1) {\n      return -1;\n    } else {\n      mod_config()->spdy_downstream_version = version;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) {\n    set_config_str(&mod_config()->backend_tls_sni_name, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_PID_FILE)) {\n    set_config_str(&mod_config()->pid_file, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_USER)) {\n    passwd *pwd = getpwnam(optarg);\n    if(pwd == 0) {\n      LOG(ERROR) << \"--user: failed to get uid from \" << optarg\n                 << \": \" << strerror(errno);\n      return -1;\n    }\n    mod_config()->uid = pwd->pw_uid;\n    mod_config()->gid = pwd->pw_gid;\n  } else if(util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) {\n    set_config_str(&mod_config()->private_key_file, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) {\n    std::string passwd = read_passwd_from_file(optarg);\n    if (passwd.empty()) {\n      LOG(ERROR) << \"Couldn't read key file's passwd from \" << optarg;\n      return -1;\n    }\n    set_config_str(&mod_config()->private_key_passwd, passwd.c_str());\n  } else if(util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) {\n    set_config_str(&mod_config()->cert_file, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) {\n    set_config_str(&mod_config()->dh_param_file, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_SUBCERT)) {\n    // Private Key file and certificate file separated by ':'.\n    const char *sp = strchr(optarg, ':');\n    if(sp) {\n      std::string keyfile(optarg, sp);\n      // TODO Do we need private key for subcert?\n      mod_config()->subcerts.push_back(std::make_pair(keyfile, sp+1));\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_SYSLOG)) {\n    mod_config()->syslog = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) {\n    int facility = int_syslog_facility(optarg);\n    if(facility == -1) {\n      LOG(ERROR) << \"Unknown syslog facility: \" << optarg;\n      return -1;\n    }\n    mod_config()->syslog_facility = facility;\n  } else if(util::strieq(opt, SHRPX_OPT_BACKLOG)) {\n    mod_config()->backlog = strtol(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_CIPHERS)) {\n    set_config_str(&mod_config()->ciphers, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_HONOR_CIPHER_ORDER)) {\n    mod_config()->honor_cipher_order = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_CLIENT)) {\n    mod_config()->client = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_INSECURE)) {\n    mod_config()->insecure = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_CACERT)) {\n    set_config_str(&mod_config()->cacert, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) {\n    mod_config()->backend_ipv4 = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) {\n    mod_config()->backend_ipv6 = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) {\n    // parse URI and get hostname, port and optionally userinfo.\n    http_parser_url u;\n    memset(&u, 0, sizeof(u));\n    int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u);\n    if(rv == 0) {\n      std::string val;\n      if(u.field_set & UF_USERINFO) {\n        http::copy_url_component(val, &u, UF_USERINFO, optarg);\n        // Surprisingly, u.field_set & UF_USERINFO is nonzero even if\n        // userinfo component is empty string.\n        if(!val.empty()) {\n          val = util::percentDecode(val.begin(), val.end());\n          set_config_str(&mod_config()->downstream_http_proxy_userinfo,\n                         val.c_str());\n        }\n      }\n      if(u.field_set & UF_HOST) {\n        http::copy_url_component(val, &u, UF_HOST, optarg);\n        set_config_str(&mod_config()->downstream_http_proxy_host, val.c_str());\n      } else {\n        LOG(ERROR) << \"backend-http-proxy-uri does not contain hostname\";\n        return -1;\n      }\n      if(u.field_set & UF_PORT) {\n        mod_config()->downstream_http_proxy_port = u.port;\n      } else {\n        LOG(ERROR) << \"backend-http-proxy-uri does not contain port\";\n        return -1;\n      }\n    } else {\n      LOG(ERROR) << \"Could not parse backend-http-proxy-uri\";\n        return -1;\n    }\n  } else if(util::strieq(opt, SHRPX_OPT_READ_RATE)) {\n    mod_config()->read_rate = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_READ_BURST)) {\n    mod_config()->read_burst = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WRITE_RATE)) {\n    mod_config()->write_rate = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WRITE_BURST)) {\n    mod_config()->write_burst = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) {\n    mod_config()->worker_read_rate = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) {\n    mod_config()->worker_read_burst = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) {\n    mod_config()->worker_write_rate = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) {\n    mod_config()->worker_write_burst = strtoul(optarg, 0, 10);\n  } else if(util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) {\n    delete [] mod_config()->tls_proto_list;\n    mod_config()->tls_proto_list = parse_config_str_list\n      (&mod_config()->tls_proto_list_len, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) {\n    mod_config()->verify_client = util::strieq(optarg, \"yes\");\n  } else if(util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) {\n    set_config_str(&mod_config()->verify_client_cacert, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) {\n    set_config_str(&mod_config()->client_private_key_file, optarg);\n  } else if(util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) {\n    set_config_str(&mod_config()->client_cert_file, optarg);\n  } else if(util::strieq(opt, \"conf\")) {\n    LOG(WARNING) << \"conf is ignored\";\n  } else {\n    LOG(ERROR) << \"Unknown option: \" << opt;\n    return -1;\n  }\n  return 0;\n}\n\nint load_config(const char *filename)\n{\n  std::ifstream in(filename, std::ios::binary);\n  if(!in) {\n    LOG(ERROR) << \"Could not open config file \" << filename;\n    return -1;\n  }\n  std::string line;\n  int linenum = 0;\n  while(std::getline(in, line)) {\n    ++linenum;\n    if(line.empty() || line[0] == '#') {\n      continue;\n    }\n    size_t i;\n    size_t size = line.size();\n    for(i = 0; i < size && line[i] != '='; ++i);\n    if(i == size) {\n      LOG(ERROR) << \"Bad configuration format at line \" << linenum;\n      return -1;\n    }\n    line[i] = '\\0';\n    const char *s = line.c_str();\n    if(parse_config(s, s+i+1) == -1) {\n      return -1;\n    }\n  }\n  return 0;\n}\n\nconst char* str_syslog_facility(int facility)\n{\n  switch(facility) {\n  case(LOG_AUTH):\n    return \"auth\";\n#ifndef __sgi\n  case(LOG_AUTHPRIV):\n    return \"authpriv\";\n#endif // !__sgi\n  case(LOG_CRON):\n    return \"cron\";\n  case(LOG_DAEMON):\n    return \"daemon\";\n#ifndef __sgi\n  case(LOG_FTP):\n    return \"ftp\";\n#endif // !__sgi\n  case(LOG_KERN):\n    return \"kern\";\n  case(LOG_LOCAL0):\n    return \"local0\";\n  case(LOG_LOCAL1):\n    return \"local1\";\n  case(LOG_LOCAL2):\n    return \"local2\";\n  case(LOG_LOCAL3):\n    return \"local3\";\n  case(LOG_LOCAL4):\n    return \"local4\";\n  case(LOG_LOCAL5):\n    return \"local5\";\n  case(LOG_LOCAL6):\n    return \"local6\";\n  case(LOG_LOCAL7):\n    return \"local7\";\n  case(LOG_LPR):\n    return \"lpr\";\n  case(LOG_MAIL):\n    return \"mail\";\n  case(LOG_SYSLOG):\n    return \"syslog\";\n  case(LOG_USER):\n    return \"user\";\n  case(LOG_UUCP):\n    return \"uucp\";\n  default:\n    return \"(unknown)\";\n  }\n}\n\nint int_syslog_facility(const char *strfacility)\n{\n  if(util::strieq(strfacility, \"auth\")) {\n    return LOG_AUTH;\n#ifndef __sgi\n  } else if(util::strieq(strfacility, \"authpriv\")) {\n    return LOG_AUTHPRIV;\n#endif // !__sgi\n  } else if(util::strieq(strfacility, \"cron\")) {\n    return LOG_CRON;\n  } else if(util::strieq(strfacility, \"daemon\")) {\n    return LOG_DAEMON;\n#ifndef __sgi\n  } else if(util::strieq(strfacility, \"ftp\")) {\n    return LOG_FTP;\n#endif // !__sgi\n  } else if(util::strieq(strfacility, \"kern\")) {\n    return LOG_KERN;\n  } else if(util::strieq(strfacility, \"local0\")) {\n    return LOG_LOCAL0;\n  } else if(util::strieq(strfacility, \"local1\")) {\n    return LOG_LOCAL1;\n  } else if(util::strieq(strfacility, \"local2\")) {\n    return LOG_LOCAL2;\n  } else if(util::strieq(strfacility, \"local3\")) {\n    return LOG_LOCAL3;\n  } else if(util::strieq(strfacility, \"local4\")) {\n    return LOG_LOCAL4;\n  } else if(util::strieq(strfacility, \"local5\")) {\n    return LOG_LOCAL5;\n  } else if(util::strieq(strfacility, \"local6\")) {\n    return LOG_LOCAL6;\n  } else if(util::strieq(strfacility, \"local7\")) {\n    return LOG_LOCAL7;\n  } else if(util::strieq(strfacility, \"lpr\")) {\n    return LOG_LPR;\n  } else if(util::strieq(strfacility, \"mail\")) {\n    return LOG_MAIL;\n  } else if(util::strieq(strfacility, \"news\")) {\n    return LOG_NEWS;\n  } else if(util::strieq(strfacility, \"syslog\")) {\n    return LOG_SYSLOG;\n  } else if(util::strieq(strfacility, \"user\")) {\n    return LOG_USER;\n  } else if(util::strieq(strfacility, \"uucp\")) {\n    return LOG_UUCP;\n  } else {\n    return -1;\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_config.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_CONFIG_H\n#define SHRPX_CONFIG_H\n\n#include \"shrpx.h\"\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <vector>\n\n#include <event.h>\n#include <openssl/ssl.h>\n\nnamespace shrpx {\n\nnamespace ssl {\n\nstruct CertLookupTree;\n\n} // namespace ssl\n\nextern const char SHRPX_OPT_PRIVATE_KEY_FILE[];\nextern const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[];\nextern const char SHRPX_OPT_CERTIFICATE_FILE[];\nextern const char SHRPX_OPT_DH_PARAM_FILE[];\nextern const char SHRPX_OPT_SUBCERT[];\nextern const char SHRPX_OPT_BACKEND[];\nextern const char SHRPX_OPT_FRONTEND[];\nextern const char SHRPX_OPT_WORKERS[];\nextern const char SHRPX_OPT_SPDY_MAX_CONCURRENT_STREAMS[];\nextern const char SHRPX_OPT_LOG_LEVEL[];\nextern const char SHRPX_OPT_DAEMON[];\nextern const char SHRPX_OPT_SPDY_PROXY[];\nextern const char SHRPX_OPT_SPDY_BRIDGE[];\nextern const char SHRPX_OPT_CLIENT_PROXY[];\nextern const char SHRPX_OPT_ADD_X_FORWARDED_FOR[];\nextern const char SHRPX_OPT_NO_VIA[];\nextern const char SHRPX_OPT_FRONTEND_SPDY_READ_TIMEOUT[];\nextern const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[];\nextern const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[];\nextern const char SHRPX_OPT_BACKEND_READ_TIMEOUT[];\nextern const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[];\nextern const char SHRPX_OPT_ACCESSLOG[];\nextern const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[];\nextern const char SHRPX_OPT_FRONTEND_SPDY_WINDOW_BITS[];\nextern const char SHRPX_OPT_BACKEND_SPDY_WINDOW_BITS[];\nextern const char SHRPX_OPT_FRONTEND_SPDY_CONNECTION_WINDOW_BITS[];\nextern const char SHRPX_OPT_BACKEND_SPDY_CONNECTION_WINDOW_BITS[];\nextern const char SHRPX_OPT_FRONTEND_SPDY_NO_TLS[];\nextern const char SHRPX_OPT_FRONTEND_SPDY_PROTO[];\nextern const char SHRPX_OPT_BACKEND_SPDY_NO_TLS[];\nextern const char SHRPX_OPT_BACKEND_SPDY_PROTO[];\nextern const char SHRPX_OPT_PID_FILE[];\nextern const char SHRPX_OPT_USER[];\nextern const char SHRPX_OPT_SYSLOG[];\nextern const char SHRPX_OPT_SYSLOG_FACILITY[];\nextern const char SHRPX_OPT_BACKLOG[];\nextern const char SHRPX_OPT_CIPHERS[];\nextern const char SHRPX_OPT_HONOR_CIPHER_ORDER[];\nextern const char SHRPX_OPT_CLIENT[];\nextern const char SHRPX_OPT_INSECURE[];\nextern const char SHRPX_OPT_CACERT[];\nextern const char SHRPX_OPT_BACKEND_IPV4[];\nextern const char SHRPX_OPT_BACKEND_IPV6[];\nextern const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[];\nextern const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[];\nextern const char SHRPX_OPT_READ_RATE[];\nextern const char SHRPX_OPT_READ_BURST[];\nextern const char SHRPX_OPT_WRITE_RATE[];\nextern const char SHRPX_OPT_WRITE_BURST[];\nextern const char SHRPX_OPT_WORKER_READ_RATE[];\nextern const char SHRPX_OPT_WORKER_READ_BURST[];\nextern const char SHRPX_OPT_WORKER_WRITE_RATE[];\nextern const char SHRPX_OPT_WORKER_WRITE_BURST[];\nextern const char SHRPX_OPT_TLS_PROTO_LIST[];\nextern const char SHRPX_OPT_VERIFY_CLIENT[];\nextern const char SHRPX_OPT_VERIFY_CLIENT_CACERT[];\nextern const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[];\nextern const char SHRPX_OPT_CLIENT_CERT_FILE[];\n\nunion sockaddr_union {\n  sockaddr sa;\n  sockaddr_storage storage;\n  sockaddr_in6 in6;\n  sockaddr_in in;\n};\n\nenum shrpx_proto {\n  PROTO_SPDY,\n  PROTO_HTTP\n};\n\nstruct Config {\n  // The list of (private key file, certificate file) pair\n  std::vector<std::pair<std::string, std::string> > subcerts;\n  sockaddr_union downstream_addr;\n  // binary form of http proxy host and port\n  sockaddr_union downstream_http_proxy_addr;\n  timeval spdy_upstream_read_timeout;\n  timeval upstream_read_timeout;\n  timeval upstream_write_timeout;\n  timeval downstream_read_timeout;\n  timeval downstream_write_timeout;\n  timeval downstream_idle_read_timeout;\n  char *host;\n  char *private_key_file;\n  char *private_key_passwd;\n  char *cert_file;\n  char *dh_param_file;\n  SSL_CTX *default_ssl_ctx;\n  ssl::CertLookupTree *cert_tree;\n  const char *server_name;\n  char *downstream_host;\n  char *downstream_hostport;\n  char *backend_tls_sni_name;\n  char *pid_file;\n  char *conf_path;\n  char *ciphers;\n  char *cacert;\n  // userinfo in http proxy URI, not percent-encoded form\n  char *downstream_http_proxy_userinfo;\n  // host in http proxy URI\n  char *downstream_http_proxy_host;\n  // Rate limit configuration per connection\n  ev_token_bucket_cfg *rate_limit_cfg;\n  // Rate limit configuration per worker (thread)\n  ev_token_bucket_cfg *worker_rate_limit_cfg;\n  // list of supported SSL/TLS protocol strings. The each element of\n  // this list is a NULL-terminated string.\n  char **tls_proto_list;\n  // Path to file containing CA certificate solely used for client\n  // certificate validation\n  char *verify_client_cacert;\n  char *client_private_key_file;\n  char *client_cert_file;\n  size_t downstream_addrlen;\n  size_t num_worker;\n  size_t spdy_max_concurrent_streams;\n  size_t spdy_upstream_window_bits;\n  size_t spdy_downstream_window_bits;\n  size_t spdy_upstream_connection_window_bits;\n  size_t spdy_downstream_connection_window_bits;\n  // actual size of downstream_http_proxy_addr\n  size_t downstream_http_proxy_addrlen;\n  size_t read_rate;\n  size_t read_burst;\n  size_t write_rate;\n  size_t write_burst;\n  size_t worker_read_rate;\n  size_t worker_read_burst;\n  size_t worker_write_rate;\n  size_t worker_write_burst;\n  // The number of elements in tls_proto_list\n  size_t tls_proto_list_len;\n  // downstream protocol; this will be determined by given options.\n  shrpx_proto downstream_proto;\n  int syslog_facility;\n  int backlog;\n  uid_t uid;\n  gid_t gid;\n  uint16_t port;\n  uint16_t downstream_port;\n  uint16_t spdy_upstream_version;\n  uint16_t spdy_downstream_version;\n  // port in http proxy URI\n  uint16_t downstream_http_proxy_port;\n  bool verbose;\n  bool daemon;\n  bool verify_client;\n  bool spdy_proxy;\n  bool spdy_bridge;\n  bool client_proxy;\n  bool add_x_forwarded_for;\n  bool no_via;\n  bool accesslog;\n  bool spdy_upstream_no_tls;\n  bool spdy_downstream_no_tls;\n  bool syslog;\n  // This member finally decides syslog is used or not\n  bool use_syslog;\n  bool honor_cipher_order;\n  bool client;\n  // true if --client or --client-proxy are enabled.\n  bool client_mode;\n  bool insecure;\n  bool backend_ipv4;\n  bool backend_ipv6;\n  // true if stderr refers to a terminal.\n  bool tty;\n};\n\nconst Config* get_config();\nConfig* mod_config();\nvoid create_config();\n\n// Parses option name |opt| and value |optarg|.  The results are\n// stored into statically allocated Config object. This function\n// returns 0 if it succeeds, or -1.\nint parse_config(const char *opt, const char *optarg);\n\n// Loads configurations from |filename| and stores them in statically\n// allocated Config object. This function returns 0 if it succeeds, or\n// -1.\nint load_config(const char *filename);\n\n// Read passwd from |filename|\nstd::string read_passwd_from_file(const char *filename);\n\n// Parses comma delimited strings in |s| and returns the array of\n// pointers, each element points to the each substring in |s|. The\n// number of elements are stored in |*outlen|. The |s| must be comma\n// delimited list of strings. The strings must be delimited by a\n// single comma and any white spaces around it are treated as a part\n// of protocol strings. This function may modify |s| and the caller\n// must leave it as is after this call. This function allocates memory\n// to store the parsed strings and it is caller's responsibility to\n// deallocate the memory.\nchar** parse_config_str_list(size_t *outlen, const char *s);\n\n// Copies NULL-terminated string |val| to |*destp|. If |*destp| is not\n// NULL, it is freed before copying.\nvoid set_config_str(char **destp, const char *val);\n\n// Returns string for syslog |facility|.\nconst char* str_syslog_facility(int facility);\n\n// Returns integer value of syslog |facility| string.\nint int_syslog_facility(const char *strfacility);\n\n} // namespace shrpx\n\n#endif // SHRPX_CONFIG_H\n"
  },
  {
    "path": "src/shrpx_downstream.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_downstream.h\"\n\n#include <cassert>\n\n#include \"shrpx_upstream.h\"\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_error.h\"\n#include \"shrpx_downstream_connection.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nDownstream::Downstream(Upstream *upstream, int stream_id, int priority)\n  : upstream_(upstream),\n    dconn_(0),\n    response_body_buf_(0),\n    stream_id_(stream_id),\n    downstream_stream_id_(-1),\n    response_rst_stream_status_code_(0),\n    priority_(priority),\n    request_state_(INITIAL),\n    request_major_(1),\n    request_minor_(1),\n    response_state_(INITIAL),\n    response_http_status_(0),\n    response_major_(1),\n    response_minor_(1),\n    chunked_request_(false),\n    request_connection_close_(false),\n    request_expect_100_continue_(false),\n    request_header_key_prev_(false),\n    chunked_response_(false),\n    response_connection_close_(false),\n    response_header_key_prev_(false)\n{}\n\nDownstream::~Downstream()\n{\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, this) << \"Deleting\";\n  }\n  if(response_body_buf_) {\n    // Passing NULL to evbuffer_free() causes segmentation fault.\n    evbuffer_free(response_body_buf_);\n  }\n  if(dconn_) {\n    delete dconn_;\n  }\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, this) << \"Deleted\";\n  }\n}\n\nvoid Downstream::set_downstream_connection(DownstreamConnection *dconn)\n{\n  dconn_ = dconn;\n}\n\nDownstreamConnection* Downstream::get_downstream_connection()\n{\n  return dconn_;\n}\n\nvoid Downstream::pause_read(IOCtrlReason reason)\n{\n  if(dconn_) {\n    dconn_->pause_read(reason);\n  }\n}\n\nint Downstream::resume_read(IOCtrlReason reason)\n{\n  if(dconn_) {\n    return dconn_->resume_read(reason);\n  } else {\n    return 0;\n  }\n}\n\nvoid Downstream::force_resume_read()\n{\n  if(dconn_) {\n    dconn_->force_resume_read();\n  }\n}\n\nnamespace {\nvoid check_header_field(bool *result, const Headers::value_type &item,\n                        const char *name, const char *value)\n{\n  if(util::strieq(item.first.c_str(), name)) {\n    if(util::strifind(item.second.c_str(), value)) {\n      *result = true;\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid check_transfer_encoding_chunked(bool *chunked,\n                                     const Headers::value_type &item)\n{\n  return check_header_field(chunked, item, \"transfer-encoding\", \"chunked\");\n}\n} // namespace\n\nnamespace {\nvoid check_expect_100_continue(bool *res,\n                               const Headers::value_type& item)\n{\n  return check_header_field(res, item, \"expect\", \"100-continue\");\n}\n} // namespace\n\nconst Headers& Downstream::get_request_headers() const\n{\n  return request_headers_;\n}\n\nvoid Downstream::add_request_header(const std::string& name,\n                                    const std::string& value)\n{\n  request_header_key_prev_ = true;\n  request_headers_.push_back(std::make_pair(name, value));\n}\n\nvoid Downstream::set_last_request_header_value(const std::string& value)\n{\n  request_header_key_prev_ = false;\n  Headers::value_type &item = request_headers_.back();\n  item.second = value;\n  check_transfer_encoding_chunked(&chunked_request_, item);\n  check_expect_100_continue(&request_expect_100_continue_, item);\n  //check_connection_close(&request_connection_close_, item);\n}\n\nbool Downstream::get_request_header_key_prev() const\n{\n  return request_header_key_prev_;\n}\n\nvoid Downstream::append_last_request_header_key(const char *data, size_t len)\n{\n  assert(request_header_key_prev_);\n  Headers::value_type &item = request_headers_.back();\n  item.first.append(data, len);\n}\n\nvoid Downstream::append_last_request_header_value(const char *data, size_t len)\n{\n  assert(!request_header_key_prev_);\n  Headers::value_type &item = request_headers_.back();\n  item.second.append(data, len);\n}\n\nvoid Downstream::set_request_method(const std::string& method)\n{\n  request_method_ = method;\n}\n\nconst std::string& Downstream::get_request_method() const\n{\n  return request_method_;\n}\n\nvoid Downstream::set_request_path(const std::string& path)\n{\n  request_path_ = path;\n}\n\nvoid Downstream::append_request_path(const char *data, size_t len)\n{\n  request_path_.append(data, len);\n}\n\nconst std::string& Downstream::get_request_path() const\n{\n  return request_path_;\n}\n\nvoid Downstream::set_request_major(int major)\n{\n  request_major_ = major;\n}\n\nvoid Downstream::set_request_minor(int minor)\n{\n  request_minor_ = minor;\n}\n\nint Downstream::get_request_major() const\n{\n  return request_major_;\n}\n\nint Downstream::get_request_minor() const\n{\n  return request_minor_;\n}\n\nUpstream* Downstream::get_upstream() const\n{\n  return upstream_;\n}\n\nint32_t Downstream::get_stream_id() const\n{\n  return stream_id_;\n}\n\nvoid Downstream::set_request_state(int state)\n{\n  request_state_ = state;\n}\n\nint Downstream::get_request_state() const\n{\n  return request_state_;\n}\n\nbool Downstream::get_chunked_request() const\n{\n  return chunked_request_;\n}\n\nbool Downstream::get_request_connection_close() const\n{\n  return request_connection_close_;\n}\n\nvoid Downstream::set_request_connection_close(bool f)\n{\n  request_connection_close_ = f;\n}\n\nbool Downstream::get_expect_100_continue() const\n{\n  return request_expect_100_continue_;\n}\n\nbool Downstream::get_output_buffer_full()\n{\n  if(dconn_) {\n    return dconn_->get_output_buffer_full();\n  } else {\n    return false;\n  }\n}\n\n// Call this function after this object is attached to\n// Downstream. Otherwise, the program will crash.\nint Downstream::push_request_headers()\n{\n  if(!dconn_) {\n    DLOG(INFO, this) << \"dconn_ is NULL\";\n    return -1;\n  }\n  return dconn_->push_request_headers();\n}\n\nint Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen)\n{\n  // Assumes that request headers have already been pushed to output\n  // buffer using push_request_headers().\n  if(!dconn_) {\n    DLOG(INFO, this) << \"dconn_ is NULL\";\n    return -1;\n  }\n  return dconn_->push_upload_data_chunk(data, datalen);\n}\n\nint Downstream::end_upload_data()\n{\n  if(!dconn_) {\n    DLOG(INFO, this) << \"dconn_ is NULL\";\n    return -1;\n  }\n  return dconn_->end_upload_data();\n}\n\nconst Headers& Downstream::get_response_headers() const\n{\n  return response_headers_;\n}\n\nvoid Downstream::add_response_header(const std::string& name,\n                                     const std::string& value)\n{\n  response_header_key_prev_ = true;\n  response_headers_.push_back(std::make_pair(name, value));\n  check_transfer_encoding_chunked(&chunked_response_,\n                                  response_headers_.back());\n}\n\nvoid Downstream::set_last_response_header_value(const std::string& value)\n{\n  response_header_key_prev_ = false;\n  Headers::value_type &item = response_headers_.back();\n  item.second = value;\n  check_transfer_encoding_chunked(&chunked_response_, item);\n  //check_connection_close(&response_connection_close_, item);\n}\n\nbool Downstream::get_response_header_key_prev() const\n{\n  return response_header_key_prev_;\n}\n\nvoid Downstream::append_last_response_header_key(const char *data, size_t len)\n{\n  assert(response_header_key_prev_);\n  Headers::value_type &item = response_headers_.back();\n  item.first.append(data, len);\n}\n\nvoid Downstream::append_last_response_header_value(const char *data,\n                                                   size_t len)\n{\n  assert(!response_header_key_prev_);\n  Headers::value_type &item = response_headers_.back();\n  item.second.append(data, len);\n}\n\nunsigned int Downstream::get_response_http_status() const\n{\n  return response_http_status_;\n}\n\nvoid Downstream::set_response_http_status(unsigned int status)\n{\n  response_http_status_ = status;\n}\n\nvoid Downstream::set_response_major(int major)\n{\n  response_major_ = major;\n}\n\nvoid Downstream::set_response_minor(int minor)\n{\n  response_minor_ = minor;\n}\n\nint Downstream::get_response_major() const\n{\n  return response_major_;\n}\n\nint Downstream::get_response_minor() const\n{\n  return response_minor_;\n}\n\nint Downstream::get_response_version() const\n{\n  return response_major_*100+response_minor_;\n}\n\nbool Downstream::get_chunked_response() const\n{\n  return chunked_response_;\n}\n\nvoid Downstream::set_chunked_response(bool f)\n{\n  chunked_response_ = f;\n}\n\nbool Downstream::get_response_connection_close() const\n{\n  return response_connection_close_;\n}\n\nvoid Downstream::set_response_connection_close(bool f)\n{\n  response_connection_close_ = f;\n}\n\nint Downstream::on_read()\n{\n  if(!dconn_) {\n    DLOG(INFO, this) << \"dconn_ is NULL\";\n    return -1;\n  }\n  return dconn_->on_read();\n}\n\nvoid Downstream::set_response_state(int state)\n{\n  response_state_ = state;\n}\n\nint Downstream::get_response_state() const\n{\n  return response_state_;\n}\n\nint Downstream::init_response_body_buf()\n{\n  if(!response_body_buf_) {\n    response_body_buf_ = evbuffer_new();\n    if(response_body_buf_ == 0) {\n      DIE();\n    }\n  }\n  return 0;\n}\n\nevbuffer* Downstream::get_response_body_buf()\n{\n  return response_body_buf_;\n}\n\nvoid Downstream::set_priority(int pri)\n{\n  priority_ = pri;\n}\n\nint Downstream::get_priority() const\n{\n  return priority_;\n}\n\nbool Downstream::tunnel_established() const\n{\n  return request_method_ == \"CONNECT\" &&\n    200 <= response_http_status_ && response_http_status_ < 300;\n}\n\nvoid Downstream::set_downstream_stream_id(int32_t stream_id)\n{\n  downstream_stream_id_ = stream_id;\n}\n\nint32_t Downstream::get_downstream_stream_id() const\n{\n  return downstream_stream_id_;\n}\n\nuint32_t Downstream::get_response_rst_stream_status_code() const\n{\n  return response_rst_stream_status_code_;\n}\n\nvoid Downstream::set_response_rst_stream_status_code(uint32_t status_code)\n{\n  response_rst_stream_status_code_ = status_code;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_downstream.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_DOWNSTREAM_H\n#define SHRPX_DOWNSTREAM_H\n\n#include \"shrpx.h\"\n\n#include <stdint.h>\n\n#include <vector>\n#include <string>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\n#include \"shrpx_io_control.h\"\n\nnamespace shrpx {\n\nclass Upstream;\nclass DownstreamConnection;\n\ntypedef std::vector<std::pair<std::string, std::string> > Headers;\n\nclass Downstream {\npublic:\n  Downstream(Upstream *upstream, int stream_id, int priority);\n  ~Downstream();\n  Upstream* get_upstream() const;\n  int32_t get_stream_id() const;\n  void set_priority(int pri);\n  int get_priority() const;\n  void pause_read(IOCtrlReason reason);\n  int resume_read(IOCtrlReason reason);\n  void force_resume_read();\n  // Set stream ID for downstream SPDY connection.\n  void set_downstream_stream_id(int32_t stream_id);\n  int32_t get_downstream_stream_id() const;\n\n  void set_downstream_connection(DownstreamConnection *dconn);\n  DownstreamConnection* get_downstream_connection();\n  // Returns true if output buffer is full. If underlying dconn_ is\n  // NULL, this function always returns false.\n  bool get_output_buffer_full();\n  // Returns true if tunnel connection has been established.\n  bool tunnel_established() const;\n  // downstream request API\n  const Headers& get_request_headers() const;\n  void add_request_header(const std::string& name, const std::string& value);\n  void set_last_request_header_value(const std::string& value);\n\n  bool get_request_header_key_prev() const;\n  void append_last_request_header_key(const char *data, size_t len);\n  void append_last_request_header_value(const char *data, size_t len);\n\n  void set_request_method(const std::string& method);\n  const std::string& get_request_method() const;\n  void set_request_path(const std::string& path);\n  void append_request_path(const char *data, size_t len);\n  const std::string& get_request_path() const;\n  void set_request_major(int major);\n  void set_request_minor(int minor);\n  int get_request_major() const;\n  int get_request_minor() const;\n  int push_request_headers();\n  bool get_chunked_request() const;\n  bool get_request_connection_close() const;\n  void set_request_connection_close(bool f);\n  bool get_expect_100_continue() const;\n  int push_upload_data_chunk(const uint8_t *data, size_t datalen);\n  int end_upload_data();\n  enum {\n    INITIAL,\n    HEADER_COMPLETE,\n    MSG_COMPLETE,\n    STREAM_CLOSED,\n    CONNECT_FAIL,\n    IDLE,\n    MSG_RESET\n  };\n  void set_request_state(int state);\n  int get_request_state() const;\n  // downstream response API\n  const Headers& get_response_headers() const;\n  void add_response_header(const std::string& name, const std::string& value);\n  void set_last_response_header_value(const std::string& value);\n\n  bool get_response_header_key_prev() const;\n  void append_last_response_header_key(const char *data, size_t len);\n  void append_last_response_header_value(const char *data, size_t len);\n\n  unsigned int get_response_http_status() const;\n  void set_response_http_status(unsigned int status);\n  void set_response_major(int major);\n  void set_response_minor(int minor);\n  int get_response_major() const;\n  int get_response_minor() const;\n  int get_response_version() const;\n  bool get_chunked_response() const;\n  void set_chunked_response(bool f);\n  bool get_response_connection_close() const;\n  void set_response_connection_close(bool f);\n  void set_response_state(int state);\n  int get_response_state() const;\n  int init_response_body_buf();\n  evbuffer* get_response_body_buf();\n  uint32_t get_response_rst_stream_status_code() const;\n  void set_response_rst_stream_status_code(uint32_t status_code);\n\n  // Call this method when there is incoming data in downstream\n  // connection.\n  int on_read();\nprivate:\n  Headers request_headers_;\n  Headers response_headers_;\n\n  Upstream *upstream_;\n  DownstreamConnection *dconn_;\n  // This buffer is used to temporarily store downstream response\n  // body. Spdylay reads data from this in the callback.\n  evbuffer *response_body_buf_;\n\n  std::string request_method_;\n  std::string request_path_;\n\n  int32_t stream_id_;\n  // stream ID in backend connection\n  int32_t downstream_stream_id_;\n\n  // RST_STREAM status_code from downstream SPDY connection\n  uint32_t response_rst_stream_status_code_;\n\n  int priority_;\n\n  int request_state_;\n  int request_major_;\n  int request_minor_;\n\n  int response_state_;\n  unsigned int response_http_status_;\n  int response_major_;\n  int response_minor_;\n\n  bool chunked_request_;\n  bool request_connection_close_;\n  bool request_expect_100_continue_;\n  bool request_header_key_prev_;\n\n  bool chunked_response_;\n  bool response_connection_close_;\n  bool response_header_key_prev_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_DOWNSTREAM_H\n"
  },
  {
    "path": "src/shrpx_downstream_connection.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_downstream_connection.h\"\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_downstream.h\"\n\nnamespace shrpx {\n\nDownstreamConnection::DownstreamConnection(ClientHandler *client_handler)\n  : client_handler_(client_handler),\n    downstream_(0)\n{}\n\nDownstreamConnection::~DownstreamConnection()\n{}\n\nClientHandler* DownstreamConnection::get_client_handler()\n{\n  return client_handler_;\n}\n\nDownstream* DownstreamConnection::get_downstream()\n{\n  return downstream_;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_downstream_connection.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_DOWNSTREAM_CONNECTION_H\n#define SHRPX_DOWNSTREAM_CONNECTION_H\n\n#include \"shrpx.h\"\n\n#include \"shrpx_io_control.h\"\n\nnamespace shrpx {\n\nclass ClientHandler;\nclass Downstream;\n\nclass DownstreamConnection {\npublic:\n  DownstreamConnection(ClientHandler *client_handler);\n  virtual ~DownstreamConnection();\n  virtual int attach_downstream(Downstream *downstream) = 0;\n  virtual void detach_downstream(Downstream *downstream) = 0;\n\n  virtual int push_request_headers() = 0;\n  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0;\n  virtual int end_upload_data() = 0;\n\n  virtual void pause_read(IOCtrlReason reason) = 0;\n  virtual int resume_read(IOCtrlReason reason) = 0;\n  virtual void force_resume_read() = 0;\n\n  virtual bool get_output_buffer_full() = 0;\n\n  virtual int on_read() = 0;\n  virtual int on_write() = 0;\n\n  ClientHandler* get_client_handler();\n  Downstream* get_downstream();\nprotected:\n  ClientHandler *client_handler_;\n  Downstream *downstream_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_DOWNSTREAM_CONNECTION_H\n"
  },
  {
    "path": "src/shrpx_downstream_queue.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_downstream_queue.h\"\n\n#include <cassert>\n\n#include \"shrpx_downstream.h\"\n\nnamespace shrpx {\n\nDownstreamQueue::DownstreamQueue()\n{}\n\nDownstreamQueue::~DownstreamQueue()\n{\n  for(std::map<int32_t, Downstream*>::iterator i = downstreams_.begin();\n      i != downstreams_.end(); ++i) {\n    delete (*i).second;\n  }\n}\n\nvoid DownstreamQueue::add(Downstream *downstream)\n{\n  downstreams_[downstream->get_stream_id()] = downstream;\n}\n\nvoid DownstreamQueue::remove(Downstream *downstream)\n{\n  downstreams_.erase(downstream->get_stream_id());\n}\n\nDownstream* DownstreamQueue::find(int32_t stream_id)\n{\n  std::map<int32_t, Downstream*>::iterator i = downstreams_.find(stream_id);\n  if(i == downstreams_.end()) {\n    return 0;\n  } else {\n    return (*i).second;\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_downstream_queue.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_DOWNSTREAM_QUEUE_H\n#define SHRPX_DOWNSTREAM_QUEUE_H\n\n#include \"shrpx.h\"\n\n#include <stdint.h>\n\n#include <map>\n\nnamespace shrpx {\n\nclass Downstream;\n\nclass DownstreamQueue {\npublic:\n  DownstreamQueue();\n  ~DownstreamQueue();\n  void add(Downstream *downstream);\n  void remove(Downstream *downstream);\n  Downstream* find(int32_t stream_id);\nprivate:\n  std::map<int32_t, Downstream*> downstreams_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_DOWNSTREAM_QUEUE_H\n"
  },
  {
    "path": "src/shrpx_error.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_ERROR_H\n#define SHRPX_ERROR_H\n\n#include \"shrpx.h\"\n\nnamespace shrpx {\n\nenum ErrorCode {\n  SHRPX_ERR_SUCCESS = 0,\n  SHRPX_ERR_UNKNOWN = -1,\n  SHRPX_ERR_HTTP_PARSE = -2,\n  SHRPX_ERR_NETWORK = -3\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_ERROR_H\n"
  },
  {
    "path": "src/shrpx_http.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_http.h\"\n\n#include \"shrpx_config.h\"\n#include \"shrpx_log.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nnamespace http {\n\nstd::string get_status_string(unsigned int status_code)\n{\n  switch(status_code) {\n  case 100: return \"100 Continue\";\n  case 101: return \"101 Switching Protocols\";\n  case 200: return \"200 OK\";\n  case 201: return \"201 Created\";\n  case 202: return \"202 Accepted\";\n  case 203: return \"203 Non-Authoritative Information\";\n  case 204: return \"204 No Content\";\n  case 205: return \"205 Reset Content\";\n  case 206: return \"206 Partial Content\";\n  case 300: return \"300 Multiple Choices\";\n  case 301: return \"301 Moved Permanently\";\n  case 302: return \"302 Found\";\n  case 303: return \"303 See Other\";\n  case 304: return \"304 Not Modified\";\n  case 305: return \"305 Use Proxy\";\n    // case 306: return \"306 (Unused)\";\n  case 307: return \"307 Temporary Redirect\";\n  case 400: return \"400 Bad Request\";\n  case 401: return \"401 Unauthorized\";\n  case 402: return \"402 Payment Required\";\n  case 403: return \"403 Forbidden\";\n  case 404: return \"404 Not Found\";\n  case 405: return \"405 Method Not Allowed\";\n  case 406: return \"406 Not Acceptable\";\n  case 407: return \"407 Proxy Authentication Required\";\n  case 408: return \"408 Request Timeout\";\n  case 409: return \"409 Conflict\";\n  case 410: return \"410 Gone\";\n  case 411: return \"411 Length Required\";\n  case 412: return \"412 Precondition Failed\";\n  case 413: return \"413 Request Entity Too Large\";\n  case 414: return \"414 Request-URI Too Long\";\n  case 415: return \"415 Unsupported Media Type\";\n  case 416: return \"416 Requested Range Not Satisfiable\";\n  case 417: return \"417 Expectation Failed\";\n  case 500: return \"500 Internal Server Error\";\n  case 501: return \"501 Not Implemented\";\n  case 502: return \"502 Bad Gateway\";\n  case 503: return \"503 Service Unavailable\";\n  case 504: return \"504 Gateway Timeout\";\n  case 505: return \"505 HTTP Version Not Supported\";\n  default: return util::utos(status_code);\n  }\n}\n\nstd::string create_error_html(unsigned int status_code)\n{\n  std::string res;\n  res.reserve(512);\n  std::string status = http::get_status_string(status_code);\n  res += \"<html><head><title>\";\n  res += status;\n  res += \"</title></head><body><h1>\";\n  res += status;\n  res += \"</h1><hr><address>\";\n  res += get_config()->server_name;\n  res += \" at port \";\n  res += util::utos(get_config()->port);\n  res += \"</address>\";\n  res += \"</body></html>\";\n  return res;\n}\n\nstd::string create_via_header_value(int major, int minor)\n{\n  std::string hdrs;\n  hdrs += static_cast<char>(major+'0');\n  hdrs += \".\";\n  hdrs += static_cast<char>(minor+'0');\n  hdrs += \" shrpx\";\n  return hdrs;\n}\n\nvoid capitalize(std::string& s, size_t offset)\n{\n  s[offset] = util::upcase(s[offset]);\n  for(size_t i = offset+1, eoi = s.size(); i < eoi; ++i) {\n    if(s[i-1] == '-') {\n      s[i] = util::upcase(s[i]);\n    } else {\n      s[i] = util::lowcase(s[i]);\n    }\n  }\n}\n\nbool check_header_value(const char *value)\n{\n  return strpbrk(value, \"\\r\\n\") == 0;\n}\n\nvoid sanitize_header_value(std::string& s, size_t offset)\n{\n  for(size_t i = offset, eoi = s.size(); i < eoi; ++i) {\n    if(s[i] == '\\r' || s[i] == '\\n') {\n      s[i] = ' ';\n    }\n  }\n}\n\nstd::string colorizeHeaders(const char *hdrs)\n{\n  std::string nhdrs;\n  const char *p = strchr(hdrs, '\\n');\n  if(!p) {\n    // Not valid HTTP header\n    return hdrs;\n  }\n  nhdrs.append(hdrs, p+1);\n  ++p;\n  while(1) {\n    const char* np = strchr(p, ':');\n    if(!np) {\n      nhdrs.append(p);\n      break;\n    }\n    nhdrs += TTY_HTTP_HD;\n    nhdrs.append(p, np);\n    nhdrs += TTY_RST;\n    p = np;\n    np = strchr(p, '\\n');\n    if(!np) {\n      nhdrs.append(p);\n      break;\n    }\n    nhdrs.append(p, np+1);\n    p = np+1;\n  }\n  return nhdrs;\n}\n\nvoid copy_url_component(std::string& dest, http_parser_url *u, int field,\n                        const char* url)\n{\n  if(u->field_set & (1 << field)) {\n    dest.assign(url+u->field_data[field].off, u->field_data[field].len);\n  }\n}\n\nint32_t determine_window_update_transmission(spdylay_session *session,\n                                             int32_t stream_id,\n                                             int32_t window_size)\n{\n  int32_t recv_length;\n  if(stream_id == 0) {\n    recv_length = spdylay_session_get_recv_data_length(session);\n  } else {\n    recv_length = spdylay_session_get_stream_recv_data_length\n      (session, stream_id);\n  }\n  if(recv_length != -1 && recv_length >= window_size / 2) {\n    return recv_length;\n  }\n  return -1;\n}\n\n} // namespace http\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_http.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_HTTP_H\n#define SHRPX_HTTP_H\n\n#include <string>\n\n#include <spdylay/spdylay.h>\n\n#include \"http-parser/http_parser.h\"\n\nnamespace shrpx {\n\nnamespace http {\n\nstd::string get_status_string(unsigned int status_code);\n\nstd::string create_error_html(unsigned int status_code);\n\nstd::string create_via_header_value(int major, int minor);\n\nvoid capitalize(std::string& s, size_t offset);\n\n// Returns false if |value| contains \\r or \\n.\nbool check_header_value(const char *value);\n\nvoid sanitize_header_value(std::string& s, size_t offset);\n\n// Adds ANSI color codes to HTTP headers |hdrs|.\nstd::string colorizeHeaders(const char *hdrs);\n\n// Copies the |field| component value from |u| and |url| to the\n// |dest|. If |u| does not have |field|, then this function does\n// nothing.\nvoid copy_url_component(std::string& dest, http_parser_url *u, int field,\n                        const char* url);\n\n// Return positive window_size_increment if WINDOW_UPDATE should be\n// sent for the stream |stream_id|. If |stream_id| == 0, this function\n// determines the necessity of the WINDOW_UPDATE for a connection.\n// The receiver window size is given in the |window_size|.\n//\n// If the function determines WINDOW_UPDATE is not necessary at the\n// moment, it returns -1.\nint32_t determine_window_update_transmission(spdylay_session *session,\n                                             int32_t stream_id,\n                                             int32_t window_size);\n\n} // namespace http\n\n} // namespace shrpx\n\n#endif // SHRPX_HTTP_H\n"
  },
  {
    "path": "src/shrpx_http_downstream_connection.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_http_downstream_connection.h\"\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_upstream.h\"\n#include \"shrpx_downstream.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_error.h\"\n#include \"shrpx_http.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nnamespace {\nconst size_t OUTBUF_MAX_THRES = 64*1024;\n} // namespace\n\n// Workaround for the inability for Bufferevent to remove timeout from\n// bufferevent. Specify this long timeout instead of removing.\nnamespace {\ntimeval max_timeout = { 86400, 0 };\n} // namespace\n\nHttpDownstreamConnection::HttpDownstreamConnection\n(ClientHandler *client_handler)\n  : DownstreamConnection(client_handler),\n    bev_(0),\n    ioctrl_(0),\n    response_htp_(new http_parser())\n{}\n\nHttpDownstreamConnection::~HttpDownstreamConnection()\n{\n  delete response_htp_;\n  if(bev_) {\n    bufferevent_disable(bev_, EV_READ | EV_WRITE);\n    bufferevent_free(bev_);\n  }\n  // Downstream and DownstreamConnection may be deleted\n  // asynchronously.\n  if(downstream_) {\n    downstream_->set_downstream_connection(0);\n  }\n}\n\nint HttpDownstreamConnection::attach_downstream(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Attaching to DOWNSTREAM:\" << downstream;\n  }\n  Upstream *upstream = downstream->get_upstream();\n  if(!bev_) {\n    event_base *evbase = client_handler_->get_evbase();\n    bev_ = bufferevent_socket_new\n      (evbase, -1,\n       BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);\n    int rv = bufferevent_socket_connect\n      (bev_,\n       // TODO maybe not thread-safe?\n       const_cast<sockaddr*>(&get_config()->downstream_addr.sa),\n       get_config()->downstream_addrlen);\n    if(rv != 0) {\n      bufferevent_free(bev_);\n      bev_ = 0;\n      return SHRPX_ERR_NETWORK;\n    }\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, this) << \"Connecting to downstream server\";\n    }\n  }\n  downstream->set_downstream_connection(this);\n  downstream_ = downstream;\n\n  ioctrl_.set_bev(bev_);\n\n  http_parser_init(response_htp_, HTTP_RESPONSE);\n  response_htp_->data = downstream_;\n\n  bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);\n  bufferevent_enable(bev_, EV_READ);\n  bufferevent_setcb(bev_,\n                    upstream->get_downstream_readcb(),\n                    upstream->get_downstream_writecb(),\n                    upstream->get_downstream_eventcb(), this);\n  // HTTP request/response model, we first issue request to downstream\n  // server, so just enable write timeout here.\n  bufferevent_set_timeouts(bev_,\n                           &max_timeout,\n                           &get_config()->downstream_write_timeout);\n  return 0;\n}\n\nint HttpDownstreamConnection::push_request_headers()\n{\n  std::string hdrs = downstream_->get_request_method();\n  hdrs += \" \";\n  hdrs += downstream_->get_request_path();\n  hdrs += \" HTTP/1.1\\r\\n\";\n  bool connection_upgrade = false;\n  std::string via_value;\n  std::string xff_value;\n  const Headers& request_headers = downstream_->get_request_headers();\n  for(Headers::const_iterator i = request_headers.begin();\n      i != request_headers.end(); ++i) {\n    if(util::strieq((*i).first.c_str(), \"connection\")) {\n      if(util::strifind((*i).second.c_str(), \"upgrade\")) {\n        connection_upgrade = true;\n      }\n    } else if(util::strieq((*i).first.c_str(), \"x-forwarded-proto\") ||\n       util::strieq((*i).first.c_str(), \"keep-alive\") ||\n       util::strieq((*i).first.c_str(), \"proxy-connection\")) {\n      continue;\n    }\n    if(!get_config()->no_via && util::strieq((*i).first.c_str(), \"via\")) {\n      via_value = (*i).second;\n      continue;\n    }\n    if(util::strieq((*i).first.c_str(), \"x-forwarded-for\")) {\n      xff_value = (*i).second;\n      continue;\n    }\n    if(util::strieq((*i).first.c_str(), \"expect\") &&\n       util::strifind((*i).second.c_str(), \"100-continue\")) {\n      continue;\n    }\n    hdrs += (*i).first;\n    http::capitalize(hdrs, hdrs.size()-(*i).first.size());\n    hdrs += \": \";\n    hdrs += (*i).second;\n    http::sanitize_header_value(hdrs, hdrs.size()-(*i).second.size());\n    hdrs += \"\\r\\n\";\n  }\n  if(downstream_->get_request_connection_close()) {\n    hdrs += \"Connection: close\\r\\n\";\n  } else if(connection_upgrade) {\n    hdrs += \"Connection: upgrade\\r\\n\";\n  }\n  if(get_config()->add_x_forwarded_for) {\n    hdrs += \"X-Forwarded-For: \";\n    if(!xff_value.empty()) {\n      hdrs += xff_value;\n      http::sanitize_header_value(hdrs, hdrs.size()-xff_value.size());\n      hdrs += \", \";\n    }\n    hdrs += downstream_->get_upstream()->get_client_handler()->get_ipaddr();\n    hdrs += \"\\r\\n\";\n  } else if(!xff_value.empty()) {\n    hdrs += \"X-Forwarded-For: \";\n    hdrs += xff_value;\n    http::sanitize_header_value(hdrs, hdrs.size()-xff_value.size());\n    hdrs += \"\\r\\n\";\n  }\n  if(!get_config()->spdy_proxy && !get_config()->client_proxy &&\n     downstream_->get_request_method() != \"CONNECT\") {\n    hdrs += \"X-Forwarded-Proto: \";\n    if(downstream_->get_upstream()->get_client_handler()->get_ssl()) {\n      hdrs += \"https\\r\\n\";\n    } else {\n      hdrs += \"http\\r\\n\";\n    }\n  }\n  if(!get_config()->no_via) {\n    hdrs += \"Via: \";\n    if(!via_value.empty()) {\n      hdrs += via_value;\n      http::sanitize_header_value(hdrs, hdrs.size()-via_value.size());\n      hdrs += \", \";\n    }\n    hdrs += http::create_via_header_value(downstream_->get_request_major(),\n                                          downstream_->get_request_minor());\n    hdrs += \"\\r\\n\";\n  }\n\n  hdrs += \"\\r\\n\";\n  if(LOG_ENABLED(INFO)) {\n    const char *hdrp;\n    std::string nhdrs;\n    if(get_config()->tty) {\n      nhdrs = http::colorizeHeaders(hdrs.c_str());\n      hdrp = nhdrs.c_str();\n    } else {\n      hdrp = hdrs.c_str();\n    }\n    DCLOG(INFO, this) << \"HTTP request headers. stream_id=\"\n                      << downstream_->get_stream_id() << \"\\n\" << hdrp;\n  }\n  evbuffer *output = bufferevent_get_output(bev_);\n  int rv;\n  rv = evbuffer_add(output, hdrs.c_str(), hdrs.size());\n  if(rv != 0) {\n    return -1;\n  }\n\n  // When downstream request is issued, set read timeout. We don't\n  // know when the request is completely received by the downstream\n  // server. This function may be called before that happens. Overall\n  // it does not cause problem for most of the time.  If the\n  // downstream server is too slow to recv/send, the connection will\n  // be dropped by read timeout.\n  bufferevent_set_timeouts(bev_,\n                           &get_config()->downstream_read_timeout,\n                           &get_config()->downstream_write_timeout);\n\n  return 0;\n}\n\nint HttpDownstreamConnection::push_upload_data_chunk\n(const uint8_t *data, size_t datalen)\n{\n  ssize_t res = 0;\n  int rv;\n  int chunked = downstream_->get_chunked_request();\n  evbuffer *output = bufferevent_get_output(bev_);\n  if(chunked) {\n    char chunk_size_hex[16];\n    rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), \"%X\\r\\n\",\n                  static_cast<unsigned int>(datalen));\n    res += rv;\n    rv = evbuffer_add(output, chunk_size_hex, rv);\n    if(rv == -1) {\n      DCLOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  rv = evbuffer_add(output, data, datalen);\n  if(rv == -1) {\n    DCLOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  res += rv;\n  if(chunked) {\n    rv = evbuffer_add(output, \"\\r\\n\", 2);\n    if(rv == -1) {\n      DCLOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n    res += 2;\n  }\n  return res;\n}\n\nint HttpDownstreamConnection::end_upload_data()\n{\n  if(downstream_->get_chunked_request()) {\n    evbuffer *output = bufferevent_get_output(bev_);\n    if(evbuffer_add(output, \"0\\r\\n\\r\\n\", 5) != 0) {\n      DCLOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  return 0;\n}\n\nnamespace {\n// Gets called when DownstreamConnection is pooled in ClientHandler.\nvoid idle_eventcb(bufferevent *bev, short events, void *arg)\n{\n  HttpDownstreamConnection *dconn;\n  dconn = static_cast<HttpDownstreamConnection*>(arg);\n  if(events & BEV_EVENT_CONNECTED) {\n    // Downstream was detached before connection established?\n    // This may be safe to be left.\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Idle connection connected?\";\n    }\n    return;\n  }\n  if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Idle connection EOF\";\n    }\n  } else if(events & BEV_EVENT_TIMEOUT) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Idle connection timeout\";\n    }\n  } else if(events & BEV_EVENT_ERROR) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Idle connection network error\";\n    }\n  }\n  ClientHandler *client_handler = dconn->get_client_handler();\n  client_handler->remove_downstream_connection(dconn);\n  delete dconn;\n}\n} // namespace\n\nvoid HttpDownstreamConnection::detach_downstream(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Detaching from DOWNSTREAM:\" << downstream;\n  }\n  downstream->set_downstream_connection(0);\n  downstream_ = 0;\n  ioctrl_.force_resume_read();\n  bufferevent_enable(bev_, EV_READ);\n  bufferevent_setcb(bev_, 0, 0, idle_eventcb, this);\n  // On idle state, just enable read timeout. Normally idle downstream\n  // connection will get EOF from the downstream server and closed.\n  bufferevent_set_timeouts(bev_,\n                           &get_config()->downstream_idle_read_timeout,\n                           &get_config()->downstream_write_timeout);\n  client_handler_->pool_downstream_connection(this);\n}\n\nbufferevent* HttpDownstreamConnection::get_bev()\n{\n  return bev_;\n}\n\nvoid HttpDownstreamConnection::pause_read(IOCtrlReason reason)\n{\n  ioctrl_.pause_read(reason);\n}\n\nint HttpDownstreamConnection::resume_read(IOCtrlReason reason)\n{\n  ioctrl_.resume_read(reason);\n  return 0;\n}\n\nvoid HttpDownstreamConnection::force_resume_read()\n{\n  ioctrl_.force_resume_read();\n}\n\nbool HttpDownstreamConnection::get_output_buffer_full()\n{\n  evbuffer *output = bufferevent_get_output(bev_);\n  return evbuffer_get_length(output) >= OUTBUF_MAX_THRES;\n}\n\nnamespace {\nint htp_hdrs_completecb(http_parser *htp)\n{\n  Downstream *downstream;\n  downstream = static_cast<Downstream*>(htp->data);\n  downstream->set_response_http_status(htp->status_code);\n  downstream->set_response_major(htp->http_major);\n  downstream->set_response_minor(htp->http_minor);\n  downstream->set_response_connection_close(!http_should_keep_alive(htp));\n  downstream->set_response_state(Downstream::HEADER_COMPLETE);\n  if(downstream->tunnel_established()) {\n    downstream->set_response_connection_close(true);\n  }\n  if(downstream->get_upstream()->on_downstream_header_complete(downstream)\n     != 0) {\n    return -1;\n  }\n  unsigned int status = downstream->get_response_http_status();\n  // Ignore the response body. HEAD response may contain\n  // Content-Length or Transfer-Encoding: chunked.  Some server send\n  // 304 status code with nonzero Content-Length, but without response\n  // body. See\n  // http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-20#section-3.3\n  return downstream->get_request_method() == \"HEAD\" ||\n    (100 <= status && status <= 199) || status == 204 ||\n    status == 304 ? 1 : 0;\n}\n} // namespace\n\nnamespace {\nint htp_hdr_keycb(http_parser *htp, const char *data, size_t len)\n{\n  Downstream *downstream;\n  downstream = static_cast<Downstream*>(htp->data);\n  if(downstream->get_response_header_key_prev()) {\n    downstream->append_last_response_header_key(data, len);\n  } else {\n    downstream->add_response_header(std::string(data, len), \"\");\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_hdr_valcb(http_parser *htp, const char *data, size_t len)\n{\n  Downstream *downstream;\n  downstream = static_cast<Downstream*>(htp->data);\n  if(downstream->get_response_header_key_prev()) {\n    downstream->set_last_response_header_value(std::string(data, len));\n  } else {\n    downstream->append_last_response_header_value(data, len);\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_bodycb(http_parser *htp, const char *data, size_t len)\n{\n  Downstream *downstream;\n  downstream = static_cast<Downstream*>(htp->data);\n\n  return downstream->get_upstream()->on_downstream_body\n    (downstream, reinterpret_cast<const uint8_t*>(data), len);\n}\n} // namespace\n\nnamespace {\nint htp_msg_completecb(http_parser *htp)\n{\n  Downstream *downstream;\n  downstream = static_cast<Downstream*>(htp->data);\n\n  downstream->set_response_state(Downstream::MSG_COMPLETE);\n  // Block reading another response message from (broken?)\n  // server. This callback is not called if the connection is\n  // tunneled.\n  downstream->pause_read(SHRPX_MSG_BLOCK);\n  return downstream->get_upstream()->on_downstream_body_complete(downstream);\n}\n} // namespace\n\nnamespace {\nhttp_parser_settings htp_hooks = {\n  0, /*http_cb      on_message_begin;*/\n  0, /*http_data_cb on_url;*/\n  0, /*http_data_cb on_status */\n  htp_hdr_keycb, /*http_data_cb on_header_field;*/\n  htp_hdr_valcb, /*http_data_cb on_header_value;*/\n  htp_hdrs_completecb, /*http_cb      on_headers_complete;*/\n  htp_bodycb, /*http_data_cb on_body;*/\n  htp_msg_completecb /*http_cb      on_message_complete;*/\n};\n} // namespace\n\nint HttpDownstreamConnection::on_read()\n{\n  evbuffer *input = bufferevent_get_input(bev_);\n  unsigned char *mem = evbuffer_pullup(input, -1);\n\n  size_t nread = http_parser_execute(response_htp_, &htp_hooks,\n                                     reinterpret_cast<const char*>(mem),\n                                     evbuffer_get_length(input));\n\n  evbuffer_drain(input, nread);\n  http_errno htperr = HTTP_PARSER_ERRNO(response_htp_);\n  if(htperr == HPE_OK) {\n    return 0;\n  } else {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, this) << \"HTTP parser failure: \"\n                        << \"(\" << http_errno_name(htperr) << \") \"\n                        << http_errno_description(htperr);\n    }\n    return SHRPX_ERR_HTTP_PARSE;\n  }\n}\n\nint HttpDownstreamConnection::on_write()\n{\n  return 0;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_http_downstream_connection.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_HTTP_DOWNSTREAM_CONNECTION_H\n#define SHRPX_HTTP_DOWNSTREAM_CONNECTION_H\n\n#include \"shrpx.h\"\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\n#include \"http-parser/http_parser.h\"\n\n#include \"shrpx_downstream_connection.h\"\n#include \"shrpx_io_control.h\"\n\nnamespace shrpx {\n\nclass HttpDownstreamConnection : public DownstreamConnection {\npublic:\n  HttpDownstreamConnection(ClientHandler *client_handler);\n  virtual ~HttpDownstreamConnection();\n  virtual int attach_downstream(Downstream *downstream);\n  virtual void detach_downstream(Downstream *downstream);\n\n  virtual int push_request_headers();\n  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);\n  virtual int end_upload_data();\n\n  virtual void pause_read(IOCtrlReason reason);\n  virtual int resume_read(IOCtrlReason reason);\n  virtual void force_resume_read();\n\n  virtual bool get_output_buffer_full();\n\n  virtual int on_read();\n  virtual int on_write();\n\n  bufferevent* get_bev();\nprivate:\n  bufferevent *bev_;\n  IOControl ioctrl_;\n  http_parser *response_htp_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_HTTP_DOWNSTREAM_CONNECTION_H\n"
  },
  {
    "path": "src/shrpx_https_upstream.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_https_upstream.h\"\n\n#include <cassert>\n#include <set>\n#include <sstream>\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_downstream.h\"\n#include \"shrpx_downstream_connection.h\"\n#include \"shrpx_spdy_downstream_connection.h\"\n#include \"shrpx_http.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_error.h\"\n#include \"shrpx_accesslog.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nnamespace {\nconst size_t OUTBUF_MAX_THRES = 64*1024;\nconst size_t SHRPX_HTTPS_MAX_HEADER_LENGTH = 64*1024;\n} // namespace\n\nHttpsUpstream::HttpsUpstream(ClientHandler *handler)\n  : handler_(handler),\n    htp_(new http_parser()),\n    current_header_length_(0),\n    downstream_(0),\n    ioctrl_(handler->get_bev())\n{\n  http_parser_init(htp_, HTTP_REQUEST);\n  htp_->data = this;\n}\n\nHttpsUpstream::~HttpsUpstream()\n{\n  delete htp_;\n  delete downstream_;\n}\n\nvoid HttpsUpstream::reset_current_header_length()\n{\n  current_header_length_ = 0;\n}\n\nnamespace {\nint htp_msg_begin(http_parser *htp)\n{\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"HTTP request started\";\n  }\n  upstream->reset_current_header_length();\n  Downstream *downstream = new Downstream(upstream, 0, -1);\n  upstream->attach_downstream(downstream);\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_uricb(http_parser *htp, const char *data, size_t len)\n{\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  Downstream *downstream = upstream->get_downstream();\n  downstream->append_request_path(data, len);\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_hdr_keycb(http_parser *htp, const char *data, size_t len)\n{\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  Downstream *downstream = upstream->get_downstream();\n  if(downstream->get_request_header_key_prev()) {\n    downstream->append_last_request_header_key(data, len);\n  } else {\n    downstream->add_request_header(std::string(data, len), \"\");\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_hdr_valcb(http_parser *htp, const char *data, size_t len)\n{\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  Downstream *downstream = upstream->get_downstream();\n  if(downstream->get_request_header_key_prev()) {\n    downstream->set_last_request_header_value(std::string(data, len));\n  } else {\n    downstream->append_last_request_header_value(data, len);\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_hdrs_completecb(http_parser *htp)\n{\n  int rv;\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"HTTP request headers completed\";\n  }\n  Downstream *downstream = upstream->get_downstream();\n\n  downstream->set_request_method(http_method_str((enum http_method)htp->method));\n  downstream->set_request_major(htp->http_major);\n  downstream->set_request_minor(htp->http_minor);\n\n  downstream->set_request_connection_close(!http_should_keep_alive(htp));\n\n  if(LOG_ENABLED(INFO)) {\n    std::stringstream ss;\n    ss << downstream->get_request_method() << \" \"\n       << downstream->get_request_path() << \" \"\n       << \"HTTP/\" << downstream->get_request_major() << \".\"\n       << downstream->get_request_minor() << \"\\n\";\n    const Headers& headers = downstream->get_request_headers();\n    for(size_t i = 0; i < headers.size(); ++i) {\n      ss << TTY_HTTP_HD << headers[i].first << TTY_RST << \": \"\n         << headers[i].second << \"\\n\";\n    }\n    ULOG(INFO, upstream) << \"HTTP request headers\\n\" << ss.str();\n  }\n\n  if(get_config()->client_proxy &&\n     downstream->get_request_method() != \"CONNECT\") {\n    // Make sure that request path is an absolute URI.\n    http_parser_url u;\n    const char *url = downstream->get_request_path().c_str();\n    memset(&u, 0, sizeof(u));\n    rv = http_parser_parse_url(url,\n                               downstream->get_request_path().size(),\n                               0, &u);\n    if(rv != 0 || !(u.field_set & (1 << UF_SCHEMA))) {\n      // Expect to respond with 400 bad request\n      return -1;\n    }\n  }\n\n  DownstreamConnection *dconn;\n  dconn = upstream->get_client_handler()->get_downstream_connection();\n\n  if(downstream->get_expect_100_continue()) {\n    static const char reply_100[] = \"HTTP/1.1 100 Continue\\r\\n\\r\\n\";\n    if(bufferevent_write(upstream->get_client_handler()->get_bev(),\n                         reply_100, sizeof(reply_100)-1) != 0) {\n      ULOG(FATAL, upstream) << \"bufferevent_write() faild\";\n      delete dconn;\n      return -1;\n    }\n  }\n\n  rv =  dconn->attach_downstream(downstream);\n  if(rv != 0) {\n    downstream->set_request_state(Downstream::CONNECT_FAIL);\n    downstream->set_downstream_connection(0);\n    delete dconn;\n    return -1;\n  } else {\n    rv = downstream->push_request_headers();\n    if(rv != 0) {\n      return -1;\n    }\n    downstream->set_request_state(Downstream::HEADER_COMPLETE);\n    return 0;\n  }\n}\n} // namespace\n\nnamespace {\nint htp_bodycb(http_parser *htp, const char *data, size_t len)\n{\n  int rv;\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  Downstream *downstream = upstream->get_downstream();\n  rv = downstream->push_upload_data_chunk\n    (reinterpret_cast<const uint8_t*>(data), len);\n  if(rv != 0) {\n    return -1;\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nint htp_msg_completecb(http_parser *htp)\n{\n  int rv;\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(htp->data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"HTTP request completed\";\n  }\n  Downstream *downstream = upstream->get_downstream();\n  downstream->set_request_state(Downstream::MSG_COMPLETE);\n  rv = downstream->end_upload_data();\n  if(rv != 0) {\n    return -1;\n  }\n  // Stop further processing to complete this request\n  http_parser_pause(htp, 1);\n  return 0;\n}\n} // namespace\n\nnamespace {\nhttp_parser_settings htp_hooks = {\n  htp_msg_begin, /*http_cb      on_message_begin;*/\n  htp_uricb, /*http_data_cb on_url;*/\n  0, /*http_data_cb on_status */\n  htp_hdr_keycb, /*http_data_cb on_header_field;*/\n  htp_hdr_valcb, /*http_data_cb on_header_value;*/\n  htp_hdrs_completecb, /*http_cb      on_headers_complete;*/\n  htp_bodycb, /*http_data_cb on_body;*/\n  htp_msg_completecb /*http_cb      on_message_complete;*/\n};\n} // namespace\n\n\n// on_read() does not consume all available data in input buffer if\n// one http request is fully received.\nint HttpsUpstream::on_read()\n{\n  bufferevent *bev = handler_->get_bev();\n  evbuffer *input = bufferevent_get_input(bev);\n\n  unsigned char *mem = evbuffer_pullup(input, -1);\n\n  if(evbuffer_get_length(input) == 0) {\n    return 0;\n  }\n\n  size_t nread = http_parser_execute(htp_, &htp_hooks,\n                                     reinterpret_cast<const char*>(mem),\n                                     evbuffer_get_length(input));\n  evbuffer_drain(input, nread);\n  // Well, actually header length + some body bytes\n  current_header_length_ += nread;\n  Downstream *downstream = get_downstream();\n\n  http_errno htperr = HTTP_PARSER_ERRNO(htp_);\n  if(htperr == HPE_PAUSED) {\n    if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {\n      get_client_handler()->set_should_close_after_write(true);\n      // Following paues_read is needed to avoid reading next data.\n      pause_read(SHRPX_MSG_BLOCK);\n      if(error_reply(503) != 0) {\n        return -1;\n      }\n      // Downstream gets deleted after response body is read.\n    } else {\n      assert(downstream->get_request_state() == Downstream::MSG_COMPLETE);\n      if(downstream->get_downstream_connection() == 0) {\n        // Error response has already be sent\n        assert(downstream->get_response_state() == Downstream::MSG_COMPLETE);\n        delete_downstream();\n      } else {\n        pause_read(SHRPX_MSG_BLOCK);\n      }\n    }\n  } else if(htperr == HPE_OK) {\n    // downstream can be NULL here.\n    if(downstream) {\n      if(downstream->get_request_state() == Downstream::INITIAL &&\n         current_header_length_ > SHRPX_HTTPS_MAX_HEADER_LENGTH) {\n        ULOG(WARNING, this) << \"Request Header too long:\"\n                            << current_header_length_\n                            << \" bytes\";\n        get_client_handler()->set_should_close_after_write(true);\n        pause_read(SHRPX_MSG_BLOCK);\n        if(error_reply(400) != 0) {\n          return -1;\n        }\n      } else if(downstream->get_output_buffer_full()) {\n        if(LOG_ENABLED(INFO)) {\n          ULOG(INFO, this) << \"Downstream output buffer is full\";\n        }\n        pause_read(SHRPX_NO_BUFFER);\n      }\n    }\n  } else {\n    if(LOG_ENABLED(INFO)) {\n      ULOG(INFO, this) << \"HTTP parse failure: \"\n                       << \"(\" << http_errno_name(htperr) << \") \"\n                       << http_errno_description(htperr);\n    }\n    get_client_handler()->set_should_close_after_write(true);\n    pause_read(SHRPX_MSG_BLOCK);\n    if(error_reply(400) != 0) {\n      return -1;\n    }\n  }\n  return 0;\n}\n\nint HttpsUpstream::on_write()\n{\n  int rv = 0;\n  Downstream *downstream = get_downstream();\n  if(downstream) {\n    rv = downstream->resume_read(SHRPX_NO_BUFFER);\n  }\n  return rv;\n}\n\nint HttpsUpstream::on_event()\n{\n  return 0;\n}\n\nClientHandler* HttpsUpstream::get_client_handler() const\n{\n  return handler_;\n}\n\nvoid HttpsUpstream::pause_read(IOCtrlReason reason)\n{\n  ioctrl_.pause_read(reason);\n}\n\nint HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream)\n{\n  if(ioctrl_.resume_read(reason)) {\n    // Process remaining data in input buffer here because these bytes\n    // are not notified by readcb until new data arrive.\n    http_parser_pause(htp_, 0);\n    return on_read();\n  } else {\n    return 0;\n  }\n}\n\nnamespace {\nvoid https_downstream_readcb(bufferevent *bev, void *ptr)\n{\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());\n  int rv;\n  rv = downstream->on_read();\n  if(downstream->get_response_state() == Downstream::MSG_RESET) {\n    delete upstream->get_client_handler();\n  } else if(rv == 0) {\n    if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {\n      if(downstream->get_response_connection_close()) {\n        // Connection close\n        downstream->set_downstream_connection(0);\n        delete dconn;\n        dconn = 0;\n      } else {\n        // Keep-alive\n        dconn->detach_downstream(downstream);\n      }\n      ClientHandler *handler = upstream->get_client_handler();\n      if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {\n        if(handler->get_should_close_after_write() &&\n           handler->get_outbuf_length() == 0) {\n          // If all upstream response body has already written out to\n          // the peer, we cannot use writecb for ClientHandler. In\n          // this case, we just delete handler here.\n          delete handler;\n          return;\n        } else {\n          upstream->delete_downstream();\n          // Process next HTTP request\n          if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {\n            return;\n          }\n        }\n      } else if(downstream->tunnel_established()) {\n        // This path is effectively only taken for SPDY downstream\n        // because only SPDY downstream sets response_state to\n        // MSG_COMPLETE and this function. For HTTP downstream, EOF\n        // from tunnel connection is handled on\n        // https_downstream_eventcb.\n        //\n        // Tunneled connection always indicates connection close.\n        if(handler->get_outbuf_length() == 0) {\n          // For tunneled connection, if there is no pending data,\n          // delete handler because on_write will not be called.\n          delete handler;\n        } else {\n          if(LOG_ENABLED(INFO)) {\n            DLOG(INFO, downstream) << \"Tunneled connection has pending data\";\n          }\n        }\n      }\n    } else {\n      if(upstream->get_client_handler()->get_outbuf_length() >\n         OUTBUF_MAX_THRES) {\n        downstream->pause_read(SHRPX_NO_BUFFER);\n      }\n    }\n  } else {\n    if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n      // We already sent HTTP response headers to upstream\n      // client. Just close the upstream connection.\n      delete upstream->get_client_handler();\n    } else {\n      // We did not sent any HTTP response, so sent error\n      // response. Cannot reuse downstream connection in this case.\n      if(upstream->error_reply(502) != 0) {\n        delete upstream->get_client_handler();\n        return;\n      }\n      if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {\n        upstream->delete_downstream();\n        // Process next HTTP request\n        if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {\n          return;\n        }\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid https_downstream_writecb(bufferevent *bev, void *ptr)\n{\n  if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {\n    return;\n  }\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());\n  // May return -1\n  upstream->resume_read(SHRPX_NO_BUFFER, downstream);\n}\n} // namespace\n\nnamespace {\nvoid https_downstream_eventcb(bufferevent *bev, short events, void *ptr)\n{\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  HttpsUpstream *upstream;\n  upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());\n  if(events & BEV_EVENT_CONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Connection established\";\n    }\n  } else if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"EOF\";\n    }\n    if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n      // Server may indicate the end of the request by EOF\n      if(LOG_ENABLED(INFO)) {\n        DCLOG(INFO, dconn) << \"The end of the response body was indicated by \"\n                           << \"EOF\";\n      }\n      upstream->on_downstream_body_complete(downstream);\n      downstream->set_response_state(Downstream::MSG_COMPLETE);\n\n      ClientHandler *handler = upstream->get_client_handler();\n      if(handler->get_should_close_after_write() &&\n         handler->get_outbuf_length() == 0) {\n        // If all upstream response body has already written out to\n        // the peer, we cannot use writecb for ClientHandler. In this\n        // case, we just delete handler here.\n        delete handler;\n        return;\n      }\n    } else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {\n      // Nothing to do\n    } else {\n      // error\n      if(LOG_ENABLED(INFO)) {\n        DCLOG(INFO, dconn) << \"Treated as error\";\n      }\n      if(upstream->error_reply(502) != 0) {\n        delete upstream->get_client_handler();\n        return;\n      }\n    }\n    if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {\n      upstream->delete_downstream();\n      if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {\n        return;\n      }\n    }\n  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {\n    if(LOG_ENABLED(INFO)) {\n      if(events & BEV_EVENT_ERROR) {\n        DCLOG(INFO, dconn) << \"Network error\";\n      } else {\n        DCLOG(INFO, dconn) << \"Timeout\";\n      }\n    }\n    if(downstream->get_response_state() == Downstream::INITIAL) {\n      unsigned int status;\n      if(events & BEV_EVENT_TIMEOUT) {\n        status = 504;\n      } else {\n        status = 502;\n      }\n      if(upstream->error_reply(status) != 0) {\n        delete upstream->get_client_handler();\n        return;\n      }\n    }\n    if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {\n      upstream->delete_downstream();\n      if(upstream->resume_read(SHRPX_MSG_BLOCK, 0) == -1) {\n        return;\n      }\n    }\n  }\n}\n} // namespace\n\nint HttpsUpstream::error_reply(unsigned int status_code)\n{\n  std::string html = http::create_error_html(status_code);\n  std::string header;\n  header.reserve(512);\n  header += \"HTTP/1.1 \";\n  header += http::get_status_string(status_code);\n  header += \"\\r\\nServer: \";\n  header += get_config()->server_name;\n  header += \"\\r\\nContent-Length: \";\n  header += util::utos(html.size());\n  header += \"\\r\\nContent-Type: text/html; charset=UTF-8\\r\\n\";\n  if(get_client_handler()->get_should_close_after_write()) {\n    header += \"Connection: close\\r\\n\";\n  }\n  header += \"\\r\\n\";\n  evbuffer *output = bufferevent_get_output(handler_->get_bev());\n  if(evbuffer_add(output, header.c_str(), header.size()) != 0 ||\n     evbuffer_add(output, html.c_str(), html.size()) != 0) {\n    ULOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  Downstream *downstream = get_downstream();\n  if(downstream) {\n    downstream->set_response_state(Downstream::MSG_COMPLETE);\n  }\n  if(get_config()->accesslog) {\n    upstream_response(this->get_client_handler()->get_ipaddr(), status_code,\n                      downstream);\n  }\n  return 0;\n}\n\nbufferevent_data_cb HttpsUpstream::get_downstream_readcb()\n{\n  return https_downstream_readcb;\n}\n\nbufferevent_data_cb HttpsUpstream::get_downstream_writecb()\n{\n  return https_downstream_writecb;\n}\n\nbufferevent_event_cb HttpsUpstream::get_downstream_eventcb()\n{\n  return https_downstream_eventcb;\n}\n\nvoid HttpsUpstream::attach_downstream(Downstream *downstream)\n{\n  assert(!downstream_);\n  downstream_ = downstream;\n}\n\nvoid HttpsUpstream::delete_downstream()\n{\n  delete downstream_;\n  downstream_ = 0;\n}\n\nDownstream* HttpsUpstream::get_downstream() const\n{\n  return downstream_;\n}\n\nint HttpsUpstream::on_downstream_header_complete(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, downstream) << \"HTTP response header completed\";\n  }\n  bool connection_upgrade = false;\n  std::string via_value;\n  char temp[16];\n  snprintf(temp, sizeof(temp), \"HTTP/%d.%d \",\n           downstream->get_request_major(),\n           downstream->get_request_minor());\n  std::string hdrs = temp;\n  hdrs += http::get_status_string(downstream->get_response_http_status());\n  hdrs += \"\\r\\n\";\n  for(Headers::const_iterator i = downstream->get_response_headers().begin();\n      i != downstream->get_response_headers().end(); ++i) {\n    if(util::strieq((*i).first.c_str(), \"connection\")) {\n      if(util::strifind((*i).second.c_str(), \"upgrade\")) {\n        connection_upgrade = true;\n      }\n    } else if(util::strieq((*i).first.c_str(), \"keep-alive\") || // HTTP/1.0?\n       util:: strieq((*i).first.c_str(), \"proxy-connection\")) {\n      // These are ignored\n    } else if(!get_config()->no_via &&\n              util::strieq((*i).first.c_str(), \"via\")) {\n      via_value = (*i).second;\n    } else {\n      hdrs += (*i).first;\n      http::capitalize(hdrs, hdrs.size()-(*i).first.size());\n      hdrs += \": \";\n      hdrs += (*i).second;\n      http::sanitize_header_value(hdrs, hdrs.size()-(*i).second.size());\n      hdrs += \"\\r\\n\";\n    }\n  }\n\n  // We check downstream->get_response_connection_close() in case when\n  // the Content-Length is not available.\n  if(!downstream->get_request_connection_close() &&\n     !downstream->get_response_connection_close()) {\n    if(downstream->get_request_major() <= 0 ||\n       downstream->get_request_minor() <= 0) {\n      // We add this header for HTTP/1.0 or HTTP/0.9 clients\n      hdrs += \"Connection: Keep-Alive\\r\\n\";\n    } else if(connection_upgrade) {\n      hdrs += \"Connection: upgrade\\r\\n\";\n    }\n  } else if(!downstream->tunnel_established()) {\n    hdrs += \"Connection: close\\r\\n\";\n  }\n  if(!get_config()->no_via) {\n    hdrs += \"Via: \";\n    if(!via_value.empty()) {\n      hdrs += via_value;\n      http::sanitize_header_value(hdrs, hdrs.size()-via_value.size());\n      hdrs += \", \";\n    }\n    hdrs += http::create_via_header_value\n      (downstream->get_response_major(), downstream->get_response_minor());\n    hdrs += \"\\r\\n\";\n  }\n\n  hdrs += \"\\r\\n\";\n  if(LOG_ENABLED(INFO)) {\n    const char *hdrp;\n    std::string nhdrs;\n    if(get_config()->tty) {\n      nhdrs = http::colorizeHeaders(hdrs.c_str());\n      hdrp = nhdrs.c_str();\n    } else {\n      hdrp = hdrs.c_str();\n    }\n    ULOG(INFO, this) << \"HTTP response headers\\n\" << hdrp;\n  }\n  evbuffer *output = bufferevent_get_output(handler_->get_bev());\n  if(evbuffer_add(output, hdrs.c_str(), hdrs.size()) != 0) {\n    ULOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  if(get_config()->accesslog) {\n    upstream_response(this->get_client_handler()->get_ipaddr(),\n                      downstream->get_response_http_status(), downstream);\n  }\n  return 0;\n}\n\nint HttpsUpstream::on_downstream_body(Downstream *downstream,\n                                      const uint8_t *data, size_t len)\n{\n  int rv;\n  if(len == 0) {\n    return 0;\n  }\n  evbuffer *output = bufferevent_get_output(handler_->get_bev());\n  if(downstream->get_chunked_response()) {\n    char chunk_size_hex[16];\n    rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), \"%X\\r\\n\",\n                  static_cast<unsigned int>(len));\n    if(evbuffer_add(output, chunk_size_hex, rv) != 0) {\n      ULOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  if(evbuffer_add(output, data, len) != 0) {\n    ULOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  if(downstream->get_chunked_response()) {\n    if(evbuffer_add(output, \"\\r\\n\", 2) != 0) {\n      ULOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  return 0;\n}\n\nint HttpsUpstream::on_downstream_body_complete(Downstream *downstream)\n{\n  if(downstream->get_chunked_response()) {\n    evbuffer *output = bufferevent_get_output(handler_->get_bev());\n    if(evbuffer_add(output, \"0\\r\\n\\r\\n\", 5) != 0) {\n      ULOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, downstream) << \"HTTP response completed\";\n  }\n  if(downstream->get_request_connection_close() ||\n     downstream->get_response_connection_close()) {\n    ClientHandler *handler = get_client_handler();\n    handler->set_should_close_after_write(true);\n  }\n  return 0;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_https_upstream.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_HTTPS_UPSTREAM_H\n#define SHRPX_HTTPS_UPSTREAM_H\n\n#include \"shrpx.h\"\n\n#include <stdint.h>\n\n#include \"http-parser/http_parser.h\"\n\n#include \"shrpx_upstream.h\"\n\nnamespace shrpx {\n\nclass ClientHandler;\n\nclass HttpsUpstream : public Upstream {\npublic:\n  HttpsUpstream(ClientHandler *handler);\n  virtual ~HttpsUpstream();\n  virtual int on_read();\n  virtual int on_write();\n  virtual int on_event();\n  //int send();\n  virtual ClientHandler* get_client_handler() const;\n  virtual bufferevent_data_cb get_downstream_readcb();\n  virtual bufferevent_data_cb get_downstream_writecb();\n  virtual bufferevent_event_cb get_downstream_eventcb();\n  void attach_downstream(Downstream *downstream);\n  void delete_downstream();\n  Downstream* get_downstream() const;\n  int error_reply(unsigned int status_code);\n\n  virtual void pause_read(IOCtrlReason reason);\n  virtual int resume_read(IOCtrlReason reason, Downstream *downstream);\n\n  virtual int on_downstream_header_complete(Downstream *downstream);\n  virtual int on_downstream_body(Downstream *downstream,\n                                 const uint8_t *data, size_t len);\n  virtual int on_downstream_body_complete(Downstream *downstream);\n\n  void reset_current_header_length();\nprivate:\n  ClientHandler *handler_;\n  http_parser *htp_;\n  size_t current_header_length_;\n  Downstream *downstream_;\n  IOControl ioctrl_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_HTTPS_UPSTREAM_H\n"
  },
  {
    "path": "src/shrpx_io_control.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_io_control.h\"\n\n#include <algorithm>\n\nnamespace shrpx {\n\nIOControl::IOControl(bufferevent *bev)\n  : bev_(bev),\n    rdbits_(0)\n{}\n\nIOControl::~IOControl()\n{}\n\nvoid IOControl::set_bev(bufferevent *bev)\n{\n  bev_ = bev;\n}\n\nvoid IOControl::pause_read(IOCtrlReason reason)\n{\n  rdbits_ |= reason;\n  if(bev_) {\n    bufferevent_disable(bev_, EV_READ);\n  }\n}\n\nbool IOControl::resume_read(IOCtrlReason reason)\n{\n  rdbits_ &= ~reason;\n  if(rdbits_ == 0) {\n    if(bev_) {\n      bufferevent_enable(bev_, EV_READ);\n    }\n    return true;\n  } else {\n    return false;\n  }\n}\n\nvoid IOControl::force_resume_read()\n{\n  rdbits_ = 0;\n  if(bev_) {\n    bufferevent_enable(bev_, EV_READ);\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_io_control.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_IO_CONTROL_H\n#define SHRPX_IO_CONTROL_H\n\n#include \"shrpx.h\"\n\n#include <vector>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\nnamespace shrpx {\n\nenum IOCtrlReason {\n  SHRPX_NO_BUFFER = 1 << 0,\n  SHRPX_MSG_BLOCK = 1 << 1\n};\n\nclass IOControl {\npublic:\n  IOControl(bufferevent *bev);\n  ~IOControl();\n  void set_bev(bufferevent *bev);\n  void pause_read(IOCtrlReason reason);\n  // Returns true if read operation is enabled after this call\n  bool resume_read(IOCtrlReason reason);\n  // Clear all pause flags and enable read\n  void force_resume_read();\nprivate:\n  bufferevent *bev_;\n  uint32_t rdbits_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_IO_CONTROL_H\n"
  },
  {
    "path": "src/shrpx_listen_handler.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_listen_handler.h\"\n\n#include <unistd.h>\n#include <pthread.h>\n\n#include <cerrno>\n#include <cstring>\n\n#include <event2/bufferevent_ssl.h>\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_thread_event_receiver.h\"\n#include \"shrpx_ssl.h\"\n#include \"shrpx_worker.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_spdy_session.h\"\n\nnamespace shrpx {\n\nListenHandler::ListenHandler(event_base *evbase, SSL_CTX *sv_ssl_ctx,\n                             SSL_CTX *cl_ssl_ctx)\n  : evbase_(evbase),\n    sv_ssl_ctx_(sv_ssl_ctx),\n    cl_ssl_ctx_(cl_ssl_ctx),\n    worker_round_robin_cnt_(0),\n    workers_(0),\n    num_worker_(0),\n    spdy_(0),\n    rate_limit_group_(bufferevent_rate_limit_group_new\n                      (evbase, get_config()->worker_rate_limit_cfg))\n{}\n\nListenHandler::~ListenHandler()\n{\n  bufferevent_rate_limit_group_free(rate_limit_group_);\n}\n\nvoid ListenHandler::create_worker_thread(size_t num)\n{\n  workers_ = new WorkerInfo[num];\n  num_worker_ = 0;\n  for(size_t i = 0; i < num; ++i) {\n    int rv;\n    pthread_t thread;\n    pthread_attr_t attr;\n    pthread_attr_init(&attr);\n    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n    WorkerInfo *info = &workers_[num_worker_];\n    rv = socketpair(AF_UNIX, SOCK_STREAM, 0, info->sv);\n    if(rv == -1) {\n      LLOG(ERROR, this) << \"socketpair() failed: errno=\" << errno;\n      continue;\n    }\n    info->sv_ssl_ctx = sv_ssl_ctx_;\n    info->cl_ssl_ctx = cl_ssl_ctx_;\n    rv = pthread_create(&thread, &attr, start_threaded_worker, info);\n    if(rv != 0) {\n      LLOG(ERROR, this) << \"pthread_create() failed: errno=\" << rv;\n      for(size_t j = 0; j < 2; ++j) {\n        close(info->sv[j]);\n      }\n      continue;\n    }\n    bufferevent *bev = bufferevent_socket_new(evbase_, info->sv[0],\n                                              BEV_OPT_DEFER_CALLBACKS);\n    info->bev = bev;\n    if(LOG_ENABLED(INFO)) {\n      LLOG(INFO, this) << \"Created thread #\" << num_worker_;\n    }\n    ++num_worker_;\n  }\n}\n\nint ListenHandler::accept_connection(evutil_socket_t fd,\n                                     sockaddr *addr, int addrlen)\n{\n  if(LOG_ENABLED(INFO)) {\n    LLOG(INFO, this) << \"Accepted connection. fd=\" << fd;\n  }\n  if(num_worker_ == 0) {\n    ClientHandler* client = ssl::accept_connection(evbase_, rate_limit_group_,\n                                                   sv_ssl_ctx_,\n                                                   fd, addr, addrlen);\n    client->set_spdy_session(spdy_);\n  } else {\n    size_t idx = worker_round_robin_cnt_ % num_worker_;\n    ++worker_round_robin_cnt_;\n    WorkerEvent wev;\n    memset(&wev, 0, sizeof(wev));\n    wev.client_fd = fd;\n    memcpy(&wev.client_addr, addr, addrlen);\n    wev.client_addrlen = addrlen;\n    evbuffer *output = bufferevent_get_output(workers_[idx].bev);\n    if(evbuffer_add(output, &wev, sizeof(wev)) != 0) {\n      LLOG(FATAL, this) << \"evbuffer_add() failed\";\n      return -1;\n    }\n  }\n  return 0;\n}\n\nevent_base* ListenHandler::get_evbase() const\n{\n  return evbase_;\n}\n\nint ListenHandler::create_spdy_session()\n{\n  int rv;\n  spdy_ = new SpdySession(evbase_, cl_ssl_ctx_);\n  rv = spdy_->init_notification();\n  return rv;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_listen_handler.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_LISTEN_HANDLER_H\n#define SHRPX_LISTEN_HANDLER_H\n\n#include \"shrpx.h\"\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include <openssl/ssl.h>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\nnamespace shrpx {\n\nstruct WorkerInfo {\n  int sv[2];\n  SSL_CTX *sv_ssl_ctx;\n  SSL_CTX *cl_ssl_ctx;\n  bufferevent *bev;\n};\n\nclass SpdySession;\n\nclass ListenHandler {\npublic:\n  ListenHandler(event_base *evbase, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx);\n  ~ListenHandler();\n  int accept_connection(evutil_socket_t fd, sockaddr *addr, int addrlen);\n  void create_worker_thread(size_t num);\n  event_base* get_evbase() const;\n  int create_spdy_session();\nprivate:\n  event_base *evbase_;\n  // The frontend server SSL_CTX\n  SSL_CTX *sv_ssl_ctx_;\n  // The backend server SSL_CTX\n  SSL_CTX *cl_ssl_ctx_;\n  unsigned int worker_round_robin_cnt_;\n  WorkerInfo *workers_;\n  size_t num_worker_;\n  // Shared backend SPDY session. NULL if multi-threaded. In\n  // multi-threaded case, see shrpx_worker.cc.\n  SpdySession *spdy_;\n  bufferevent_rate_limit_group *rate_limit_group_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_LISTEN_HANDLER_H\n"
  },
  {
    "path": "src/shrpx_log.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_log.h\"\n\n#include <syslog.h>\n\n#include <cstdio>\n#include <cstring>\n\n#include \"shrpx_config.h\"\n\nnamespace shrpx {\n\nnamespace {\nconst char *SEVERITY_STR[] = {\n  \"INFO\", \"WARN\", \"ERROR\", \"FATAL\"\n};\n} // namespace\n\nnamespace {\nconst char *SEVERITY_COLOR[] = {\n  \"\\033[1;32m\", // INFO\n  \"\\033[1;33m\", // WARN\n  \"\\033[1;31m\", // ERROR\n  \"\\033[1;35m\", // FATAL\n};\n} // namespace\n\nint Log::severity_thres_ = WARNING;\n\nvoid Log::set_severity_level(int severity)\n{\n  severity_thres_ = severity;\n}\n\nint Log::set_severity_level_by_name(const char *name)\n{\n  for(size_t i = 0, max = sizeof(SEVERITY_STR)/sizeof(char*); i < max;  ++i) {\n    if(strcmp(SEVERITY_STR[i], name) == 0) {\n      severity_thres_ = i;\n      return 0;\n    }\n  }\n  return -1;\n}\n\nint severity_to_syslog_level(int severity)\n{\n  switch(severity) {\n  case(INFO):\n    return LOG_INFO;\n  case(WARNING):\n    return LOG_WARNING;\n  case(ERROR):\n    return LOG_ERR;\n  case(FATAL):\n    return LOG_CRIT;\n  default:\n    return -1;\n  }\n}\n\nLog::Log(int severity, const char *filename, int linenum)\n  : filename_(filename),\n    severity_(severity),\n    linenum_(linenum)\n{}\n\nLog::~Log()\n{\n  if(log_enabled(severity_)) {\n    fprintf(stderr, \"[%s%s%s] %s\\n       %s(%s:%d)%s\\n\",\n            get_config()->tty ? SEVERITY_COLOR[severity_] : \"\",\n            SEVERITY_STR[severity_],\n            get_config()->tty ? \"\\033[0m\" : \"\",\n            stream_.str().c_str(),\n            get_config()->tty ? \"\\033[1;30m\" : \"\",\n            filename_, linenum_,\n            get_config()->tty ? \"\\033[0m\" : \"\");\n    fflush(stderr);\n    if(get_config()->use_syslog) {\n      syslog(severity_to_syslog_level(severity_), \"%s (%s:%d)\\n\",\n             stream_.str().c_str(), filename_, linenum_);\n    }\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_log.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_LOG_H\n#define SHRPX_LOG_H\n\n#include \"shrpx.h\"\n\n#include <sstream>\n\nnamespace shrpx {\n\n#define ENABLE_LOG 1\n\n#define LOG_ENABLED(SEVERITY) (ENABLE_LOG && Log::log_enabled(SEVERITY))\n\n#define LOG(SEVERITY) Log(SEVERITY, __FILE__, __LINE__)\n\n// Listener log\n#define LLOG(SEVERITY, LISTEN)                                       \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[LISTEN:\" << LISTEN         \\\n   << \"] \")\n\n// ThreadEventReceiver log\n#define TLOG(SEVERITY, THREAD_RECV)                                     \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[THREAD_RECV:\" << THREAD_RECV  \\\n   << \"] \")\n\n// ClientHandler log\n#define CLOG(SEVERITY, CLIENT_HANDLER)                                  \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[CLIENT_HANDLER:\" << CLIENT_HANDLER \\\n   << \"] \")\n\n// Upstream log\n#define ULOG(SEVERITY, UPSTREAM)                                        \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[UPSTREAM:\" << UPSTREAM << \"] \")\n\n// Downstream log\n#define DLOG(SEVERITY, DOWNSTREAM)                                      \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[DOWNSTREAM:\" << DOWNSTREAM << \"] \")\n\n// Downstream connection log\n#define DCLOG(SEVERITY, DCONN)                                          \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[DCONN:\" << DCONN << \"] \")\n\n// Downstream SPDY session log\n#define SSLOG(SEVERITY, SPDY)                                           \\\n  (Log(SEVERITY, __FILE__, __LINE__) << \"[DSPDY:\" << SPDY << \"] \")\n\nenum SeverityLevel {\n  INFO, WARNING, ERROR, FATAL\n};\n\nclass Log {\npublic:\n  Log(int severity, const char *filename, int linenum);\n  ~Log();\n  template<typename Type> Log& operator<<(Type s)\n  {\n    stream_ << s;\n    return *this;\n  }\n  static void set_severity_level(int severity);\n  static int set_severity_level_by_name(const char *name);\n  static bool log_enabled(int severity)\n  {\n    return severity >= severity_thres_;\n  }\nprivate:\n  std::stringstream stream_;\n  const char *filename_;\n  int severity_;\n  int linenum_;\n  static int severity_thres_;\n};\n\n#define TTY_HTTP_HD (get_config()->tty ? \"\\033[1;34m\" : \"\")\n#define TTY_RST (get_config()->tty ? \"\\033[0m\" : \"\")\n\n} // namespace shrpx\n\n#endif // SHRPX_LOG_H\n"
  },
  {
    "path": "src/shrpx_spdy_downstream_connection.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_spdy_downstream_connection.h\"\n\n#include <unistd.h>\n\n#include <openssl/err.h>\n\n#include <event2/bufferevent_ssl.h>\n\n#include \"http-parser/http_parser.h\"\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_upstream.h\"\n#include \"shrpx_downstream.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_error.h\"\n#include \"shrpx_http.h\"\n#include \"shrpx_spdy_session.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nSpdyDownstreamConnection::SpdyDownstreamConnection\n(ClientHandler *client_handler)\n  : DownstreamConnection(client_handler),\n    spdy_(client_handler->get_spdy_session()),\n    request_body_buf_(0),\n    sd_(0)\n{}\n\nSpdyDownstreamConnection::~SpdyDownstreamConnection()\n{\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Deleting\";\n  }\n  if(request_body_buf_) {\n    evbuffer_free(request_body_buf_);\n  }\n  if(downstream_) {\n    if(submit_rst_stream(downstream_) == 0) {\n      spdy_->notify();\n    }\n  }\n  spdy_->remove_downstream_connection(this);\n  // Downstream and DownstreamConnection may be deleted\n  // asynchronously.\n  if(downstream_) {\n    downstream_->set_downstream_connection(0);\n  }\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Deleted\";\n  }\n}\n\nint SpdyDownstreamConnection::init_request_body_buf()\n{\n  int rv;\n  if(request_body_buf_) {\n    rv = evbuffer_drain(request_body_buf_,\n                        evbuffer_get_length(request_body_buf_));\n    if(rv != 0) {\n      return -1;\n    }\n  } else {\n    request_body_buf_ = evbuffer_new();\n    if(request_body_buf_ == 0) {\n      return -1;\n    }\n  }\n  return 0;\n}\n\nint SpdyDownstreamConnection::attach_downstream(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Attaching to DOWNSTREAM:\" << downstream;\n  }\n  if(init_request_body_buf() == -1) {\n    return -1;\n  }\n  spdy_->add_downstream_connection(this);\n  if(spdy_->get_state() == SpdySession::DISCONNECTED) {\n    spdy_->notify();\n  }\n  downstream->set_downstream_connection(this);\n  downstream_ = downstream;\n  return 0;\n}\n\nvoid SpdyDownstreamConnection::detach_downstream(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DCLOG(INFO, this) << \"Detaching from DOWNSTREAM:\" << downstream;\n  }\n  if(submit_rst_stream(downstream) == 0) {\n    spdy_->notify();\n  }\n  downstream->set_downstream_connection(0);\n  downstream_ = 0;\n\n  client_handler_->pool_downstream_connection(this);\n}\n\nint SpdyDownstreamConnection::submit_rst_stream(Downstream *downstream)\n{\n  int rv = -1;\n  if(spdy_->get_state() == SpdySession::CONNECTED &&\n     downstream->get_downstream_stream_id() != -1) {\n    switch(downstream->get_response_state()) {\n    case Downstream::MSG_RESET:\n    case Downstream::MSG_COMPLETE:\n      break;\n    default:\n      if(LOG_ENABLED(INFO)) {\n        DCLOG(INFO, this) << \"Submit RST_STREAM for DOWNSTREAM:\"\n                          << downstream << \", stream_id=\"\n                          << downstream->get_downstream_stream_id();\n      }\n      rv = spdy_->submit_rst_stream(this,\n                                    downstream->get_downstream_stream_id(),\n                                    SPDYLAY_INTERNAL_ERROR);\n    }\n  }\n  return rv;\n}\n\nnamespace {\nssize_t spdy_data_read_callback(spdylay_session *session,\n                                int32_t stream_id,\n                                uint8_t *buf, size_t length,\n                                int *eof,\n                                spdylay_data_source *source,\n                                void *user_data)\n{\n  StreamData *sd;\n  sd = static_cast<StreamData*>\n    (spdylay_session_get_stream_user_data(session, stream_id));\n  if(!sd || !sd->dconn) {\n    return SPDYLAY_ERR_DEFERRED;\n  }\n  SpdyDownstreamConnection *dconn;\n  dconn = static_cast<SpdyDownstreamConnection*>(source->ptr);\n  Downstream *downstream = dconn->get_downstream();\n  if(!downstream) {\n    // In this case, RST_STREAM should have been issued. But depending\n    // on the priority, DATA frame may come first.\n    return SPDYLAY_ERR_DEFERRED;\n  }\n  evbuffer *body = dconn->get_request_body_buf();\n  int nread = 0;\n  for(;;) {\n    nread = evbuffer_remove(body, buf, length);\n    if(nread == 0) {\n      // If CONNECT request failed, set *eof = 1.\n      if(downstream->get_request_state() == Downstream::MSG_COMPLETE ||\n         (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&\n          downstream->get_request_method() == \"CONNECT\" &&\n          !downstream->tunnel_established())) {\n        *eof = 1;\n        break;\n      } else {\n        // This is important because it will handle flow control\n        // stuff.\n        if(downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER,\n                                                   downstream) != 0) {\n          // In this case, downstream may be deleted.\n          return SPDYLAY_ERR_CALLBACK_FAILURE;\n        }\n        // Check dconn is still alive because Upstream::resume_read()\n        // may delete downstream which will delete dconn.\n        if(sd->dconn == 0) {\n          return SPDYLAY_ERR_DEFERRED;\n        }\n        if(evbuffer_get_length(body) == 0) {\n          // Check get_request_state() == MSG_COMPLETE just in case\n          if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {\n            *eof = 1;\n            break;\n          }\n          return SPDYLAY_ERR_DEFERRED;\n        }\n      }\n    } else {\n      // Send WINDOW_UPDATE before buffer is empty to avoid delay\n      // because of RTT.\n      if(!downstream->get_output_buffer_full() &&\n         downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER,\n                                                 downstream) != 0) {\n        // In this case, downstream may be deleted.\n        return SPDYLAY_ERR_CALLBACK_FAILURE;\n      }\n      break;\n    }\n  }\n  return nread;\n}\n} // namespace\n\nint SpdyDownstreamConnection::push_request_headers()\n{\n  int rv;\n  if(spdy_->get_state() != SpdySession::CONNECTED) {\n    // The SPDY session to the backend has not been established.  This\n    // function will be called again just after it is established.\n    return 0;\n  }\n  if(!downstream_) {\n    return 0;\n  }\n  size_t nheader = downstream_->get_request_headers().size();\n  // 14 means :method, :scheme, :path, :version and possible via,\n  // x-forwarded-proto and x-forwarded-for header fields. We rename\n  // host header field as :host.\n  const char **nv = new const char*[nheader * 2 + 14 + 1];\n  size_t hdidx = 0;\n  std::string via_value;\n  std::string xff_value;\n  std::string scheme, path, query;\n  if(downstream_->get_request_method() == \"CONNECT\") {\n    // No :scheme header field for CONNECT method.\n    nv[hdidx++] = \":path\";\n    nv[hdidx++] = downstream_->get_request_path().c_str();\n  } else {\n    http_parser_url u;\n    const char *url = downstream_->get_request_path().c_str();\n    memset(&u, 0, sizeof(u));\n    rv = http_parser_parse_url(url,\n                               downstream_->get_request_path().size(),\n                               0, &u);\n    if(rv == 0) {\n      http::copy_url_component(scheme, &u, UF_SCHEMA, url);\n      http::copy_url_component(path, &u, UF_PATH, url);\n      http::copy_url_component(query, &u, UF_QUERY, url);\n      if(path.empty()) {\n        path = \"/\";\n      }\n      if(!query.empty()) {\n        path += \"?\";\n        path += query;\n      }\n    }\n    nv[hdidx++] = \":scheme\";\n    if(scheme.empty()) {\n      if(client_handler_->get_ssl()) {\n        nv[hdidx++] = \"https\";\n      } else {\n        // The default scheme is http. For SPDY upstream, the path must\n        // be absolute URI, so scheme should be provided.\n        nv[hdidx++] = \"http\";\n      }\n    } else {\n      nv[hdidx++] = scheme.c_str();\n    }\n    nv[hdidx++] = \":path\";\n    if(path.empty()) {\n      nv[hdidx++] = downstream_->get_request_path().c_str();\n    } else {\n      nv[hdidx++] = path.c_str();\n    }\n  }\n\n  nv[hdidx++] = \":method\";\n  nv[hdidx++] = downstream_->get_request_method().c_str();\n\n  nv[hdidx++] = \":version\";\n  nv[hdidx++] = \"HTTP/1.1\";\n  bool chunked_encoding = false;\n  bool content_length = false;\n  for(Headers::const_iterator i = downstream_->get_request_headers().begin();\n      i != downstream_->get_request_headers().end(); ++i) {\n    if(util::strieq((*i).first.c_str(), \"transfer-encoding\")) {\n      if(util::strieq((*i).second.c_str(), \"chunked\")) {\n        chunked_encoding = true;\n      }\n      // Ignore transfer-encoding\n    } else if(util::strieq((*i).first.c_str(), \"x-forwarded-proto\") ||\n              util::strieq((*i).first.c_str(), \"keep-alive\") ||\n              util::strieq((*i).first.c_str(), \"connection\") ||\n              util:: strieq((*i).first.c_str(), \"proxy-connection\")) {\n      // These are ignored\n    } else if(!get_config()->no_via &&\n              util::strieq((*i).first.c_str(), \"via\")) {\n      via_value = (*i).second;\n    } else if(util::strieq((*i).first.c_str(), \"x-forwarded-for\")) {\n      xff_value = (*i).second;\n    } else if(util::strieq((*i).first.c_str(), \"expect\") &&\n       util::strifind((*i).second.c_str(), \"100-continue\")) {\n      // Ignore\n    } else if(util::strieq((*i).first.c_str(), \"host\")) {\n      nv[hdidx++] = \":host\";\n      nv[hdidx++] = (*i).second.c_str();\n    } else {\n      if(util::strieq((*i).first.c_str(), \"content-length\")) {\n        content_length = true;\n      }\n      nv[hdidx++] = (*i).first.c_str();\n      nv[hdidx++] = (*i).second.c_str();\n    }\n  }\n\n  if(get_config()->add_x_forwarded_for) {\n    nv[hdidx++] = \"x-forwarded-for\";\n    if(!xff_value.empty()) {\n      xff_value += \", \";\n    }\n    xff_value += client_handler_->get_ipaddr();\n    nv[hdidx++] = xff_value.c_str();\n  } else if(!xff_value.empty()) {\n    nv[hdidx++] = \"x-forwarded-for\";\n    nv[hdidx++] = xff_value.c_str();\n  }\n\n  if(!get_config()->spdy_proxy && !get_config()->client_proxy &&\n     downstream_->get_request_method() != \"CONNECT\") {\n    // We use same protocol with :scheme header field\n    nv[hdidx++] = \"x-forwarded-proto\";\n    if(scheme.empty()) {\n      if(client_handler_->get_ssl()) {\n        nv[hdidx++] = \"https\";\n      } else {\n        nv[hdidx++] = \"http\";\n      }\n    } else {\n      nv[hdidx++] = scheme.c_str();\n    }\n  }\n\n  if(!get_config()->no_via) {\n    if(!via_value.empty()) {\n      via_value += \", \";\n    }\n    via_value += http::create_via_header_value\n      (downstream_->get_request_major(), downstream_->get_request_minor());\n    nv[hdidx++] = \"via\";\n    nv[hdidx++] = via_value.c_str();\n  }\n  nv[hdidx++] = 0;\n  if(LOG_ENABLED(INFO)) {\n    std::stringstream ss;\n    for(size_t i = 0; nv[i]; i += 2) {\n      ss << TTY_HTTP_HD << nv[i] << TTY_RST << \": \" << nv[i+1] << \"\\n\";\n    }\n    DCLOG(INFO, this) << \"HTTP request headers\\n\" << ss.str();\n  }\n  int pri = downstream_->get_priority();\n  if(pri == -1) {\n    pri = spdylay_session_get_pri_lowest(spdy_->get_session());\n  }\n  if(downstream_->get_request_method() == \"CONNECT\" ||\n     chunked_encoding || content_length) {\n    // Request-body is expected.\n    spdylay_data_provider data_prd;\n    data_prd.source.ptr = this;\n    data_prd.read_callback = spdy_data_read_callback;\n    rv = spdy_->submit_request(this, pri, nv, &data_prd);\n  } else {\n    rv = spdy_->submit_request(this, pri, nv, 0);\n  }\n  delete [] nv;\n  if(rv != 0) {\n    DCLOG(FATAL, this) << \"spdylay_submit_request() failed\";\n    return -1;\n  }\n  spdy_->notify();\n  return 0;\n}\n\nint SpdyDownstreamConnection::push_upload_data_chunk(const uint8_t *data,\n                                                     size_t datalen)\n{\n  int rv = evbuffer_add(request_body_buf_, data, datalen);\n  if(rv != 0) {\n    DCLOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  if(downstream_->get_downstream_stream_id() != -1) {\n    rv = spdy_->resume_data(this);\n    if(rv != 0) {\n      return -1;\n    }\n    spdy_->notify();\n  }\n  return 0;\n}\n\nint SpdyDownstreamConnection::end_upload_data()\n{\n  int rv;\n  if(downstream_->get_downstream_stream_id() != -1) {\n    rv = spdy_->resume_data(this);\n    if(rv != 0) {\n      return -1;\n    }\n    spdy_->notify();\n  }\n  return 0;\n}\n\nint SpdyDownstreamConnection::resume_read(IOCtrlReason reason)\n{\n  int rv1 = 0, rv2 = 0;\n  if(spdy_->get_state() == SpdySession::CONNECTED &&\n     spdy_->get_flow_control()) {\n    int32_t delta;\n    delta = http::determine_window_update_transmission\n      (spdy_->get_session(), 0,\n       1 << get_config()->spdy_downstream_connection_window_bits);\n    if(delta != -1) {\n      rv1 = spdy_->submit_window_update(0, delta);\n      if(rv1 == 0) {\n        spdy_->notify();\n      }\n    }\n    if(downstream_ && downstream_->get_downstream_stream_id() != -1) {\n      delta = http::determine_window_update_transmission\n        (spdy_->get_session(), downstream_->get_downstream_stream_id(),\n         1 << get_config()->spdy_downstream_window_bits);\n      if(delta != -1) {\n        rv2 = spdy_->submit_window_update(this, delta);\n        if(rv2 == 0) {\n          spdy_->notify();\n        }\n      }\n    }\n  }\n  if(rv1 == 0 && rv2 == 0) {\n    return 0;\n  }\n  DLOG(WARNING, this) << \"Sending WINDOW_UPDATE failed\";\n  return -1;\n}\n\nint SpdyDownstreamConnection::on_read()\n{\n  return 0;\n}\n\nint SpdyDownstreamConnection::on_write()\n{\n  return 0;\n}\n\nevbuffer* SpdyDownstreamConnection::get_request_body_buf() const\n{\n  return request_body_buf_;\n}\n\nvoid SpdyDownstreamConnection::attach_stream_data(StreamData *sd)\n{\n  // It is possible sd->dconn is not NULL. sd is detached when\n  // on_stream_close_callback. Before that, after MSG_COMPLETE is set\n  // to Downstream::set_response_state(), upstream's readcb is called\n  // and execution path eventually could reach here. Since the\n  // response was already handled, we just detach sd.\n  detach_stream_data();\n  sd_ = sd;\n  sd_->dconn = this;\n}\n\nStreamData* SpdyDownstreamConnection::detach_stream_data()\n{\n  if(sd_) {\n    StreamData *sd = sd_;\n    sd_ = 0;\n    sd->dconn = 0;\n    return sd;\n  } else {\n    return 0;\n  }\n}\n\nbool SpdyDownstreamConnection::get_output_buffer_full()\n{\n  if(request_body_buf_) {\n    return spdy_->get_outbuf_length() +\n      evbuffer_get_length(request_body_buf_) >= SpdySession::OUTBUF_MAX_THRES;\n  } else {\n    return false;\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_spdy_downstream_connection.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_SPDY_DOWNSTREAM_CONNECTION_H\n#define SHRPX_SPDY_DOWNSTREAM_CONNECTION_H\n\n#include \"shrpx.h\"\n\n#include <openssl/ssl.h>\n\n#include <spdylay/spdylay.h>\n\n#include \"shrpx_downstream_connection.h\"\n\nnamespace shrpx {\n\nstruct StreamData;\nclass SpdySession;\n\nclass SpdyDownstreamConnection : public DownstreamConnection {\npublic:\n  SpdyDownstreamConnection(ClientHandler *client_handler);\n  virtual ~SpdyDownstreamConnection();\n  virtual int attach_downstream(Downstream *downstream);\n  virtual void detach_downstream(Downstream *downstream);\n\n  virtual int push_request_headers();\n  virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);\n  virtual int end_upload_data();\n\n  virtual void pause_read(IOCtrlReason reason) {}\n  virtual int resume_read(IOCtrlReason reason);\n  virtual void force_resume_read() {}\n\n  virtual bool get_output_buffer_full();\n\n  virtual int on_read();\n  virtual int on_write();\n\n  int send();\n\n  int init_request_body_buf();\n  evbuffer* get_request_body_buf() const;\n\n  void attach_stream_data(StreamData *sd);\n  StreamData* detach_stream_data();\n\n  int submit_rst_stream(Downstream *downstream);\nprivate:\n  SpdySession *spdy_;\n  evbuffer *request_body_buf_;\n  StreamData *sd_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_SPDY_DOWNSTREAM_CONNECTION_H\n"
  },
  {
    "path": "src/shrpx_spdy_session.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_spdy_session.h\"\n\n#include <netinet/tcp.h>\n#include <unistd.h>\n#include <vector>\n\n#include <openssl/err.h>\n\n#include <event2/bufferevent_ssl.h>\n\n#include \"shrpx_upstream.h\"\n#include \"shrpx_downstream.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_error.h\"\n#include \"shrpx_spdy_downstream_connection.h\"\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_ssl.h\"\n#include \"util.h\"\n#include \"base64.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nSpdySession::SpdySession(event_base *evbase, SSL_CTX *ssl_ctx)\n  : evbase_(evbase),\n    ssl_ctx_(ssl_ctx),\n    ssl_(0),\n    session_(0),\n    bev_(0),\n    wrbev_(0),\n    rdbev_(0),\n    proxy_htp_(0),\n    fd_(-1),\n    state_(DISCONNECTED),\n    notified_(false),\n    flow_control_(false)\n{}\n\nSpdySession::~SpdySession()\n{\n  disconnect();\n}\n\nint SpdySession::disconnect()\n{\n  if(LOG_ENABLED(INFO)) {\n    SSLOG(INFO, this) << \"Disconnecting\";\n  }\n  spdylay_session_del(session_);\n  session_ = 0;\n\n  if(ssl_) {\n    SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);\n    SSL_shutdown(ssl_);\n  }\n  if(bev_) {\n    int fd = bufferevent_getfd(bev_);\n    bufferevent_disable(bev_, EV_READ | EV_WRITE);\n    bufferevent_free(bev_);\n    bev_ = 0;\n    if(fd != -1) {\n      if(fd_ == -1) {\n        fd_ = fd;\n      } else if(fd != fd_) {\n        SSLOG(WARNING, this) << \"fd in bev_ != fd_\";\n        shutdown(fd, SHUT_WR);\n        close(fd);\n      }\n    }\n  }\n  if(ssl_) {\n    SSL_free(ssl_);\n  }\n  ssl_ = 0;\n\n  if(fd_ != -1) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, this) << \"Closing fd=\" << fd_;\n    }\n    shutdown(fd_, SHUT_WR);\n    close(fd_);\n    fd_ = -1;\n  }\n\n  if(proxy_htp_) {\n    delete proxy_htp_;\n    proxy_htp_ = 0;\n  }\n\n  notified_ = false;\n  state_ = DISCONNECTED;\n\n  // Delete all client handler associated to Downstream. When deleting\n  // SpdyDownstreamConnection, it calls this object's\n  // remove_downstream_connection(). The multiple\n  // SpdyDownstreamConnection objects belong to the same ClientHandler\n  // object. So first dump ClientHandler objects and delete them once\n  // and for all.\n  std::vector<SpdyDownstreamConnection*> vec(dconns_.begin(), dconns_.end());\n  std::set<ClientHandler*> handlers;\n  for(size_t i = 0; i < vec.size(); ++i) {\n    handlers.insert(vec[i]->get_client_handler());\n  }\n  for(std::set<ClientHandler*>::iterator i = handlers.begin(),\n        eoi = handlers.end(); i != eoi; ++i) {\n    delete *i;\n  }\n\n  dconns_.clear();\n  for(std::set<StreamData*>::iterator i = streams_.begin(),\n        eoi = streams_.end(); i != eoi; ++i) {\n    delete *i;\n  }\n  streams_.clear();\n  return 0;\n}\n\nnamespace {\nvoid notify_readcb(bufferevent *bev, void *arg)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(arg);\n  spdy->clear_notify();\n  switch(spdy->get_state()) {\n  case SpdySession::DISCONNECTED:\n    rv = spdy->initiate_connection();\n    if(rv != 0) {\n      SSLOG(FATAL, spdy) << \"Could not initiate notification connection\";\n      spdy->disconnect();\n    }\n    break;\n  case SpdySession::CONNECTED:\n    rv = spdy->send();\n    if(rv != 0) {\n      spdy->disconnect();\n    }\n    break;\n  }\n}\n} // namespace\n\nnamespace {\nvoid notify_eventcb(bufferevent *bev, short events, void *arg)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(arg);\n  // TODO should DIE()?\n  if(events & BEV_EVENT_EOF) {\n    SSLOG(ERROR, spdy) << \"Notification connection lost: EOF\";\n  }\n  if(events & BEV_EVENT_TIMEOUT) {\n    SSLOG(ERROR, spdy) << \"Notification connection lost: timeout\";\n  }\n  if(events & BEV_EVENT_ERROR) {\n    SSLOG(ERROR, spdy) << \"Notification connection lost: network error\";\n  }\n}\n} // namespace\n\nint SpdySession::init_notification()\n{\n  int rv;\n  int sockpair[2];\n  rv = socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair);\n  if(rv == -1) {\n    SSLOG(FATAL, this) << \"socketpair() failed: errno=\" << errno;\n    return -1;\n  }\n  wrbev_ = bufferevent_socket_new(evbase_, sockpair[0],\n                                  BEV_OPT_CLOSE_ON_FREE|\n                                  BEV_OPT_DEFER_CALLBACKS);\n  if(!wrbev_) {\n    SSLOG(FATAL, this) << \"bufferevent_socket_new() failed\";\n    for(int i = 0; i < 2; ++i) {\n      close(sockpair[i]);\n    }\n    return -1;\n  }\n  rdbev_ = bufferevent_socket_new(evbase_, sockpair[1],\n                                  BEV_OPT_CLOSE_ON_FREE|\n                                  BEV_OPT_DEFER_CALLBACKS);\n  if(!rdbev_) {\n    SSLOG(FATAL, this) << \"bufferevent_socket_new() failed\";\n    close(sockpair[1]);\n    return -1;\n  }\n  bufferevent_enable(rdbev_, EV_READ);\n  bufferevent_setcb(rdbev_, notify_readcb, 0, notify_eventcb, this);\n  return 0;\n}\n\nnamespace {\nvoid readcb(bufferevent *bev, void *ptr)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(ptr);\n  rv = spdy->on_read();\n  if(rv != 0) {\n    spdy->disconnect();\n  }\n}\n} // namespace\n\nnamespace {\nvoid writecb(bufferevent *bev, void *ptr)\n{\n  if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {\n    return;\n  }\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(ptr);\n  rv = spdy->on_write();\n  if(rv != 0) {\n    spdy->disconnect();\n  }\n}\n} // namespace\n\nnamespace {\nvoid eventcb(bufferevent *bev, short events, void *ptr)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(ptr);\n  if(events & BEV_EVENT_CONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"Connection established\";\n    }\n    spdy->set_state(SpdySession::CONNECTED);\n    if((!get_config()->spdy_downstream_no_tls &&\n        !get_config()->insecure && spdy->check_cert() != 0) ||\n       spdy->on_connect() != 0) {\n      spdy->disconnect();\n      return;\n    }\n    int fd = bufferevent_getfd(bev);\n    int val = 1;\n    if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,\n                  reinterpret_cast<char *>(&val), sizeof(val)) == -1) {\n      SSLOG(WARNING, spdy) << \"Setting option TCP_NODELAY failed: errno=\"\n                           << errno;\n    }\n  } else if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"EOF\";\n    }\n    spdy->disconnect();\n  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {\n    if(LOG_ENABLED(INFO)) {\n      if(events & BEV_EVENT_ERROR) {\n        SSLOG(INFO, spdy) << \"Network error\";\n      } else {\n        SSLOG(INFO, spdy) << \"Timeout\";\n      }\n    }\n    spdy->disconnect();\n  }\n}\n} // namespace\n\nnamespace {\nvoid proxy_readcb(bufferevent *bev, void *ptr)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(ptr);\n  if(spdy->on_read_proxy() == 0) {\n    switch(spdy->get_state()) {\n    case SpdySession::PROXY_CONNECTED:\n      // The current bufferevent is no longer necessary, so delete it\n      // here. But we keep fd_ inside it.\n      spdy->unwrap_free_bev();\n      // Initiate SSL/TLS handshake through established tunnel.\n      if(spdy->initiate_connection() != 0) {\n        spdy->disconnect();\n      }\n      break;\n    case SpdySession::PROXY_FAILED:\n      spdy->disconnect();\n      break;\n    }\n  } else {\n    spdy->disconnect();\n  }\n}\n} // namespace\n\nnamespace {\nvoid proxy_eventcb(bufferevent *bev, short events, void *ptr)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(ptr);\n  if(events & BEV_EVENT_CONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"Connected to the proxy\";\n    }\n    std::string req = \"CONNECT \";\n    req += get_config()->downstream_hostport;\n    req += \" HTTP/1.1\\r\\nHost: \";\n    req += get_config()->downstream_host;\n    req += \"\\r\\n\";\n    if(get_config()->downstream_http_proxy_userinfo) {\n      req += \"Proxy-Authorization: Basic \";\n      size_t len = strlen(get_config()->downstream_http_proxy_userinfo);\n      req += base64::encode(get_config()->downstream_http_proxy_userinfo,\n                            get_config()->downstream_http_proxy_userinfo+len);\n      req += \"\\r\\n\";\n    }\n    req += \"\\r\\n\";\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"HTTP proxy request headers\\n\" << req;\n    }\n    if(bufferevent_write(bev, req.c_str(), req.size()) != 0) {\n      SSLOG(ERROR, spdy) << \"bufferevent_write() failed\";\n      spdy->disconnect();\n    }\n  } else if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"Proxy EOF\";\n    }\n    spdy->disconnect();\n  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {\n    if(LOG_ENABLED(INFO)) {\n      if(events & BEV_EVENT_ERROR) {\n        SSLOG(INFO, spdy) << \"Network error\";\n      } else {\n        SSLOG(INFO, spdy) << \"Timeout\";\n      }\n    }\n    spdy->disconnect();\n  }\n}\n} // namespace\n\nint SpdySession::check_cert()\n{\n  return ssl::check_cert(ssl_);\n}\n\nint SpdySession::initiate_connection()\n{\n  int rv = 0;\n  if(get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, this) << \"Connecting to the proxy \"\n                        << get_config()->downstream_http_proxy_host << \":\"\n                        << get_config()->downstream_http_proxy_port;\n    }\n    bev_ = bufferevent_socket_new(evbase_, -1, BEV_OPT_DEFER_CALLBACKS);\n    bufferevent_enable(bev_, EV_READ);\n    bufferevent_set_timeouts(bev_, &get_config()->downstream_read_timeout,\n                             &get_config()->downstream_write_timeout);\n\n    // No need to set writecb because we write the request when\n    // connected at once.\n    bufferevent_setcb(bev_, proxy_readcb, 0, proxy_eventcb, this);\n    rv = bufferevent_socket_connect\n      (bev_,\n       const_cast<sockaddr*>(&get_config()->downstream_http_proxy_addr.sa),\n       get_config()->downstream_http_proxy_addrlen);\n    if(rv != 0) {\n      SSLOG(ERROR, this) << \"Failed to connect to the proxy \"\n                         << get_config()->downstream_http_proxy_host << \":\"\n                         << get_config()->downstream_http_proxy_port;\n      return SHRPX_ERR_NETWORK;\n    }\n    proxy_htp_ = new http_parser();\n    http_parser_init(proxy_htp_, HTTP_RESPONSE);\n    proxy_htp_->data = this;\n\n    state_ = PROXY_CONNECTING;\n  } else if(state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, this) << \"Connecting to downstream server\";\n    }\n    if(ssl_ctx_) {\n      // We are establishing TLS connection.\n      ssl_ = SSL_new(ssl_ctx_);\n      if(!ssl_) {\n        SSLOG(ERROR, this) << \"SSL_new() failed: \"\n                           << ERR_error_string(ERR_get_error(), NULL);\n        return -1;\n      }\n\n      const char *sni_name = 0;\n      if ( get_config()->backend_tls_sni_name ) {\n        sni_name = get_config()->backend_tls_sni_name;\n      }\n      else {\n        sni_name = get_config()->downstream_host;\n      }\n\n      if(!ssl::numeric_host(sni_name)) {\n        // TLS extensions: SNI. There is no documentation about the return\n        // code for this function (actually this is macro wrapping SSL_ctrl\n        // at the time of this writing).\n        SSL_set_tlsext_host_name(ssl_, sni_name);\n      }\n      // If state_ == PROXY_CONNECTED, we has connected to the proxy\n      // using fd_ and tunnel has been established.\n      bev_ = bufferevent_openssl_socket_new(evbase_, fd_, ssl_,\n                                            BUFFEREVENT_SSL_CONNECTING,\n                                            BEV_OPT_DEFER_CALLBACKS);\n      rv = bufferevent_socket_connect\n        (bev_,\n         // TODO maybe not thread-safe?\n         const_cast<sockaddr*>(&get_config()->downstream_addr.sa),\n         get_config()->downstream_addrlen);\n    } else if(state_ == DISCONNECTED) {\n      // Without TLS and proxy.\n      bev_ = bufferevent_socket_new(evbase_, -1, BEV_OPT_DEFER_CALLBACKS);\n      rv = bufferevent_socket_connect\n        (bev_,\n         const_cast<sockaddr*>(&get_config()->downstream_addr.sa),\n         get_config()->downstream_addrlen);\n    } else {\n      assert(state_ == PROXY_CONNECTED);\n      // Without TLS but with proxy.\n      bev_ = bufferevent_socket_new(evbase_, fd_, BEV_OPT_DEFER_CALLBACKS);\n      // Connection already established.\n      eventcb(bev_, BEV_EVENT_CONNECTED, this);\n      // eventcb() has no return value. Check state_ to whether it was\n      // failed or not.\n      if(state_ == DISCONNECTED) {\n        return -1;\n      }\n    }\n    if(rv != 0) {\n      return SHRPX_ERR_NETWORK;\n    }\n\n    bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);\n    bufferevent_enable(bev_, EV_READ);\n    bufferevent_setcb(bev_, readcb, writecb, eventcb, this);\n\n    // Set timeout for SPDY session\n    bufferevent_set_timeouts(bev_, &get_config()->downstream_read_timeout,\n                             &get_config()->downstream_write_timeout);\n\n    // We have been already connected when no TLS and proxy is used.\n    if(state_ != CONNECTED) {\n      state_ = CONNECTING;\n    }\n  } else {\n    // Unreachable\n    DIE();\n  }\n  return 0;\n}\n\nvoid SpdySession::unwrap_free_bev()\n{\n  assert(fd_ == -1);\n  fd_ = bufferevent_getfd(bev_);\n  bufferevent_free(bev_);\n  bev_ = 0;\n}\n\nnamespace {\nint htp_hdrs_completecb(http_parser *htp)\n{\n  SpdySession *spdy;\n  spdy = static_cast<SpdySession*>(htp->data);\n  // We just check status code here\n  if(htp->status_code == 200) {\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"Tunneling success\";\n    }\n    spdy->set_state(SpdySession::PROXY_CONNECTED);\n  } else {\n    SSLOG(WARNING, spdy) << \"Tunneling failed\";\n    spdy->set_state(SpdySession::PROXY_FAILED);\n  }\n  return 0;\n}\n} // namespace\n\nnamespace {\nhttp_parser_settings htp_hooks = {\n  0, /*http_cb      on_message_begin;*/\n  0, /*http_data_cb on_url;*/\n  0, /*http_cb on_status_complete */\n  0, /*http_data_cb on_header_field;*/\n  0, /*http_data_cb on_header_value;*/\n  htp_hdrs_completecb, /*http_cb      on_headers_complete;*/\n  0, /*http_data_cb on_body;*/\n  0  /*http_cb      on_message_complete;*/\n};\n} // namespace\n\nint SpdySession::on_read_proxy()\n{\n  evbuffer *input = bufferevent_get_input(bev_);\n  unsigned char *mem = evbuffer_pullup(input, -1);\n\n  size_t nread = http_parser_execute(proxy_htp_, &htp_hooks,\n                                     reinterpret_cast<const char*>(mem),\n                                     evbuffer_get_length(input));\n\n  evbuffer_drain(input, nread);\n  http_errno htperr = HTTP_PARSER_ERRNO(proxy_htp_);\n  if(htperr == HPE_OK) {\n    return 0;\n  } else {\n    return -1;\n  }\n}\n\nvoid SpdySession::add_downstream_connection(SpdyDownstreamConnection *dconn)\n{\n  dconns_.insert(dconn);\n}\n\nvoid SpdySession::remove_downstream_connection(SpdyDownstreamConnection *dconn)\n{\n  dconns_.erase(dconn);\n  dconn->detach_stream_data();\n}\n\nvoid SpdySession::remove_stream_data(StreamData *sd)\n{\n  streams_.erase(sd);\n  if(sd->dconn) {\n    sd->dconn->detach_stream_data();\n  }\n  delete sd;\n}\n\nint SpdySession::submit_request(SpdyDownstreamConnection *dconn,\n                                uint8_t pri, const char **nv,\n                                const spdylay_data_provider *data_prd)\n{\n  assert(state_ == CONNECTED);\n  StreamData *sd = new StreamData();\n  int rv = spdylay_submit_request(session_, pri, nv, data_prd, sd);\n  if(rv == 0) {\n    dconn->attach_stream_data(sd);\n    streams_.insert(sd);\n  } else {\n    SSLOG(FATAL, this) << \"spdylay_submit_request() failed: \"\n                       << spdylay_strerror(rv);\n    delete sd;\n    return -1;\n  }\n  return 0;\n}\n\nint SpdySession::submit_rst_stream(SpdyDownstreamConnection *dconn,\n                                   int32_t stream_id, uint32_t status_code)\n{\n  assert(state_ == CONNECTED);\n  int rv = spdylay_submit_rst_stream(session_, stream_id, status_code);\n  if(rv != 0) {\n    SSLOG(FATAL, this) << \"spdylay_submit_rst_stream() failed: \"\n                       << spdylay_strerror(rv);\n    return -1;\n  }\n  return 0;\n}\n\nint SpdySession::submit_window_update(SpdyDownstreamConnection *dconn,\n                                      int32_t amount)\n{\n  assert(state_ == CONNECTED);\n  int rv;\n  int32_t stream_id;\n  if(dconn) {\n    stream_id = dconn->get_downstream()->get_downstream_stream_id();\n  } else {\n    stream_id = 0;\n  }\n  rv = spdylay_submit_window_update(session_, stream_id, amount);\n  if(rv < SPDYLAY_ERR_FATAL) {\n    SSLOG(FATAL, this) << \"spdylay_submit_window_update() failed: \"\n                       << spdylay_strerror(rv);\n    return -1;\n  }\n  return 0;\n}\n\nbool SpdySession::get_flow_control() const\n{\n  return flow_control_;\n}\n\nint SpdySession::resume_data(SpdyDownstreamConnection *dconn)\n{\n  assert(state_ == CONNECTED);\n  Downstream *downstream = dconn->get_downstream();\n  int rv = spdylay_session_resume_data(session_,\n                                       downstream->get_downstream_stream_id());\n  switch(rv) {\n  case 0:\n  case SPDYLAY_ERR_INVALID_ARGUMENT:\n    return 0;\n  default:\n    SSLOG(FATAL, this) << \"spdylay_resume_session() failed: \"\n                       << spdylay_strerror(rv);\n    return -1;\n  }\n}\n\nnamespace {\nvoid call_downstream_readcb(SpdySession *spdy, Downstream *downstream)\n{\n  Upstream *upstream = downstream->get_upstream();\n  if(upstream) {\n    (upstream->get_downstream_readcb())\n      (spdy->get_bev(),\n       downstream->get_downstream_connection());\n  }\n}\n} // namespace\n\nnamespace {\nssize_t send_callback(spdylay_session *session,\n                      const uint8_t *data, size_t len, int flags,\n                      void *user_data)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n\n  bufferevent *bev = spdy->get_bev();\n  evbuffer *output = bufferevent_get_output(bev);\n  // Check buffer length and return WOULDBLOCK if it is large enough.\n  if(evbuffer_get_length(output) > SpdySession::OUTBUF_MAX_THRES) {\n    return SPDYLAY_ERR_WOULDBLOCK;\n  }\n\n  rv = evbuffer_add(output, data, len);\n  if(rv == -1) {\n    SSLOG(FATAL, spdy) << \"evbuffer_add() failed\";\n    return SPDYLAY_ERR_CALLBACK_FAILURE;\n  } else {\n    return len;\n  }\n}\n} // namespace\n\nnamespace {\nssize_t recv_callback(spdylay_session *session,\n                      uint8_t *data, size_t len, int flags, void *user_data)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n\n  bufferevent *bev = spdy->get_bev();\n  evbuffer *input = bufferevent_get_input(bev);\n  int nread = evbuffer_remove(input, data, len);\n  if(nread == -1) {\n    return SPDYLAY_ERR_CALLBACK_FAILURE;\n  } else if(nread == 0) {\n    return SPDYLAY_ERR_WOULDBLOCK;\n  } else {\n    return nread;\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_stream_close_callback\n(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,\n void *user_data)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    SSLOG(INFO, spdy) << \"Stream stream_id=\" << stream_id\n                      << \" is being closed\";\n  }\n  StreamData *sd;\n  sd = static_cast<StreamData*>\n    (spdylay_session_get_stream_user_data(session, stream_id));\n  if(sd == 0) {\n    // We might get this close callback when pushed streams are\n    // closed.\n    return;\n  }\n  SpdyDownstreamConnection* dconn = sd->dconn;\n  if(dconn) {\n    Downstream *downstream = dconn->get_downstream();\n    if(downstream && downstream->get_downstream_stream_id() == stream_id) {\n      Upstream *upstream = downstream->get_upstream();\n      if(status_code == SPDYLAY_OK) {\n        downstream->set_response_state(Downstream::MSG_COMPLETE);\n        rv = upstream->on_downstream_body_complete(downstream);\n        if(rv != 0) {\n          downstream->set_response_state(Downstream::MSG_RESET);\n        }\n      } else {\n        downstream->set_response_state(Downstream::MSG_RESET);\n      }\n      call_downstream_readcb(spdy, downstream);\n      // dconn may be deleted\n    }\n  }\n  // The life time of StreamData ends here\n  spdy->remove_stream_data(sd);\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  StreamData *sd;\n  Downstream *downstream;\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    if(LOG_ENABLED(INFO)) {\n      SSLOG(INFO, spdy) << \"Received upstream SYN_STREAM stream_id=\"\n                        << frame->syn_stream.stream_id;\n    }\n    // We just respond pushed stream with RST_STREAM.\n    spdylay_submit_rst_stream(session, frame->syn_stream.stream_id,\n                              SPDYLAY_REFUSED_STREAM);\n    break;\n  case SPDYLAY_RST_STREAM:\n    sd = static_cast<StreamData*>\n      (spdylay_session_get_stream_user_data(session,\n                                            frame->rst_stream.stream_id));\n    if(sd && sd->dconn) {\n      downstream = sd->dconn->get_downstream();\n      if(downstream &&\n         downstream->get_downstream_stream_id() ==\n         frame->rst_stream.stream_id) {\n        if(downstream->tunnel_established() &&\n           downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n          // For tunneled connection, we has to submit RST_STREAM to\n          // upstream *after* whole response body is sent. We just set\n          // MSG_COMPLETE here. Upstream will take care of that.\n          if(LOG_ENABLED(INFO)) {\n            SSLOG(INFO, spdy) << \"RST_STREAM against tunneled stream \"\n                              << \"stream_id=\"\n                              << frame->rst_stream.stream_id;\n          }\n          downstream->get_upstream()->on_downstream_body_complete(downstream);\n          downstream->set_response_state(Downstream::MSG_COMPLETE);\n        } else {\n          // If we got RST_STREAM, just flag MSG_RESET to indicate\n          // upstream connection must be terminated.\n          downstream->set_response_state(Downstream::MSG_RESET);\n        }\n        downstream->set_response_rst_stream_status_code\n          (frame->rst_stream.status_code);\n        call_downstream_readcb(spdy, downstream);\n      }\n    }\n    break;\n  case SPDYLAY_SYN_REPLY: {\n    sd = static_cast<StreamData*>\n      (spdylay_session_get_stream_user_data(session,\n                                            frame->syn_reply.stream_id));\n    if(!sd || !sd->dconn) {\n      spdylay_submit_rst_stream(session, frame->syn_reply.stream_id,\n                                SPDYLAY_INTERNAL_ERROR);\n      break;\n    }\n    downstream = sd->dconn->get_downstream();\n    if(!downstream ||\n       downstream->get_downstream_stream_id() != frame->syn_reply.stream_id) {\n      spdylay_submit_rst_stream(session, frame->syn_reply.stream_id,\n                                SPDYLAY_INTERNAL_ERROR);\n      break;\n    }\n    char **nv = frame->syn_reply.nv;\n    const char *status = 0;\n    const char *version = 0;\n    const char *content_length = 0;\n    for(size_t i = 0; nv[i]; i += 2) {\n      if(strcmp(nv[i], \":status\") == 0) {\n        unsigned int code = strtoul(nv[i+1], 0, 10);\n        downstream->set_response_http_status(code);\n        status = nv[i+1];\n      } else if(strcmp(nv[i], \":version\") == 0) {\n        // We assume for now that most version is HTTP/1.1 from\n        // SPDY. So just check if it is HTTP/1.0 and then set response\n        // minor as so.\n        downstream->set_response_major(1);\n        if(util::strieq(nv[i+1], \"HTTP/1.0\")) {\n          downstream->set_response_minor(0);\n        } else {\n          downstream->set_response_minor(1);\n        }\n        version = nv[i+1];\n      } else if(nv[i][0] != ':') {\n        if(strcmp(nv[i], \"content-length\") == 0) {\n          content_length = nv[i+1];\n        }\n        downstream->add_response_header(nv[i], nv[i+1]);\n      }\n    }\n    if(!status || !version) {\n      spdylay_submit_rst_stream(session, frame->syn_reply.stream_id,\n                                SPDYLAY_PROTOCOL_ERROR);\n      downstream->set_response_state(Downstream::MSG_RESET);\n      call_downstream_readcb(spdy, downstream);\n      return;\n    }\n\n    if(!content_length && downstream->get_request_method() != \"HEAD\" &&\n       downstream->get_request_method() != \"CONNECT\") {\n      unsigned int status;\n      status = downstream->get_response_http_status();\n      if(!((100 <= status && status <= 199) || status == 204 ||\n           status == 304)) {\n        // Here we have response body but Content-Length is not known\n        // in advance.\n        if(downstream->get_request_major() <= 0 ||\n           downstream->get_request_minor() <= 0) {\n          // We simply close connection for pre-HTTP/1.1 in this case.\n          downstream->set_response_connection_close(true);\n        } else {\n          // Otherwise, use chunked encoding to keep upstream\n          // connection open.  In SPDY, we are supporsed not to\n          // receive transfer-encoding.\n          downstream->add_response_header(\"transfer-encoding\", \"chunked\");\n        }\n      }\n    }\n\n    if(LOG_ENABLED(INFO)) {\n      std::stringstream ss;\n      for(size_t i = 0; nv[i]; i += 2) {\n        ss << TTY_HTTP_HD << nv[i] << TTY_RST << \": \" << nv[i+1] << \"\\n\";\n      }\n      SSLOG(INFO, spdy) << \"HTTP response headers. stream_id=\"\n                        << frame->syn_reply.stream_id\n                        << \"\\n\" << ss.str();\n    }\n\n    Upstream *upstream = downstream->get_upstream();\n    downstream->set_response_state(Downstream::HEADER_COMPLETE);\n    if(downstream->tunnel_established()) {\n      downstream->set_response_connection_close(true);\n    } else if(downstream->get_request_method() == \"CONNECT\") {\n      // If CONNECT failed, close upstream connection, since the\n      // modified http-parser is in tunnel mode and\n      // Downstream::get_request_state() is MSG_HEADER_COMPLETE.\n      downstream->set_response_connection_close(true);\n      downstream->end_upload_data();\n    }\n    rv = upstream->on_downstream_header_complete(downstream);\n    if(rv != 0) {\n      spdylay_submit_rst_stream(session, frame->syn_reply.stream_id,\n                                SPDYLAY_PROTOCOL_ERROR);\n      downstream->set_response_state(Downstream::MSG_RESET);\n    }\n    call_downstream_readcb(spdy, downstream);\n    break;\n  }\n  default:\n    break;\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_data_chunk_recv_callback(spdylay_session *session,\n                                 uint8_t flags, int32_t stream_id,\n                                 const uint8_t *data, size_t len,\n                                 void *user_data)\n{\n  int rv;\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  StreamData *sd;\n  sd = static_cast<StreamData*>\n    (spdylay_session_get_stream_user_data(session, stream_id));\n  if(!sd || !sd->dconn) {\n    spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);\n    return;\n  }\n  Downstream *downstream = sd->dconn->get_downstream();\n  if(!downstream || downstream->get_downstream_stream_id() != stream_id) {\n    spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);\n    return;\n  }\n\n  if(spdy->get_flow_control()) {\n    if(spdylay_session_get_recv_data_length(session) >\n       std::max(SPDYLAY_INITIAL_WINDOW_SIZE,\n                1 << get_config()->spdy_downstream_connection_window_bits)) {\n      if(LOG_ENABLED(INFO)) {\n        SSLOG(INFO, spdy)\n          << \"Flow control error on connection: \"\n          << \"recv_window_size=\"\n          << spdylay_session_get_recv_data_length(session)\n          << \", window_size=\"\n          << (1 << get_config()->spdy_downstream_connection_window_bits);\n      }\n      spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n      downstream->set_response_state(Downstream::MSG_RESET);\n      call_downstream_readcb(spdy, downstream);\n      return;\n    }\n    if(spdylay_session_get_stream_recv_data_length(session, stream_id) >\n       std::max(SPDYLAY_INITIAL_WINDOW_SIZE,\n                1 << get_config()->spdy_downstream_window_bits)) {\n      if(LOG_ENABLED(INFO)) {\n        SSLOG(INFO, spdy)\n          << \"Flow control error: recv_window_size=\"\n          << spdylay_session_get_stream_recv_data_length(session, stream_id)\n          << \", initial_window_size=\"\n          << (1 << get_config()->spdy_downstream_window_bits);\n      }\n      spdylay_submit_rst_stream(session, stream_id,\n                                SPDYLAY_FLOW_CONTROL_ERROR);\n      downstream->set_response_state(Downstream::MSG_RESET);\n      call_downstream_readcb(spdy, downstream);\n      return;\n    }\n  }\n\n  Upstream *upstream = downstream->get_upstream();\n  rv = upstream->on_downstream_body(downstream, data, len);\n  if(rv != 0) {\n    spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);\n    downstream->set_response_state(Downstream::MSG_RESET);\n  }\n  call_downstream_readcb(spdy, downstream);\n}\n} // namespace\n\nnamespace {\nvoid before_ctrl_send_callback(spdylay_session *session,\n                               spdylay_frame_type type,\n                               spdylay_frame *frame,\n                               void *user_data)\n{\n  if(type == SPDYLAY_SYN_STREAM) {\n    StreamData *sd;\n    sd = static_cast<StreamData*>\n      (spdylay_session_get_stream_user_data(session,\n                                            frame->syn_stream.stream_id));\n    if(!sd || !sd->dconn) {\n      spdylay_submit_rst_stream(session, frame->syn_stream.stream_id,\n                                SPDYLAY_CANCEL);\n      return;\n    }\n    Downstream *downstream = sd->dconn->get_downstream();\n    if(downstream) {\n      downstream->set_downstream_stream_id(frame->syn_stream.stream_id);\n    } else {\n      spdylay_submit_rst_stream(session, frame->syn_stream.stream_id,\n                                SPDYLAY_CANCEL);\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_not_send_callback(spdylay_session *session,\n                               spdylay_frame_type type,\n                               spdylay_frame *frame,\n                               int error_code, void *user_data)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  SSLOG(WARNING, spdy) << \"Failed to send control frame type=\" << type << \", \"\n                       << \"error_code=\" << error_code << \":\"\n                       << spdylay_strerror(error_code);\n  if(type == SPDYLAY_SYN_STREAM) {\n    // To avoid stream hanging around, flag Downstream::MSG_RESET and\n    // terminate the upstream and downstream connections.\n    StreamData *sd;\n    sd = static_cast<StreamData*>\n      (spdylay_session_get_stream_user_data(session,\n                                            frame->syn_stream.stream_id));\n    if(!sd) {\n      return;\n    }\n    if(sd->dconn) {\n      Downstream *downstream = sd->dconn->get_downstream();\n      if(!downstream ||\n         downstream->get_downstream_stream_id() !=\n         frame->syn_stream.stream_id) {\n        return;\n      }\n      downstream->set_response_state(Downstream::MSG_RESET);\n      call_downstream_readcb(spdy, downstream);\n    }\n    spdy->remove_stream_data(sd);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_recv_parse_error_callback(spdylay_session *session,\n                                       spdylay_frame_type type,\n                                       const uint8_t *head, size_t headlen,\n                                       const uint8_t *payload,\n                                       size_t payloadlen, int error_code,\n                                       void *user_data)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    SSLOG(INFO, spdy) << \"Failed to parse received control frame. type=\"\n                      << type\n                      << \", error_code=\" << error_code << \":\"\n                      << spdylay_strerror(error_code);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_unknown_ctrl_recv_callback(spdylay_session *session,\n                                   const uint8_t *head, size_t headlen,\n                                   const uint8_t *payload, size_t payloadlen,\n                                   void *user_data)\n{\n  SpdySession *spdy = static_cast<SpdySession*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    SSLOG(INFO, spdy) << \"Received unknown control frame\";\n  }\n}\n} // namespace\n\nint SpdySession::on_connect()\n{\n  int rv;\n  uint16_t version;\n  const unsigned char *next_proto = 0;\n  unsigned int next_proto_len;\n  if(ssl_ctx_) {\n    SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);\n\n    if(LOG_ENABLED(INFO)) {\n      std::string proto(next_proto, next_proto+next_proto_len);\n      SSLOG(INFO, this) << \"Negotiated next protocol: \" << proto;\n    }\n    version = spdylay_npn_get_version(next_proto, next_proto_len);\n    if(!version) {\n      return -1;\n    }\n  } else {\n    version = get_config()->spdy_downstream_version;\n  }\n  spdylay_session_callbacks callbacks;\n  memset(&callbacks, 0, sizeof(callbacks));\n  callbacks.send_callback = send_callback;\n  callbacks.recv_callback = recv_callback;\n  callbacks.on_stream_close_callback = on_stream_close_callback;\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n  callbacks.before_ctrl_send_callback = before_ctrl_send_callback;\n  callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback;\n  callbacks.on_ctrl_recv_parse_error_callback =\n    on_ctrl_recv_parse_error_callback;\n  callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;\n\n  rv = spdylay_session_client_new(&session_, version, &callbacks, this);\n  if(rv != 0) {\n    return -1;\n  }\n\n  if(version >= SPDYLAY_PROTO_SPDY3) {\n    int val = 1;\n    flow_control_ = true;\n    rv = spdylay_session_set_option(session_,\n                                    SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val,\n                                    sizeof(val));\n    assert(rv == 0);\n  } else {\n    flow_control_ = false;\n  }\n\n  spdylay_settings_entry entry[2];\n  entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  entry[0].value = get_config()->spdy_max_concurrent_streams;\n  entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  entry[1].value = 1 << get_config()->spdy_downstream_window_bits;\n  entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  rv = spdylay_submit_settings\n    (session_, SPDYLAY_FLAG_SETTINGS_NONE,\n     entry, sizeof(entry)/sizeof(spdylay_settings_entry));\n  if(rv != 0) {\n    return -1;\n  }\n\n  if(version >= SPDYLAY_PROTO_SPDY3_1 &&\n     get_config()->spdy_downstream_connection_window_bits > 16) {\n    int32_t delta =\n      (1 << get_config()->spdy_downstream_connection_window_bits)\n      - SPDYLAY_INITIAL_WINDOW_SIZE;\n    rv = spdylay_submit_window_update(session_, 0, delta);\n    if(rv != 0) {\n      return -1;\n    }\n  }\n\n  rv = send();\n  if(rv != 0) {\n    return -1;\n  }\n\n  // submit pending request\n  for(std::set<SpdyDownstreamConnection*>::iterator i = dconns_.begin(),\n        eoi = dconns_.end(); i != eoi; ++i) {\n    if((*i)->push_request_headers() != 0) {\n      return -1;\n    }\n  }\n  return 0;\n}\n\nint SpdySession::on_read()\n{\n  int rv = 0;\n  if((rv = spdylay_session_recv(session_)) < 0) {\n    if(rv != SPDYLAY_ERR_EOF) {\n      SSLOG(ERROR, this) << \"spdylay_session_recv() returned error: \"\n                         << spdylay_strerror(rv);\n    }\n  } else if((rv = spdylay_session_send(session_)) < 0) {\n    SSLOG(ERROR, this) << \"spdylay_session_send() returned error: \"\n                       << spdylay_strerror(rv);\n  }\n  if(rv == 0) {\n    if(spdylay_session_want_read(session_) == 0 &&\n       spdylay_session_want_write(session_) == 0 &&\n       evbuffer_get_length(bufferevent_get_output(bev_)) == 0) {\n      if(LOG_ENABLED(INFO)) {\n        SSLOG(INFO, this) << \"No more read/write for this session\";\n      }\n      rv = -1;\n    }\n  }\n  return rv;\n}\n\nint SpdySession::on_write()\n{\n  return send();\n}\n\nint SpdySession::send()\n{\n  int rv = 0;\n  if((rv = spdylay_session_send(session_)) < 0) {\n    SSLOG(ERROR, this) << \"spdylay_session_send() returned error: \"\n                       << spdylay_strerror(rv);\n  }\n  if(rv == 0) {\n    if(spdylay_session_want_read(session_) == 0 &&\n       spdylay_session_want_write(session_) == 0 &&\n       evbuffer_get_length(bufferevent_get_output(bev_)) == 0) {\n      if(LOG_ENABLED(INFO)) {\n        SSLOG(INFO, this) << \"No more read/write for this session\";\n      }\n      rv = -1;\n    }\n  }\n  return rv;\n}\n\nvoid SpdySession::clear_notify()\n{\n  evbuffer *input = bufferevent_get_output(rdbev_);\n  evbuffer_drain(input, evbuffer_get_length(input));\n  notified_ = false;\n}\n\nvoid SpdySession::notify()\n{\n  if(!notified_) {\n    bufferevent_write(wrbev_, \"1\", 1);\n    notified_ = true;\n  }\n}\n\nbufferevent* SpdySession::get_bev() const\n{\n  return bev_;\n}\n\nint SpdySession::get_state() const\n{\n  return state_;\n}\n\nvoid SpdySession::set_state(int state)\n{\n  state_ = state;\n}\n\nspdylay_session* SpdySession::get_session() const\n{\n  return session_;\n}\n\nsize_t SpdySession::get_outbuf_length() const\n{\n  if(bev_) {\n    return evbuffer_get_length(bufferevent_get_output(bev_));\n  } else {\n    return OUTBUF_MAX_THRES;\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_spdy_session.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_SPDY_SESSION_H\n#define SHRPX_SPDY_SESSION_H\n\n#include \"shrpx.h\"\n\n#include <set>\n\n#include <openssl/ssl.h>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\n#include <spdylay/spdylay.h>\n\n#include \"http-parser/http_parser.h\"\n\nnamespace shrpx {\n\nclass SpdyDownstreamConnection;\n\nstruct StreamData {\n  SpdyDownstreamConnection *dconn;\n};\n\nclass SpdySession {\npublic:\n  SpdySession(event_base *evbase, SSL_CTX *ssl_ctx);\n  ~SpdySession();\n\n  int init_notification();\n\n  int check_cert();\n\n  int disconnect();\n  int initiate_connection();\n\n  void add_downstream_connection(SpdyDownstreamConnection *dconn);\n  void remove_downstream_connection(SpdyDownstreamConnection *dconn);\n\n  void remove_stream_data(StreamData *sd);\n\n  int submit_request(SpdyDownstreamConnection *dconn,\n                     uint8_t pri, const char **nv,\n                     const spdylay_data_provider *data_prd);\n\n  int submit_rst_stream(SpdyDownstreamConnection *dconn,\n                        int32_t stream_id, uint32_t status_code);\n\n  int submit_window_update(SpdyDownstreamConnection *dconn, int32_t amount);\n\n  bool get_flow_control() const;\n\n  int resume_data(SpdyDownstreamConnection *dconn);\n\n  int on_connect();\n\n  int on_read();\n  int on_write();\n  int send();\n\n  int on_read_proxy();\n\n  void clear_notify();\n  void notify();\n\n  bufferevent* get_bev() const;\n  void unwrap_free_bev();\n\n  int get_state() const;\n  void set_state(int state);\n\n  spdylay_session* get_session() const;\n\n  size_t get_outbuf_length() const;\n\n  enum {\n    // Disconnected\n    DISCONNECTED,\n    // Connecting proxy and making CONNECT request\n    PROXY_CONNECTING,\n    // Tunnel is established with proxy\n    PROXY_CONNECTED,\n    // Establishing tunnel is failed\n    PROXY_FAILED,\n    // Connecting to downstream and/or performing SSL/TLS handshake\n    CONNECTING,\n    // Connected to downstream\n    CONNECTED\n  };\n\n  static const size_t OUTBUF_MAX_THRES = 64*1024;\nprivate:\n  std::set<SpdyDownstreamConnection*> dconns_;\n  std::set<StreamData*> streams_;\n  event_base *evbase_;\n  // NULL if no TLS is configured\n  SSL_CTX *ssl_ctx_;\n  SSL *ssl_;\n  spdylay_session *session_;\n  bufferevent *bev_;\n  bufferevent *wrbev_;\n  bufferevent *rdbev_;\n  // Used to parse the response from HTTP proxy\n  http_parser *proxy_htp_;\n  // fd_ is used for proxy connection and no TLS connection. For\n  // direct or TLS connection, it may be -1 even after connection is\n  // established. Use bufferevent_getfd(bev_) to get file descriptor\n  // in these cases.\n  int fd_;\n  int state_;\n  bool notified_;\n  bool flow_control_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_SPDY_SESSION_H\n"
  },
  {
    "path": "src/shrpx_spdy_upstream.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_spdy_upstream.h\"\n\n#include <netinet/tcp.h>\n#include <assert.h>\n#include <cerrno>\n#include <sstream>\n\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_downstream.h\"\n#include \"shrpx_downstream_connection.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_http.h\"\n#include \"shrpx_accesslog.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nnamespace {\nconst size_t OUTBUF_MAX_THRES = 64*1024;\n} // namespace\n\nnamespace {\nssize_t send_callback(spdylay_session *session,\n                      const uint8_t *data, size_t len, int flags,\n                      void *user_data)\n{\n  int rv;\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  ClientHandler *handler = upstream->get_client_handler();\n  bufferevent *bev = handler->get_bev();\n  evbuffer *output = bufferevent_get_output(bev);\n  // Check buffer length and return WOULDBLOCK if it is large enough.\n  if(handler->get_outbuf_length() > OUTBUF_MAX_THRES) {\n    return SPDYLAY_ERR_WOULDBLOCK;\n  }\n\n  rv = evbuffer_add(output, data, len);\n  if(rv == -1) {\n    ULOG(FATAL, upstream) << \"evbuffer_add() failed\";\n    return SPDYLAY_ERR_CALLBACK_FAILURE;\n  } else {\n    return len;\n  }\n}\n} // namespace\n\nnamespace {\nssize_t recv_callback(spdylay_session *session,\n                      uint8_t *data, size_t len, int flags, void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  ClientHandler *handler = upstream->get_client_handler();\n  bufferevent *bev = handler->get_bev();\n  evbuffer *input = bufferevent_get_input(bev);\n  int nread = evbuffer_remove(input, data, len);\n  if(nread == -1) {\n    return SPDYLAY_ERR_CALLBACK_FAILURE;\n  } else if(nread == 0) {\n    return SPDYLAY_ERR_WOULDBLOCK;\n  } else {\n    return nread;\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_stream_close_callback\n(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,\n void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"Stream stream_id=\" << stream_id\n                         << \" is being closed\";\n  }\n  Downstream *downstream = upstream->find_downstream(stream_id);\n  if(downstream) {\n    if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {\n      upstream->remove_downstream(downstream);\n      delete downstream;\n    } else {\n      downstream->set_request_state(Downstream::STREAM_CLOSED);\n      if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {\n        // At this point, downstream response was read\n        if(!downstream->tunnel_established() &&\n           !downstream->get_response_connection_close()) {\n          // Keep-alive\n          DownstreamConnection *dconn;\n          dconn = downstream->get_downstream_connection();\n          if(dconn) {\n            dconn->detach_downstream(downstream);\n          }\n        }\n        upstream->remove_downstream(downstream);\n        delete downstream;\n      } else {\n        // At this point, downstream read may be paused.\n\n        // If shrpx_downstream::push_request_headers() failed, the\n        // error is handled here.\n        upstream->remove_downstream(downstream);\n        delete downstream;\n        // How to test this case? Request sufficient large download\n        // and make client send RST_STREAM after it gets first DATA\n        // frame chunk.\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  switch(type) {\n  case SPDYLAY_SYN_STREAM: {\n    if(LOG_ENABLED(INFO)) {\n      ULOG(INFO, upstream) << \"Received upstream SYN_STREAM stream_id=\"\n                           << frame->syn_stream.stream_id;\n    }\n    Downstream *downstream;\n    downstream = new Downstream(upstream,\n                                frame->syn_stream.stream_id,\n                                frame->syn_stream.pri);\n    upstream->add_downstream(downstream);\n    downstream->init_response_body_buf();\n\n    char **nv = frame->syn_stream.nv;\n    const char *path = 0;\n    const char *scheme = 0;\n    const char *host = 0;\n    const char *method = 0;\n    const char *content_length = 0;\n    for(size_t i = 0; nv[i]; i += 2) {\n      if(strcmp(nv[i], \":path\") == 0) {\n        path = nv[i+1];\n      } else if(strcmp(nv[i], \":scheme\") == 0) {\n        scheme = nv[i+1];\n      } else if(strcmp(nv[i], \":method\") == 0) {\n        method = nv[i+1];\n        downstream->set_request_method(nv[i+1]);\n      } else if(strcmp(nv[i], \":host\") == 0) {\n        host = nv[i+1];\n      } else if(nv[i][0] != ':') {\n        if(strcmp(nv[i], \"content-length\") == 0) {\n          content_length = nv[i+1];\n        }\n        downstream->add_request_header(nv[i], nv[i+1]);\n      }\n    }\n    if(!path || !host || !method ||\n       !http::check_header_value(host) ||\n       !http::check_header_value(path) ||\n       !http::check_header_value(method) ||\n       (scheme && !http::check_header_value(scheme))) {\n      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n      return;\n    }\n    // Require content-length if FIN flag is not set.\n    if(!util::strieq(\"CONNECT\", method) &&\n       (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) == 0 &&\n       !content_length) {\n      upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);\n      return;\n    }\n    // SpdyDownstreamConnection examines request path to find\n    // scheme. We construct abs URI for spdy_bridge mode as well as\n    // spdy_proxy mode.\n    if((get_config()->spdy_proxy || get_config()->spdy_bridge) &&\n       scheme && path[0] == '/') {\n      std::string reqpath = scheme;\n      reqpath += \"://\";\n      reqpath += host;\n      reqpath += path;\n      downstream->set_request_path(reqpath);\n    } else {\n      downstream->set_request_path(path);\n    }\n\n    downstream->add_request_header(\"host\", host);\n\n    if(LOG_ENABLED(INFO)) {\n      std::stringstream ss;\n      for(size_t i = 0; nv[i]; i += 2) {\n        ss << TTY_HTTP_HD << nv[i] << TTY_RST << \": \" << nv[i+1] << \"\\n\";\n      }\n      ULOG(INFO, upstream) << \"HTTP request headers. stream_id=\"\n                           << downstream->get_stream_id()\n                           << \"\\n\" << ss.str();\n    }\n\n    DownstreamConnection *dconn;\n    dconn = upstream->get_client_handler()->get_downstream_connection();\n    int rv = dconn->attach_downstream(downstream);\n    if(rv != 0) {\n      // If downstream connection fails, issue RST_STREAM.\n      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n      downstream->set_request_state(Downstream::CONNECT_FAIL);\n      return;\n    }\n    rv = downstream->push_request_headers();\n    if(rv != 0) {\n      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n      return;\n    }\n    downstream->set_request_state(Downstream::HEADER_COMPLETE);\n    if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {\n      downstream->set_request_state(Downstream::MSG_COMPLETE);\n    }\n    break;\n  }\n  default:\n    break;\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_data_chunk_recv_callback(spdylay_session *session,\n                                 uint8_t flags, int32_t stream_id,\n                                 const uint8_t *data, size_t len,\n                                 void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  Downstream *downstream = upstream->find_downstream(stream_id);\n  if(downstream) {\n    if(downstream->push_upload_data_chunk(data, len) != 0) {\n      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n      return;\n    }\n    if(upstream->get_flow_control()) {\n      // If connection-level window control is not enabled (e.g,\n      // spdy/3), spdylay_session_get_recv_data_length() is always\n      // returns 0.\n      if(spdylay_session_get_recv_data_length(session) >\n         std::max(SPDYLAY_INITIAL_WINDOW_SIZE,\n                  1 << get_config()->spdy_upstream_connection_window_bits)) {\n        if(LOG_ENABLED(INFO)) {\n          ULOG(INFO, upstream)\n            << \"Flow control error on connection: \"\n            << \"recv_window_size=\"\n            << spdylay_session_get_recv_data_length(session)\n            << \", window_size=\"\n            << (1 << get_config()->spdy_upstream_connection_window_bits);\n        }\n        spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n        return;\n      }\n      if(spdylay_session_get_stream_recv_data_length(session, stream_id) >\n         std::max(SPDYLAY_INITIAL_WINDOW_SIZE,\n                  1 << get_config()->spdy_upstream_window_bits)) {\n        if(LOG_ENABLED(INFO)) {\n          ULOG(INFO, upstream)\n            << \"Flow control error: recv_window_size=\"\n            << spdylay_session_get_stream_recv_data_length(session, stream_id)\n            << \", initial_window_size=\"\n            << (1 << get_config()->spdy_upstream_window_bits);\n        }\n        upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR);\n        return;\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_data_recv_callback(spdylay_session *session, uint8_t flags,\n                           int32_t stream_id, int32_t length, void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  Downstream *downstream = upstream->find_downstream(stream_id);\n  if(downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {\n    downstream->end_upload_data();\n    downstream->set_request_state(Downstream::MSG_COMPLETE);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_not_send_callback(spdylay_session *session,\n                               spdylay_frame_type type,\n                               spdylay_frame *frame,\n                               int error_code, void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  ULOG(WARNING, upstream) << \"Failed to send control frame type=\" << type\n                          << \", error_code=\" << error_code << \":\"\n                          << spdylay_strerror(error_code);\n  if(type == SPDYLAY_SYN_REPLY) {\n    // To avoid stream hanging around, issue RST_STREAM.\n    int32_t stream_id = frame->syn_reply.stream_id;\n    Downstream *downstream = upstream->find_downstream(stream_id);\n    if(downstream) {\n      upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n    }\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_ctrl_recv_parse_error_callback(spdylay_session *session,\n                                       spdylay_frame_type type,\n                                       const uint8_t *head, size_t headlen,\n                                       const uint8_t *payload,\n                                       size_t payloadlen, int error_code,\n                                       void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"Failed to parse received control frame. type=\"\n                         << type\n                         << \", error_code=\" << error_code << \":\"\n                         << spdylay_strerror(error_code);\n  }\n}\n} // namespace\n\nnamespace {\nvoid on_unknown_ctrl_recv_callback(spdylay_session *session,\n                                   const uint8_t *head, size_t headlen,\n                                   const uint8_t *payload, size_t payloadlen,\n                                   void *user_data)\n{\n  SpdyUpstream *upstream = static_cast<SpdyUpstream*>(user_data);\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, upstream) << \"Received unknown control frame.\";\n  }\n}\n} // namespace\n\nnamespace {\nuint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_status_code)\n{\n  // Only propagate SPDYLAY_REFUSED_STREAM so that upstream client\n  // can resend request.\n  if(downstream_status_code != SPDYLAY_REFUSED_STREAM) {\n    return SPDYLAY_INTERNAL_ERROR;\n  } else {\n    return downstream_status_code;\n  }\n}\n} // namespace\n\nSpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)\n  : handler_(handler),\n    session_(0)\n{\n  //handler->set_bev_cb(spdy_readcb, 0, spdy_eventcb);\n  handler->set_upstream_timeouts(&get_config()->spdy_upstream_read_timeout,\n                                 &get_config()->upstream_write_timeout);\n\n  spdylay_session_callbacks callbacks;\n  memset(&callbacks, 0, sizeof(callbacks));\n  callbacks.send_callback = send_callback;\n  callbacks.recv_callback = recv_callback;\n  callbacks.on_stream_close_callback = on_stream_close_callback;\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n  callbacks.on_data_recv_callback = on_data_recv_callback;\n  callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback;\n  callbacks.on_ctrl_recv_parse_error_callback =\n    on_ctrl_recv_parse_error_callback;\n  callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;\n\n  int rv;\n  rv = spdylay_session_server_new(&session_, version, &callbacks, this);\n  assert(rv == 0);\n\n  int32_t initial_window_size = 0;\n  if(version >= SPDYLAY_PROTO_SPDY3) {\n    int val = 1;\n    flow_control_ = true;\n    initial_window_size = 1 << get_config()->spdy_upstream_window_bits;\n    rv = spdylay_session_set_option(session_,\n                                    SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE, &val,\n                                    sizeof(val));\n    assert(rv == 0);\n  } else {\n    flow_control_ = false;\n  }\n  // TODO Maybe call from outside?\n  spdylay_settings_entry entry[2];\n  entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  entry[0].value = get_config()->spdy_max_concurrent_streams;\n  entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  entry[1].value = initial_window_size;\n  entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  rv = spdylay_submit_settings\n    (session_, SPDYLAY_FLAG_SETTINGS_NONE,\n     entry, sizeof(entry)/sizeof(spdylay_settings_entry));\n  assert(rv == 0);\n\n  if(version >= SPDYLAY_PROTO_SPDY3_1 &&\n     get_config()->spdy_upstream_connection_window_bits > 16) {\n    int32_t delta = (1 << get_config()->spdy_upstream_connection_window_bits)\n      - SPDYLAY_INITIAL_WINDOW_SIZE;\n    rv = spdylay_submit_window_update(session_, 0, delta);\n    assert(rv == 0);\n  }\n\n  // TODO Maybe call from outside?\n  send();\n}\n\nSpdyUpstream::~SpdyUpstream()\n{\n  spdylay_session_del(session_);\n}\n\nint SpdyUpstream::on_read()\n{\n  int rv = 0;\n  if((rv = spdylay_session_recv(session_)) < 0) {\n    if(rv != SPDYLAY_ERR_EOF) {\n      ULOG(ERROR, this) << \"spdylay_session_recv() returned error: \"\n                        << spdylay_strerror(rv);\n    }\n  } else if((rv = spdylay_session_send(session_)) < 0) {\n    ULOG(ERROR, this) << \"spdylay_session_send() returned error: \"\n                      << spdylay_strerror(rv);\n  }\n  if(rv == 0) {\n    if(spdylay_session_want_read(session_) == 0 &&\n       spdylay_session_want_write(session_) == 0 &&\n       handler_->get_outbuf_length() == 0) {\n      if(LOG_ENABLED(INFO)) {\n        ULOG(INFO, this) << \"No more read/write for this SPDY session\";\n      }\n      rv = -1;\n    }\n  }\n  return rv;\n}\n\nint SpdyUpstream::on_write()\n{\n  return send();\n}\n\n// After this function call, downstream may be deleted.\nint SpdyUpstream::send()\n{\n  int rv = 0;\n  if((rv = spdylay_session_send(session_)) < 0) {\n    ULOG(ERROR, this) << \"spdylay_session_send() returned error: \"\n                      << spdylay_strerror(rv);\n  }\n  if(rv == 0) {\n    if(spdylay_session_want_read(session_) == 0 &&\n       spdylay_session_want_write(session_) == 0 &&\n       handler_->get_outbuf_length() == 0) {\n      if(LOG_ENABLED(INFO)) {\n        ULOG(INFO, this) << \"No more read/write for this SPDY session\";\n      }\n      rv = -1;\n    }\n  }\n  return rv;\n}\n\nint SpdyUpstream::on_event()\n{\n  return 0;\n}\n\nClientHandler* SpdyUpstream::get_client_handler() const\n{\n  return handler_;\n}\n\nnamespace {\nvoid spdy_downstream_readcb(bufferevent *bev, void *ptr)\n{\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  SpdyUpstream *upstream;\n  upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());\n  if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {\n    // If upstream SPDY stream was closed, we just close downstream,\n    // because there is no consumer now. Downstream connection is also\n    // closed in this case.\n    upstream->remove_downstream(downstream);\n    delete downstream;\n    return;\n  }\n\n  if(downstream->get_response_state() == Downstream::MSG_RESET) {\n    // The downstream stream was reset (canceled). In this case,\n    // RST_STREAM to the upstream and delete downstream connection\n    // here. Deleting downstream will be taken place at\n    // on_stream_close_callback.\n    upstream->rst_stream(downstream, infer_upstream_rst_stream_status_code\n                         (downstream->get_response_rst_stream_status_code()));\n    downstream->set_downstream_connection(0);\n    delete dconn;\n    dconn = 0;\n  } else {\n    int rv = downstream->on_read();\n    if(rv != 0) {\n      if(LOG_ENABLED(INFO)) {\n        DCLOG(INFO, dconn) << \"HTTP parser failure\";\n      }\n      if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n        upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n      } else if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {\n        // If response was completed, then don't issue RST_STREAM\n        if(upstream->error_reply(downstream, 502) != 0) {\n          delete upstream->get_client_handler();\n          return;\n        }\n      }\n      downstream->set_response_state(Downstream::MSG_COMPLETE);\n      // Clearly, we have to close downstream connection on http parser\n      // failure.\n      downstream->set_downstream_connection(0);\n      delete dconn;\n      dconn = 0;\n    }\n  }\n  if(upstream->send() != 0) {\n    delete upstream->get_client_handler();\n    return;\n  }\n  // At this point, downstream may be deleted.\n}\n} // namespace\n\nnamespace {\nvoid spdy_downstream_writecb(bufferevent *bev, void *ptr)\n{\n  if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {\n    return;\n  }\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  SpdyUpstream *upstream;\n  upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());\n  upstream->resume_read(SHRPX_NO_BUFFER, downstream);\n}\n} // namespace\n\nnamespace {\nvoid spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)\n{\n  DownstreamConnection *dconn = static_cast<DownstreamConnection*>(ptr);\n  Downstream *downstream = dconn->get_downstream();\n  SpdyUpstream *upstream;\n  upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());\n  if(events & BEV_EVENT_CONNECTED) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"Connection established. stream_id=\"\n                         << downstream->get_stream_id();\n    }\n    int fd = bufferevent_getfd(bev);\n    int val = 1;\n    if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,\n                  reinterpret_cast<char *>(&val), sizeof(val)) == -1) {\n      DCLOG(WARNING, dconn) << \"Setting option TCP_NODELAY failed: errno=\"\n                            << errno;\n    }\n  } else if(events & BEV_EVENT_EOF) {\n    if(LOG_ENABLED(INFO)) {\n      DCLOG(INFO, dconn) << \"EOF. stream_id=\" << downstream->get_stream_id();\n    }\n    if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {\n      // If stream was closed already, we don't need to send reply at\n      // the first place. We can delete downstream.\n      upstream->remove_downstream(downstream);\n      delete downstream;\n    } else {\n      // Delete downstream connection. If we don't delete it here, it\n      // will be pooled in on_stream_close_callback.\n      downstream->set_downstream_connection(0);\n      delete dconn;\n      dconn = 0;\n      // downstream wil be deleted in on_stream_close_callback.\n      if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n        // Server may indicate the end of the request by EOF\n        if(LOG_ENABLED(INFO)) {\n          ULOG(INFO, upstream) << \"Downstream body was ended by EOF\";\n        }\n        downstream->set_response_state(Downstream::MSG_COMPLETE);\n\n        // For tunneled connection, MSG_COMPLETE signals\n        // spdy_data_read_callback to send RST_STREAM after pending\n        // response body is sent. This is needed to ensure that\n        // RST_STREAM is sent after all pending data are sent.\n        upstream->on_downstream_body_complete(downstream);\n      } else if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {\n        // If stream was not closed, then we set MSG_COMPLETE and let\n        // on_stream_close_callback delete downstream.\n        if(upstream->error_reply(downstream, 502) != 0) {\n          delete upstream->get_client_handler();\n          return;\n        }\n        downstream->set_response_state(Downstream::MSG_COMPLETE);\n      }\n      if(upstream->send() != 0) {\n        delete upstream->get_client_handler();\n        return;\n      }\n      // At this point, downstream may be deleted.\n    }\n  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {\n    if(LOG_ENABLED(INFO)) {\n      if(events & BEV_EVENT_ERROR) {\n        DCLOG(INFO, dconn) << \"Downstream network error: \"\n                           << evutil_socket_error_to_string\n          (EVUTIL_SOCKET_ERROR());\n      } else {\n        DCLOG(INFO, dconn) << \"Timeout\";\n      }\n      if(downstream->tunnel_established()) {\n        DCLOG(INFO, dconn) << \"Note: this is tunnel connection\";\n      }\n    }\n    if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {\n      upstream->remove_downstream(downstream);\n      delete downstream;\n    } else {\n      // Delete downstream connection. If we don't delete it here, it\n      // will be pooled in on_stream_close_callback.\n      downstream->set_downstream_connection(0);\n      delete dconn;\n      dconn = 0;\n      if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {\n        // For SSL tunneling, we issue RST_STREAM. For other types of\n        // stream, we don't have to do anything since response was\n        // complete.\n        if(downstream->tunnel_established()) {\n          upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n        }\n      } else {\n        if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {\n          upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);\n        } else {\n          unsigned int status;\n          if(events & BEV_EVENT_TIMEOUT) {\n            status = 504;\n          } else {\n            status = 502;\n          }\n          if(upstream->error_reply(downstream, status) != 0) {\n            delete upstream->get_client_handler();\n            return;\n          }\n        }\n        downstream->set_response_state(Downstream::MSG_COMPLETE);\n      }\n      if(upstream->send() != 0) {\n        delete upstream->get_client_handler();\n        return;\n      }\n      // At this point, downstream may be deleted.\n    }\n  }\n}\n} // namespace\n\nint SpdyUpstream::rst_stream(Downstream *downstream, int status_code)\n{\n  if(LOG_ENABLED(INFO)) {\n    ULOG(INFO, this) << \"RST_STREAM stream_id=\"\n                     << downstream->get_stream_id();\n  }\n  int rv;\n  rv = spdylay_submit_rst_stream(session_, downstream->get_stream_id(),\n                                 status_code);\n  if(rv < SPDYLAY_ERR_FATAL) {\n    ULOG(FATAL, this) << \"spdylay_submit_rst_stream() failed: \"\n                      << spdylay_strerror(rv);\n    DIE();\n  }\n  return 0;\n}\n\nint SpdyUpstream::window_update(Downstream *downstream, int32_t delta)\n{\n  int rv;\n  rv = spdylay_submit_window_update(session_,\n                                    downstream ?\n                                    downstream->get_stream_id() : 0,\n                                    delta);\n  if(rv < SPDYLAY_ERR_FATAL) {\n    ULOG(FATAL, this) << \"spdylay_submit_window_update() failed: \"\n                      << spdylay_strerror(rv);\n    DIE();\n  }\n  return 0;\n}\n\nnamespace {\nssize_t spdy_data_read_callback(spdylay_session *session,\n                                int32_t stream_id,\n                                uint8_t *buf, size_t length,\n                                int *eof,\n                                spdylay_data_source *source,\n                                void *user_data)\n{\n  Downstream *downstream = static_cast<Downstream*>(source->ptr);\n  SpdyUpstream *upstream =\n    static_cast<SpdyUpstream*>(downstream->get_upstream());\n  ClientHandler *handler = upstream->get_client_handler();\n  evbuffer *body = downstream->get_response_body_buf();\n  assert(body);\n  int nread = evbuffer_remove(body, buf, length);\n  if(nread == 0 &&\n     downstream->get_response_state() == Downstream::MSG_COMPLETE) {\n    if(!downstream->tunnel_established()) {\n      *eof = 1;\n    } else {\n      // For tunneling, issue RST_STREAM to finish the stream.\n      if(LOG_ENABLED(INFO)) {\n        ULOG(INFO, upstream) << \"RST_STREAM to tunneled stream stream_id=\"\n                             << stream_id;\n      }\n      upstream->rst_stream(downstream, infer_upstream_rst_stream_status_code\n                           (downstream->get_response_rst_stream_status_code()));\n    }\n  }\n  // Send WINDOW_UPDATE before buffer is empty to avoid delay because\n  // of RTT.\n  if(*eof != 1 &&\n     handler->get_outbuf_length() + evbuffer_get_length(body) <\n     OUTBUF_MAX_THRES) {\n    if(downstream->resume_read(SHRPX_NO_BUFFER) != 0) {\n      return SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  }\n  if(nread == 0 && *eof != 1) {\n    return SPDYLAY_ERR_DEFERRED;\n  }\n  return nread;\n}\n} // namespace\n\nint SpdyUpstream::error_reply(Downstream *downstream, unsigned int status_code)\n{\n  int rv;\n  std::string html = http::create_error_html(status_code);\n  downstream->init_response_body_buf();\n  evbuffer *body = downstream->get_response_body_buf();\n  rv = evbuffer_add(body, html.c_str(), html.size());\n  if(rv == -1) {\n    ULOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  downstream->set_response_state(Downstream::MSG_COMPLETE);\n\n  spdylay_data_provider data_prd;\n  data_prd.source.ptr = downstream;\n  data_prd.read_callback = spdy_data_read_callback;\n\n  std::string content_length = util::utos(html.size());\n  std::string status_string = http::get_status_string(status_code);\n  const char *nv[] = {\n    \":status\", status_string.c_str(),\n    \":version\", \"http/1.1\",\n    \"content-type\", \"text/html; charset=UTF-8\",\n    \"server\", get_config()->server_name,\n    \"content-length\", content_length.c_str(),\n    0\n  };\n\n  rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,\n                               &data_prd);\n  if(rv < SPDYLAY_ERR_FATAL) {\n    ULOG(FATAL, this) << \"spdylay_submit_response() failed: \"\n                      << spdylay_strerror(rv);\n    DIE();\n  }\n  if(get_config()->accesslog) {\n    upstream_response(get_client_handler()->get_ipaddr(),\n                      status_code, downstream);\n  }\n  return 0;\n}\n\nbufferevent_data_cb SpdyUpstream::get_downstream_readcb()\n{\n  return spdy_downstream_readcb;\n}\n\nbufferevent_data_cb SpdyUpstream::get_downstream_writecb()\n{\n  return spdy_downstream_writecb;\n}\n\nbufferevent_event_cb SpdyUpstream::get_downstream_eventcb()\n{\n  return spdy_downstream_eventcb;\n}\n\nvoid SpdyUpstream::add_downstream(Downstream *downstream)\n{\n  downstream_queue_.add(downstream);\n}\n\nvoid SpdyUpstream::remove_downstream(Downstream *downstream)\n{\n  downstream_queue_.remove(downstream);\n}\n\nDownstream* SpdyUpstream::find_downstream(int32_t stream_id)\n{\n  return downstream_queue_.find(stream_id);\n}\n\nspdylay_session* SpdyUpstream::get_spdy_session()\n{\n  return session_;\n}\n\n// WARNING: Never call directly or indirectly spdylay_session_send or\n// spdylay_session_recv. These calls may delete downstream.\nint SpdyUpstream::on_downstream_header_complete(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, downstream) << \"HTTP response header completed\";\n  }\n  size_t nheader = downstream->get_response_headers().size();\n  // 6 means :status, :version and possible via header field.\n  const char **nv = new const char*[nheader * 2 + 6 + 1];\n  size_t hdidx = 0;\n  std::string via_value;\n  std::string status_string =\n    http::get_status_string(downstream->get_response_http_status());\n  nv[hdidx++] = \":status\";\n  nv[hdidx++] = status_string.c_str();\n  nv[hdidx++] = \":version\";\n  nv[hdidx++] = \"HTTP/1.1\";\n  for(Headers::const_iterator i = downstream->get_response_headers().begin();\n      i != downstream->get_response_headers().end(); ++i) {\n    if(util::strieq((*i).first.c_str(), \"transfer-encoding\") ||\n       util::strieq((*i).first.c_str(), \"keep-alive\") || // HTTP/1.0?\n       util::strieq((*i).first.c_str(), \"connection\") ||\n       util:: strieq((*i).first.c_str(), \"proxy-connection\")) {\n      // These are ignored\n    } else if(!get_config()->no_via &&\n              util::strieq((*i).first.c_str(), \"via\")) {\n      via_value = (*i).second;\n    } else {\n      nv[hdidx++] = (*i).first.c_str();\n      nv[hdidx++] = (*i).second.c_str();\n    }\n  }\n  if(!get_config()->no_via) {\n    if(!via_value.empty()) {\n      via_value += \", \";\n    }\n    via_value += http::create_via_header_value\n      (downstream->get_response_major(), downstream->get_response_minor());\n    nv[hdidx++] = \"via\";\n    nv[hdidx++] = via_value.c_str();\n  }\n  nv[hdidx++] = 0;\n  if(LOG_ENABLED(INFO)) {\n    std::stringstream ss;\n    for(size_t i = 0; nv[i]; i += 2) {\n      ss << TTY_HTTP_HD << nv[i] << TTY_RST << \": \" << nv[i+1] << \"\\n\";\n    }\n    ULOG(INFO, this) << \"HTTP response headers. stream_id=\"\n                     << downstream->get_stream_id() << \"\\n\"\n                     << ss.str();\n  }\n  spdylay_data_provider data_prd;\n  data_prd.source.ptr = downstream;\n  data_prd.read_callback = spdy_data_read_callback;\n\n  int rv;\n  rv = spdylay_submit_response(session_, downstream->get_stream_id(), nv,\n                               &data_prd);\n  delete [] nv;\n  if(rv != 0) {\n    ULOG(FATAL, this) << \"spdylay_submit_response() failed\";\n    return -1;\n  }\n  if(get_config()->accesslog) {\n    upstream_response(get_client_handler()->get_ipaddr(),\n                      downstream->get_response_http_status(),\n                      downstream);\n  }\n  return 0;\n}\n\n// WARNING: Never call directly or indirectly spdylay_session_send or\n// spdylay_session_recv. These calls may delete downstream.\nint SpdyUpstream::on_downstream_body(Downstream *downstream,\n                                     const uint8_t *data, size_t len)\n{\n  Upstream *upstream = downstream->get_upstream();\n  evbuffer *body = downstream->get_response_body_buf();\n  int rv = evbuffer_add(body, data, len);\n  if(rv != 0) {\n    ULOG(FATAL, this) << \"evbuffer_add() failed\";\n    return -1;\n  }\n  spdylay_session_resume_data(session_, downstream->get_stream_id());\n\n  size_t outbuflen = upstream->get_client_handler()->get_outbuf_length()\n    + evbuffer_get_length(body);\n  if(outbuflen > OUTBUF_MAX_THRES) {\n    downstream->pause_read(SHRPX_NO_BUFFER);\n  }\n\n  return 0;\n}\n\n// WARNING: Never call directly or indirectly spdylay_session_send or\n// spdylay_session_recv. These calls may delete downstream.\nint SpdyUpstream::on_downstream_body_complete(Downstream *downstream)\n{\n  if(LOG_ENABLED(INFO)) {\n    DLOG(INFO, downstream) << \"HTTP response completed\";\n  }\n  spdylay_session_resume_data(session_, downstream->get_stream_id());\n  return 0;\n}\n\nbool SpdyUpstream::get_flow_control() const\n{\n  return flow_control_;\n}\n\nvoid SpdyUpstream::pause_read(IOCtrlReason reason)\n{}\n\nint SpdyUpstream::resume_read(IOCtrlReason reason, Downstream *downstream)\n{\n  if(get_flow_control()) {\n    int32_t delta;\n    delta = http::determine_window_update_transmission\n      (session_, 0, 1 << get_config()->spdy_upstream_connection_window_bits);\n    if(delta != -1) {\n      window_update(0, delta);\n    }\n    delta = http::determine_window_update_transmission\n      (session_, downstream->get_stream_id(),\n       1 << get_config()->spdy_upstream_window_bits);\n    if(delta != -1) {\n      window_update(downstream, delta);\n    }\n  }\n  return send();\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_spdy_upstream.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_SPDY_UPSTREAM_H\n#define SHRPX_SPDY_UPSTREAM_H\n\n#include \"shrpx.h\"\n\n#include <spdylay/spdylay.h>\n\n#include \"shrpx_upstream.h\"\n#include \"shrpx_downstream_queue.h\"\n\nnamespace shrpx {\n\nclass ClientHandler;\n\nclass SpdyUpstream : public Upstream {\npublic:\n  SpdyUpstream(uint16_t version, ClientHandler *handler);\n  virtual ~SpdyUpstream();\n  virtual int on_read();\n  virtual int on_write();\n  virtual int on_event();\n  int send();\n  virtual ClientHandler* get_client_handler() const;\n  virtual bufferevent_data_cb get_downstream_readcb();\n  virtual bufferevent_data_cb get_downstream_writecb();\n  virtual bufferevent_event_cb get_downstream_eventcb();\n  void add_downstream(Downstream *downstream);\n  void remove_downstream(Downstream *downstream);\n  Downstream* find_downstream(int32_t stream_id);\n\n  spdylay_session* get_spdy_session();\n\n  int rst_stream(Downstream *downstream, int status_code);\n  int window_update(Downstream *downstream, int32_t delta);\n  int error_reply(Downstream *downstream, unsigned int status_code);\n\n  virtual void pause_read(IOCtrlReason reason);\n  virtual int resume_read(IOCtrlReason reason, Downstream *downstream);\n\n  virtual int on_downstream_header_complete(Downstream *downstream);\n  virtual int on_downstream_body(Downstream *downstream,\n                                 const uint8_t *data, size_t len);\n  virtual int on_downstream_body_complete(Downstream *downstream);\n\n  bool get_flow_control() const;\nprivate:\n  DownstreamQueue downstream_queue_;\n  ClientHandler *handler_;\n  spdylay_session *session_;\n  bool flow_control_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_SPDY_UPSTREAM_H\n"
  },
  {
    "path": "src/shrpx_ssl.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_ssl.h\"\n\n#include <sys/socket.h>\n#include <netdb.h>\n#include <netinet/tcp.h>\n#include <pthread.h>\n\n#include <vector>\n#include <cstring>\n\n#include <openssl/crypto.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n\n#include <event2/bufferevent.h>\n#include <event2/bufferevent_ssl.h>\n\n#include <spdylay/spdylay.h>\n\n#include \"shrpx_log.h\"\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_config.h\"\n#include \"shrpx_accesslog.h\"\n#include \"util.h\"\n\nusing namespace spdylay;\n\nnamespace shrpx {\n\nnamespace ssl {\n\n#if defined(LIBRESSL_VERSION_NUMBER)\n#define LIBRESSL_IN_USE 1\n#else // !defined(LIBRESSL_VERSION_NUMBER)\n#define LIBRESSL_IN_USE 0\n#endif // !defined(LIBRESSL_VERSION_NUMBER)\n\n#define OPENSSL_1_1_API                                                        \\\n  (!LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x1010000fL)\n\n#if !OPENSSL_1_1_API\nnamespace {\nconst unsigned char *ASN1_STRING_get0_data(ASN1_STRING *x) {\n  return ASN1_STRING_data(x);\n}\n} // namespace\n#endif // !OPENSSL_1_1_API\n\nnamespace {\nstd::pair<unsigned char*, size_t> next_proto;\nunsigned char proto_list[255];\n} // namespace\n\nnamespace {\nint next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,\n                  void *arg)\n{\n  std::pair<unsigned char*, size_t> *next_proto =\n    static_cast<std::pair<unsigned char*, size_t>* >(arg);\n  *data = next_proto->first;\n  *len = next_proto->second;\n  return SSL_TLSEXT_ERR_OK;\n}\n} // namespace\n\nnamespace {\nint verify_callback(int preverify_ok, X509_STORE_CTX *ctx)\n{\n  if(!preverify_ok) {\n    int err = X509_STORE_CTX_get_error(ctx);\n    int depth = X509_STORE_CTX_get_error_depth(ctx);\n    LOG(ERROR) << \"client certificate verify error:num=\" << err << \":\"\n               << X509_verify_cert_error_string(err)\n               << \":depth=\" << depth;\n  }\n  return preverify_ok;\n}\n} // namespace\n\nnamespace {\nsize_t set_npn_prefs(unsigned char *out, const char **protos, size_t len)\n{\n  unsigned char *ptr = out;\n  for(size_t i = 0; i < len; ++i) {\n    *ptr = strlen(protos[i]);\n    memcpy(ptr+1, protos[i], *ptr);\n    ptr += *ptr+1;\n  }\n  return ptr - out;\n}\n} // namespace\n\nnamespace {\nint ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data)\n{\n  Config *config = (Config *)user_data;\n  int len = (int)strlen(config->private_key_passwd);\n  if (size < len + 1) {\n    LOG(ERROR) << \"ssl_pem_passwd_cb: buf is too small \" << size;\n    return 0;\n  }\n  // Copy string including last '\\0'.\n  memcpy(buf, config->private_key_passwd, len+1);\n  return len;\n}\n} // namespace\n\nnamespace {\nint servername_callback(SSL *ssl, int *al, void *arg)\n{\n  if(get_config()->cert_tree) {\n    const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n    if(hostname) {\n      SSL_CTX *ssl_ctx = cert_lookup_tree_lookup(get_config()->cert_tree,\n                                                 hostname, strlen(hostname));\n      if(ssl_ctx) {\n        SSL_set_SSL_CTX(ssl, ssl_ctx);\n      }\n    }\n  }\n  return SSL_TLSEXT_ERR_OK;\n}\n} // namespace\n\nnamespace {\nvoid info_callback(const SSL *ssl, int where, int ret)\n{\n  // To mitigate possible DOS attack using lots of renegotiations, we\n  // disable renegotiation. Since OpenSSL does not provide an easy way\n  // to disable it, we check that renegotiation is started in this\n  // callback.\n  if(where & SSL_CB_HANDSHAKE_START) {\n    ClientHandler *handler = static_cast<ClientHandler*>(SSL_get_app_data(ssl));\n    if(handler && handler->get_tls_handshake()) {\n      handler->set_tls_renegotiation(true);\n      if(LOG_ENABLED(INFO)) {\n        CLOG(INFO, handler) << \"TLS renegotiation started\";\n      }\n    }\n  }\n}\n} // namespace\n\nnamespace {\nconst char *names[] = { \"TLSv1.2\", \"TLSv1.1\", \"TLSv1.0\", \"SSLv3\" };\nconst size_t namelen = sizeof(names)/sizeof(names[0]);\nconst long int masks[] = { SSL_OP_NO_TLSv1_2, SSL_OP_NO_TLSv1_1,\n                           SSL_OP_NO_TLSv1, SSL_OP_NO_SSLv3 };\nlong int create_tls_proto_mask(char **tls_proto_list, size_t len)\n{\n  long int res = 0;\n  for(size_t i = 0; i < namelen; ++i) {\n    size_t j;\n    for(j = 0; j < len; ++j) {\n      if(strcasecmp(names[i], tls_proto_list[j]) == 0) {\n        break;\n      }\n    }\n    if(j == len) {\n      res |= masks[i];\n    }\n  }\n  return res;\n}\n} // namespace\n\nSSL_CTX* create_ssl_context(const char *private_key_file,\n                            const char *cert_file)\n{\n  SSL_CTX *ssl_ctx;\n  ssl_ctx = SSL_CTX_new(SSLv23_server_method());\n  if(!ssl_ctx) {\n    LOG(FATAL) << ERR_error_string(ERR_get_error(), 0);\n    DIE();\n  }\n  SSL_CTX_set_options(ssl_ctx,\n                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |\n                      SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |\n                      SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE |\n                      SSL_OP_NO_TICKET |\n                      create_tls_proto_mask(get_config()->tls_proto_list,\n                                            get_config()->tls_proto_list_len));\n\n  const unsigned char sid_ctx[] = \"shrpx\";\n  SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx)-1);\n  SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);\n\n  if(get_config()->ciphers) {\n    if(SSL_CTX_set_cipher_list(ssl_ctx, get_config()->ciphers) == 0) {\n      LOG(FATAL) << \"SSL_CTX_set_cipher_list failed: \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n    SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n  } else if(get_config()->honor_cipher_order) {\n    SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n  }\n\n#ifndef OPENSSL_NO_EC\n  EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);\n  if(ecdh == NULL) {\n    LOG(FATAL) << \"EC_KEY_new_by_curv_name failed: \"\n               << ERR_error_string(ERR_get_error(), NULL);\n    DIE();\n  }\n  SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);\n  EC_KEY_free(ecdh);\n#endif // OPENSSL_NO_EC\n\n  if(get_config()->dh_param_file) {\n    BIO *bio = BIO_new_file(get_config()->dh_param_file, \"r\");\n    if(bio == NULL) {\n      LOG(FATAL) << \"BIO_new_file() failed: \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n    DH *dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);\n    if(dh == NULL) {\n      LOG(FATAL) << \"PEM_read_bio_DHparams() failed: \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n    SSL_CTX_set_tmp_dh(ssl_ctx, dh);\n    DH_free(dh);\n    BIO_free(bio);\n  }\n\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n  if (get_config()->private_key_passwd) {\n    SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);\n    SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config());\n  }\n  if(SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file,\n                                 SSL_FILETYPE_PEM) != 1) {\n    LOG(FATAL) << \"SSL_CTX_use_PrivateKey_file failed: \"\n               << ERR_error_string(ERR_get_error(), NULL);\n    DIE();\n  }\n  if(SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {\n    LOG(FATAL) << \"SSL_CTX_use_certificate_file failed: \"\n               << ERR_error_string(ERR_get_error(), NULL);\n    DIE();\n  }\n  if(SSL_CTX_check_private_key(ssl_ctx) != 1) {\n    LOG(FATAL) << \"SSL_CTX_check_private_key failed: \"\n               << ERR_error_string(ERR_get_error(), NULL);\n    DIE();\n  }\n  if(get_config()->verify_client) {\n    if(get_config()->verify_client_cacert) {\n      if(SSL_CTX_load_verify_locations(ssl_ctx,\n                                       get_config()->verify_client_cacert,\n                                       0) != 1) {\n        LOG(FATAL) << \"Could not load trusted ca certificates from \"\n                   << get_config()->verify_client_cacert << \": \"\n                   << ERR_error_string(ERR_get_error(), 0);\n        DIE();\n      }\n      // It is heard that SSL_CTX_load_verify_locations() may leave\n      // error even though it returns success. See\n      // http://forum.nginx.org/read.php?29,242540\n      ERR_clear_error();\n      STACK_OF(X509_NAME) *list =\n        SSL_load_client_CA_file(get_config()->verify_client_cacert);\n      if(!list) {\n        LOG(FATAL) << \"Could not load ca certificates from \"\n                   << get_config()->verify_client_cacert << \": \"\n                   << ERR_error_string(ERR_get_error(), 0);\n        DIE();\n      }\n      SSL_CTX_set_client_CA_list(ssl_ctx, list);\n    }\n    SSL_CTX_set_verify(ssl_ctx,\n                       SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |\n                       SSL_VERIFY_FAIL_IF_NO_PEER_CERT,\n                       verify_callback);\n  }\n  SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);\n  SSL_CTX_set_info_callback(ssl_ctx, info_callback);\n\n  // We speak \"http/1.1\", \"spdy/2\", \"spdy/3\" and \"spdy/3.1\".\n  const char *protos[] = { \"spdy/3.1\", \"spdy/3\", \"spdy/2\", \"http/1.1\" };\n  size_t proto_list_len = set_npn_prefs(proto_list, protos,\n                                        sizeof(protos)/sizeof(protos[0]));\n\n  next_proto.first = proto_list;\n  next_proto.second = proto_list_len;\n  SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);\n  return ssl_ctx;\n}\n\nnamespace {\nint select_next_proto_cb(SSL* ssl,\n                         unsigned char **out, unsigned char *outlen,\n                         const unsigned char *in, unsigned int inlen,\n                         void *arg)\n{\n  if(spdylay_select_next_protocol(out, outlen, in, inlen) <= 0) {\n    *out = (unsigned char*)\"spdy/3.1\";\n    *outlen = 8;\n  }\n  return SSL_TLSEXT_ERR_OK;\n}\n} // namespace\n\nSSL_CTX* create_ssl_client_context()\n{\n  SSL_CTX *ssl_ctx;\n  ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n  if(!ssl_ctx) {\n    LOG(FATAL) << ERR_error_string(ERR_get_error(), 0);\n    DIE();\n  }\n  SSL_CTX_set_options(ssl_ctx,\n                      SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |\n                      SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |\n                      create_tls_proto_mask(get_config()->tls_proto_list,\n                                            get_config()->tls_proto_list_len));\n\n  if(get_config()->ciphers) {\n    if(SSL_CTX_set_cipher_list(ssl_ctx, get_config()->ciphers) == 0) {\n      LOG(FATAL) << \"SSL_CTX_set_cipher_list failed: \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n  }\n\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n\n  if(SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {\n    LOG(WARNING) << \"Could not load system trusted ca certificates: \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n  }\n\n  if(get_config()->cacert) {\n    if(SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert, 0) != 1) {\n      LOG(FATAL) << \"Could not load trusted ca certificates from \"\n                 << get_config()->cacert << \": \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n  }\n\n  if(get_config()->client_private_key_file) {\n    if(SSL_CTX_use_PrivateKey_file(ssl_ctx,\n                                   get_config()->client_private_key_file,\n                                   SSL_FILETYPE_PEM) != 1) {\n      LOG(FATAL) << \"Could not load client private key from \"\n                 << get_config()->client_private_key_file << \": \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n  }\n  if(get_config()->client_cert_file) {\n    if(SSL_CTX_use_certificate_chain_file(ssl_ctx,\n                                          get_config()->client_cert_file)\n       != 1) {\n      LOG(FATAL) << \"Could not load client certificate from \"\n                 << get_config()->client_cert_file << \": \"\n                 << ERR_error_string(ERR_get_error(), NULL);\n      DIE();\n    }\n  }\n\n  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, 0);\n  return ssl_ctx;\n}\n\nClientHandler* accept_connection\n(event_base *evbase,\n bufferevent_rate_limit_group *rate_limit_group,\n SSL_CTX *ssl_ctx,\n evutil_socket_t fd,\n sockaddr *addr, int addrlen)\n{\n  char host[NI_MAXHOST];\n  int rv;\n  rv = getnameinfo(addr, addrlen, host, sizeof(host), 0, 0, NI_NUMERICHOST);\n  if(rv == 0) {\n    if(get_config()->accesslog) {\n      upstream_connect(host);\n    }\n\n    int val = 1;\n    rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,\n                    reinterpret_cast<char *>(&val), sizeof(val));\n    if(rv == -1) {\n      LOG(WARNING) << \"Setting option TCP_NODELAY failed: errno=\"\n                   << errno;\n    }\n    SSL *ssl = 0;\n    bufferevent *bev;\n    if(ssl_ctx) {\n      ssl = SSL_new(ssl_ctx);\n      if(!ssl) {\n        LOG(ERROR) << \"SSL_new() failed: \"\n                   << ERR_error_string(ERR_get_error(), NULL);\n        return 0;\n      }\n      if(SSL_set_fd(ssl, fd) == 0) {\n        LOG(ERROR) << \"SSL_set_fd() failed: \"\n                   << ERR_error_string(ERR_get_error(), NULL);\n        SSL_free(ssl);\n        return 0;\n      }\n      bev = bufferevent_openssl_socket_new(evbase, fd, ssl,\n                                           BUFFEREVENT_SSL_ACCEPTING,\n                                           BEV_OPT_DEFER_CALLBACKS);\n    } else {\n      bev = bufferevent_socket_new(evbase, fd, BEV_OPT_DEFER_CALLBACKS);\n    }\n    return new ClientHandler(bev, rate_limit_group, fd, ssl, host);\n  } else {\n    LOG(ERROR) << \"getnameinfo() failed: \" << gai_strerror(rv);\n    return 0;\n  }\n}\n\nbool numeric_host(const char *hostname)\n{\n  struct addrinfo hints;\n  struct addrinfo* res;\n  memset(&hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_flags = AI_NUMERICHOST;\n  if(getaddrinfo(hostname, 0, &hints, &res)) {\n    return false;\n  }\n  freeaddrinfo(res);\n  return true;\n}\n\nnamespace {\nbool tls_hostname_match(const char *pattern, const char *hostname)\n{\n  const char *ptWildcard = strchr(pattern, '*');\n  if(ptWildcard == 0) {\n    return util::strieq(pattern, hostname);\n  }\n  const char *ptLeftLabelEnd = strchr(pattern, '.');\n  bool wildcardEnabled = true;\n  // Do case-insensitive match. At least 2 dots are required to enable\n  // wildcard match. Also wildcard must be in the left-most label.\n  // Don't attempt to match a presented identifier where the wildcard\n  // character is embedded within an A-label.\n  if(ptLeftLabelEnd == 0 || strchr(ptLeftLabelEnd+1, '.') == 0 ||\n     ptLeftLabelEnd < ptWildcard || util::istartsWith(pattern, \"xn--\")) {\n    wildcardEnabled = false;\n  }\n  if(!wildcardEnabled) {\n    return util::strieq(pattern, hostname);\n  }\n  const char *hnLeftLabelEnd = strchr(hostname, '.');\n  if(hnLeftLabelEnd == 0 || !util::strieq(ptLeftLabelEnd, hnLeftLabelEnd)) {\n    return false;\n  }\n  // Perform wildcard match. Here '*' must match at least one\n  // character.\n  if(hnLeftLabelEnd - hostname < ptLeftLabelEnd - pattern) {\n    return false;\n  }\n  return util::istartsWith(hostname, hnLeftLabelEnd, pattern, ptWildcard) &&\n    util::iendsWith(hostname, hnLeftLabelEnd, ptWildcard+1, ptLeftLabelEnd);\n}\n} // namespace\n\nnamespace {\nint verify_hostname(const char *hostname,\n                    const sockaddr_union *su,\n                    size_t salen,\n                    const std::vector<std::string>& dns_names,\n                    const std::vector<std::string>& ip_addrs,\n                    const std::string& common_name)\n{\n  if(numeric_host(hostname)) {\n    if(ip_addrs.empty()) {\n      return util::strieq(common_name.c_str(), hostname) ? 0 : -1;\n    }\n    const void *saddr;\n    switch(su->storage.ss_family) {\n    case AF_INET:\n      saddr = &su->in.sin_addr;\n      break;\n    case AF_INET6:\n      saddr = &su->in6.sin6_addr;\n      break;\n    default:\n      return -1;\n    }\n    for(size_t i = 0; i < ip_addrs.size(); ++i) {\n      if(salen == ip_addrs[i].size() &&\n         memcmp(saddr, ip_addrs[i].c_str(), salen) == 0) {\n        return 0;\n      }\n    }\n  } else {\n    if(dns_names.empty()) {\n      return tls_hostname_match(common_name.c_str(), hostname) ? 0 : -1;\n    }\n    for(size_t i = 0; i < dns_names.size(); ++i) {\n      if(tls_hostname_match(dns_names[i].c_str(), hostname)) {\n        return 0;\n      }\n    }\n  }\n  return -1;\n}\n} // namespace\n\nvoid get_altnames(X509 *cert,\n                  std::vector<std::string>& dns_names,\n                  std::vector<std::string>& ip_addrs,\n                  std::string& common_name)\n{\n  GENERAL_NAMES* altnames;\n  altnames = static_cast<GENERAL_NAMES*>\n    (X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0));\n  if(altnames) {\n    util::auto_delete<GENERAL_NAMES*> altnames_deleter(altnames,\n                                                       GENERAL_NAMES_free);\n    size_t n = sk_GENERAL_NAME_num(altnames);\n    for(size_t i = 0; i < n; ++i) {\n      const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i);\n      if(altname->type == GEN_DNS) {\n        const unsigned char *name = ASN1_STRING_get0_data(altname->d.ia5);\n        if(!name) {\n          continue;\n        }\n        size_t len = ASN1_STRING_length(altname->d.ia5);\n        if(std::find(name, name+len, '\\0') != name+len) {\n          // Embedded NULL is not permitted.\n          continue;\n        }\n        dns_names.push_back(std::string(name, name+len));\n      } else if(altname->type == GEN_IPADD) {\n        const unsigned char *ip_addr = altname->d.iPAddress->data;\n        if(!ip_addr) {\n          continue;\n        }\n        size_t len = altname->d.iPAddress->length;\n        ip_addrs.push_back(std::string(reinterpret_cast<const char*>(ip_addr),\n                                      len));\n      }\n    }\n  }\n  X509_NAME *subjectname = X509_get_subject_name(cert);\n  if(!subjectname) {\n    LOG(WARNING) << \"Could not get X509 name object from the certificate.\";\n    return;\n  }\n  int lastpos = -1;\n  while(1) {\n    lastpos = X509_NAME_get_index_by_NID(subjectname, NID_commonName,\n                                         lastpos);\n    if(lastpos == -1) {\n      break;\n    }\n    X509_NAME_ENTRY *entry = X509_NAME_get_entry(subjectname, lastpos);\n    unsigned char *out;\n    int outlen = ASN1_STRING_to_UTF8(&out, X509_NAME_ENTRY_get_data(entry));\n    if(outlen < 0) {\n      continue;\n    }\n    if(std::find(out, out+outlen, '\\0') != out+outlen) {\n      // Embedded NULL is not permitted.\n      continue;\n    }\n    common_name.assign(&out[0], &out[outlen]);\n    OPENSSL_free(out);\n    break;\n  }\n}\n\nint check_cert(SSL *ssl)\n{\n  X509 *cert = SSL_get_peer_certificate(ssl);\n  if(!cert) {\n    LOG(ERROR) << \"No certificate found\";\n    return -1;\n  }\n  util::auto_delete<X509*> cert_deleter(cert, X509_free);\n  long verify_res = SSL_get_verify_result(ssl);\n  if(verify_res != X509_V_OK) {\n    LOG(ERROR) << \"Certificate verification failed: \"\n               << X509_verify_cert_error_string(verify_res);\n    return -1;\n  }\n  std::string common_name;\n  std::vector<std::string> dns_names;\n  std::vector<std::string> ip_addrs;\n  get_altnames(cert, dns_names, ip_addrs, common_name);\n  if(verify_hostname(get_config()->downstream_host,\n                     &get_config()->downstream_addr,\n                     get_config()->downstream_addrlen,\n                     dns_names, ip_addrs, common_name) != 0) {\n    LOG(ERROR) << \"Certificate verification failed: hostname does not match\";\n    return -1;\n  }\n  return 0;\n}\n\n#if !OPENSSL_1_1_API\nnamespace {\npthread_mutex_t *ssl_locks;\n} // namespace\n\nnamespace {\nvoid ssl_locking_cb(int mode, int type, const char *file, int line)\n{\n  if(mode & CRYPTO_LOCK) {\n    pthread_mutex_lock(&(ssl_locks[type]));\n  } else {\n    pthread_mutex_unlock(&(ssl_locks[type]));\n  }\n}\n} // namespace\n#endif // !OPENSSL_1_1_API\n\nvoid setup_ssl_lock()\n{\n#if !OPENSSL_1_1_API\n  ssl_locks = new pthread_mutex_t[CRYPTO_num_locks()];\n  for(int i = 0; i < CRYPTO_num_locks(); ++i) {\n    // Always returns 0\n    pthread_mutex_init(&(ssl_locks[i]), 0);\n  }\n  //CRYPTO_set_id_callback(ssl_thread_id); OpenSSL manual says that if\n  // threadid_func is not specified using\n  // CRYPTO_THREADID_set_callback(), then default implementation is\n  // used. We use this default one.\n  CRYPTO_set_locking_callback(ssl_locking_cb);\n#endif // !OPENSSL_1_1_API\n}\n\nvoid teardown_ssl_lock()\n{\n#if !OPENSSL_1_1_API\n  for(int i = 0; i < CRYPTO_num_locks(); ++i) {\n    pthread_mutex_destroy(&(ssl_locks[i]));\n  }\n  delete [] ssl_locks;\n#endif // !OPENSSL_1_1_API\n}\n\nCertLookupTree* cert_lookup_tree_new()\n{\n  CertLookupTree *tree = new CertLookupTree();\n  CertNode *root = new CertNode();\n  root->ssl_ctx = 0;\n  root->str = 0;\n  root->first = root->last = 0;\n  tree->root = root;\n  return tree;\n}\n\nnamespace {\nvoid cert_node_del(CertNode *node)\n{\n  for(std::vector<CertNode*>::iterator i = node->next.begin(),\n        eoi = node->next.end(); i != eoi; ++i) {\n    cert_node_del(*i);\n  }\n  delete node;\n}\n} // namespace\n\nvoid cert_lookup_tree_del(CertLookupTree *lt)\n{\n  cert_node_del(lt->root);\n  for(std::vector<char*>::iterator i = lt->hosts.begin(),\n        eoi = lt->hosts.end(); i != eoi; ++i) {\n    delete [] *i;\n  }\n  delete lt;\n}\n\nnamespace {\n// The |offset| is the index in the hostname we are examining.  We are\n// going to scan from |offset| in backwards.\nvoid cert_lookup_tree_add_cert(CertLookupTree *lt, CertNode *node,\n                               SSL_CTX *ssl_ctx,\n                               char *hostname, size_t len, int offset)\n{\n  int i, next_len = node->next.size();\n  char c = hostname[offset];\n  CertNode *cn = 0;\n  for(i = 0; i < next_len; ++i) {\n    cn = node->next[i];\n    if(cn->str[cn->first] == c) {\n      break;\n    }\n  }\n  if(i == next_len) {\n    if(c == '*') {\n      // We assume hostname as wildcard hostname when first '*' is\n      // encountered. Note that as per RFC 6125 (6.4.3), there are\n      // some restrictions for wildcard hostname. We just ignore\n      // these rules here but do the proper check when we do the\n      // match.\n      node->wildcard_certs.push_back(std::make_pair(hostname, ssl_ctx));\n    } else {\n      int j;\n      CertNode *new_node = new CertNode();\n      new_node->str = hostname;\n      new_node->first = offset;\n      // If wildcard is found, set the region before it because we\n      // don't include it in [first, last).\n      for(j = offset; j >= 0 && hostname[j] != '*'; --j);\n      new_node->last = j;\n      if(j == -1) {\n        new_node->ssl_ctx = ssl_ctx;\n      } else {\n        new_node->ssl_ctx = 0;\n        new_node->wildcard_certs.push_back(std::make_pair(hostname, ssl_ctx));\n      }\n      node->next.push_back(new_node);\n    }\n  } else {\n    int j;\n    for(i = cn->first, j = offset; i > cn->last && j >= 0 &&\n          cn->str[i] == hostname[j]; --i, --j);\n    if(i == cn->last) {\n      if(j == -1) {\n        if(cn->ssl_ctx) {\n          // same hostname, we don't overwrite exiting ssl_ctx\n        } else {\n          cn->ssl_ctx = ssl_ctx;\n        }\n      } else {\n        // The existing hostname is a suffix of this hostname.\n        // Continue matching at potion j.\n        cert_lookup_tree_add_cert(lt, cn, ssl_ctx, hostname, len, j);\n      }\n    } else {\n      CertNode *new_node = new CertNode();\n      new_node->ssl_ctx = cn->ssl_ctx;\n      new_node->str = cn->str;\n      new_node->first = i;\n      new_node->last = cn->last;\n      new_node->wildcard_certs.swap(cn->wildcard_certs);\n      new_node->next.swap(cn->next);\n\n      cn->next.push_back(new_node);\n\n      cn->last = i;\n      if(j == -1) {\n        // This hostname is a suffix of the existing hostname.\n        cn->ssl_ctx = ssl_ctx;\n      } else {\n        // This hostname and existing one share suffix.\n        cn->ssl_ctx = 0;\n        cert_lookup_tree_add_cert(lt, cn, ssl_ctx, hostname, len, j);\n      }\n    }\n  }\n}\n} // namespace\n\nvoid cert_lookup_tree_add_cert(CertLookupTree *lt, SSL_CTX *ssl_ctx,\n                               const char *hostname, size_t len)\n{\n  if(len == 0) {\n    return;\n  }\n  // Copy hostname including terminal NULL\n  char *host_copy = new char[len + 1];\n  for(size_t i = 0; i < len; ++i) {\n    host_copy[i] = util::lowcase(hostname[i]);\n  }\n  host_copy[len] = '\\0';\n  lt->hosts.push_back(host_copy);\n  cert_lookup_tree_add_cert(lt, lt->root, ssl_ctx, host_copy, len, len-1);\n}\n\nnamespace {\nSSL_CTX* cert_lookup_tree_lookup(CertLookupTree *lt, CertNode *node,\n                                 const char *hostname, size_t len, int offset)\n{\n  int i, j;\n  for(i = node->first, j = offset; i > node->last && j >= 0 &&\n        node->str[i] == util::lowcase(hostname[j]); --i, --j);\n  if(i == node->last) {\n    if(j == -1) {\n      if(node->ssl_ctx) {\n        // exact match\n        return node->ssl_ctx;\n      } else {\n        // Do not perform wildcard-match because '*' must match at least\n        // one character.\n        return 0;\n      }\n    } else {\n      for(std::vector<std::pair<char*, SSL_CTX*> >::iterator i =\n            node->wildcard_certs.begin(), eoi = node->wildcard_certs.end();\n          i != eoi; ++i) {\n        if(tls_hostname_match((*i).first, hostname)) {\n          return (*i).second;\n        }\n      }\n      char c = util::lowcase(hostname[j]);\n      for(std::vector<CertNode*>::iterator i = node->next.begin(),\n            eoi = node->next.end(); i != eoi; ++i) {\n        if((*i)->str[(*i)->first] == c) {\n          return cert_lookup_tree_lookup(lt, *i, hostname, len, j);\n        }\n      }\n      return 0;\n    }\n  } else {\n    return 0;\n  }\n}\n} // namespace\n\nSSL_CTX* cert_lookup_tree_lookup(CertLookupTree *lt,\n                                 const char *hostname, size_t len)\n{\n  return cert_lookup_tree_lookup(lt, lt->root, hostname, len, len-1);\n}\n\n\nint cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,\n                                        const char *certfile)\n{\n  BIO *bio = BIO_new(BIO_s_file());\n  if(!bio) {\n    LOG(ERROR) << \"BIO_new failed\";\n    return -1;\n  }\n  util::auto_delete<BIO*> bio_deleter(bio, BIO_vfree);\n  if(!BIO_read_filename(bio, certfile)) {\n    LOG(ERROR) << \"Could not read certificate file '\" << certfile << \"'\";\n    return -1;\n  }\n  X509 *cert = PEM_read_bio_X509(bio, 0, 0, 0);\n  if(!cert) {\n    LOG(ERROR) << \"Could not read X509 structure from file '\"\n               << certfile << \"'\";\n    return -1;\n  }\n  util::auto_delete<X509*> cert_deleter(cert, X509_free);\n  std::string common_name;\n  std::vector<std::string> dns_names;\n  std::vector<std::string> ip_addrs;\n  get_altnames(cert, dns_names, ip_addrs, common_name);\n  for(std::vector<std::string>::iterator i = dns_names.begin(),\n        eoi = dns_names.end(); i != eoi; ++i) {\n    cert_lookup_tree_add_cert(lt, ssl_ctx, (*i).c_str(), (*i).size());\n  }\n  cert_lookup_tree_add_cert(lt, ssl_ctx, common_name.c_str(),\n                            common_name.size());\n  return 0;\n}\n\n} // namespace ssl\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_ssl.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_SSL_H\n#define SHRPX_SSL_H\n\n#include \"shrpx.h\"\n\n#include <vector>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#include <event.h>\n\nnamespace shrpx {\n\nclass ClientHandler;\n\nnamespace ssl {\n\nSSL_CTX* create_ssl_context(const char *private_key_file,\n                            const char *cert_file);\n\nSSL_CTX* create_ssl_client_context();\n\nClientHandler* accept_connection\n(event_base *evbase,\n bufferevent_rate_limit_group *rate_limit_group,\n SSL_CTX *ssl_ctx,\n evutil_socket_t fd,\n sockaddr *addr, int addrlen);\n\nbool numeric_host(const char *hostname);\n\nint check_cert(SSL *ssl);\n\nvoid setup_ssl_lock();\n\nvoid teardown_ssl_lock();\n\n// Retrieves DNS and IP address in subjectAltNames and commonName from\n// the |cert|.\nvoid get_altnames(X509 *cert,\n                  std::vector<std::string>& dns_names,\n                  std::vector<std::string>& ip_addrs,\n                  std::string& common_name);\n\n// CertLookupTree forms lookup tree to get SSL_CTX whose DNS or\n// commonName matches hostname in query. The tree is patricia trie\n// data structure formed from the tail of the hostname pattern. Each\n// CertNode contains part of hostname str member in range [first,\n// last) member and the next member contains the following CertNode\n// pointers ('following' means character before the current one). The\n// CertNode where a hostname pattern ends contains its SSL_CTX pointer\n// in the ssl_ctx member.  For wildcard hostname pattern, we store the\n// its pattern and SSL_CTX in CertNode one before first \"*\" found from\n// the tail.\n//\n// When querying SSL_CTX with particular hostname, we match from its\n// tail in our lookup tree. If the query goes to the first character\n// of the hostname and current CertNode has non-NULL ssl_ctx member,\n// then it is the exact match. The ssl_ctx member is returned.  Along\n// the way, if CertNode which contains non-empty wildcard_certs member\n// is encountered, wildcard hostname matching is performed against\n// them. If there is a match, its SSL_CTX is returned. If none\n// matches, query is continued to the next character.\n\nstruct CertNode {\n  // SSL_CTX for exact match\n  SSL_CTX *ssl_ctx;\n  // list of wildcard domain name and its SSL_CTX pair, the wildcard\n  // '*' appears in this position.\n  std::vector<std::pair<char*, SSL_CTX*> > wildcard_certs;\n  // Next CertNode index of CertLookupTree::nodes\n  std::vector<CertNode*> next;\n  char *str;\n  // [first, last) in the reverse direction in str, first >=\n  // last. This indices only work for str member.\n  int first, last;\n};\n\nstruct CertLookupTree {\n  std::vector<SSL_CTX*> certs;\n  std::vector<char*> hosts;\n  CertNode *root;\n};\n\nCertLookupTree* cert_lookup_tree_new();\nvoid cert_lookup_tree_del(CertLookupTree *lt);\n\n// Adds |ssl_ctx| with hostname pattern |hostname| with length |len|\n// to the lookup tree |lt|. The |hostname| must be NULL-terminated.\nvoid cert_lookup_tree_add_cert(CertLookupTree *lt, SSL_CTX *ssl_ctx,\n                               const char *hostname, size_t len);\n\n// Looks up SSL_CTX using the given |hostname| with length |len|. If\n// more than one SSL_CTX which matches the query, it is undefined\n// which one is returned. The |hostname| must be NULL-terminated. If\n// no matching SSL_CTX found, returns NULL.\nSSL_CTX* cert_lookup_tree_lookup(CertLookupTree *lt, const char *hostname,\n                                 size_t len);\n\n// Adds |ssl_ctx| to lookup tree |lt| using hostnames read from\n// |certfile|. The subjectAltNames and commonName are considered as\n// eligible hostname. This function returns 0 if it succeeds, or -1.\n// Even if no ssl_ctx is added to tree, this function returns 0.\nint cert_lookup_tree_add_cert_from_file(CertLookupTree *lt, SSL_CTX *ssl_ctx,\n                                        const char *certfile);\n\n} // namespace ssl\n\n} // namespace shrpx\n\n#endif // SHRPX_SSL_H\n"
  },
  {
    "path": "src/shrpx_ssl_test.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_ssl_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include \"shrpx_ssl.h\"\n\nnamespace shrpx {\n\nvoid test_shrpx_ssl_create_lookup_tree(void)\n{\n  ssl::CertLookupTree* tree = ssl::cert_lookup_tree_new();\n  SSL_CTX *ctxs[] = {SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method()),\n                     SSL_CTX_new(SSLv23_method())};\n\n  const char *hostnames[] = { \"example.com\",\n                              \"www.example.org\",\n                              \"*www.example.org\",\n                              \"x*.host.domain\",\n                              \"*yy.host.domain\",\n                              \"spdylay.sourceforge.net\",\n                              \"sourceforge.net\",\n                              \"sourceforge.net\", // duplicate\n                              \"*.foo.bar\", // oo.bar is suffix of *.foo.bar\n                              \"oo.bar\"\n  };\n  int num = sizeof(ctxs)/sizeof(ctxs[0]);\n  for(int i = 0; i < num; ++i) {\n    ssl::cert_lookup_tree_add_cert(tree, ctxs[i], hostnames[i],\n                                   strlen(hostnames[i]));\n  }\n\n  CU_ASSERT(ctxs[0] == ssl::cert_lookup_tree_lookup(tree, hostnames[0],\n                                                    strlen(hostnames[0])));\n  CU_ASSERT(ctxs[1] == ssl::cert_lookup_tree_lookup(tree, hostnames[1],\n                                                    strlen(hostnames[1])));\n  const char h1[] = \"2www.example.org\";\n  CU_ASSERT(ctxs[2] == ssl::cert_lookup_tree_lookup(tree, h1, strlen(h1)));\n  const char h2[] = \"www2.example.org\";\n  CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h2, strlen(h2)));\n  const char h3[] = \"x1.host.domain\";\n  CU_ASSERT(ctxs[3] == ssl::cert_lookup_tree_lookup(tree, h3, strlen(h3)));\n  // Does not match *yy.host.domain, because * must match at least 1\n  // character.\n  const char h4[] = \"yy.Host.domain\";\n  CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h4, strlen(h4)));\n  const char h5[] = \"zyy.host.domain\";\n  CU_ASSERT(ctxs[4] == ssl::cert_lookup_tree_lookup(tree, h5, strlen(h5)));\n  CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, \"\", 0));\n  CU_ASSERT(ctxs[5] == ssl::cert_lookup_tree_lookup(tree, hostnames[5],\n                                                    strlen(hostnames[5])));\n  CU_ASSERT(ctxs[6] == ssl::cert_lookup_tree_lookup(tree, hostnames[6],\n                                                    strlen(hostnames[6])));\n  const char h6[] = \"pdylay.sourceforge.net\";\n  for(int i = 0; i < 7; ++i) {\n    CU_ASSERT(0 == ssl::cert_lookup_tree_lookup(tree, h6 + i, strlen(h6) - i));\n  }\n  const char h7[] = \"x.foo.bar\";\n  CU_ASSERT(ctxs[8] == ssl::cert_lookup_tree_lookup(tree, h7, strlen(h7)));\n  CU_ASSERT(ctxs[9] == ssl::cert_lookup_tree_lookup(tree, hostnames[9],\n                                                    strlen(hostnames[9])));\n  ssl::cert_lookup_tree_del(tree);\n  for(int i = 0; i < num; ++i) {\n    SSL_CTX_free(ctxs[i]);\n  }\n\n  SSL_CTX *ctxs2[] = {SSL_CTX_new(SSLv23_method()),\n                      SSL_CTX_new(SSLv23_method()),\n                      SSL_CTX_new(SSLv23_method()),\n                      SSL_CTX_new(SSLv23_method())};\n  const char *names[] = { \"rab\", \"zab\", \"zzub\", \"ab\" };\n  num = sizeof(ctxs2)/sizeof(ctxs2[0]);\n  tree = ssl::cert_lookup_tree_new();\n  for(int i = 0; i < num; ++i) {\n    ssl::cert_lookup_tree_add_cert(tree, ctxs2[i], names[i], strlen(names[i]));\n  }\n  for(int i = 0; i < num; ++i) {\n    CU_ASSERT(ctxs2[i] == ssl::cert_lookup_tree_lookup(tree, names[i],\n                                                       strlen(names[i])));\n  }\n  ssl::cert_lookup_tree_del(tree);\n  for(int i = 0; i < num; ++i) {\n    SSL_CTX_free(ctxs2[i]);\n  }\n}\n\nvoid test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void)\n{\n  int rv;\n  ssl::CertLookupTree* tree = ssl::cert_lookup_tree_new();\n  SSL_CTX *ssl_ctx = SSL_CTX_new(SSLv23_method());\n  const char certfile[] = SPDYLAY_TESTS_DIR\"/testdata/cacert.pem\";\n  rv = ssl::cert_lookup_tree_add_cert_from_file(tree, ssl_ctx, certfile);\n  CU_ASSERT(0 == rv);\n  const char localhost[] = \"localhost\";\n  CU_ASSERT(ssl_ctx == ssl::cert_lookup_tree_lookup(tree, localhost,\n                                                    sizeof(localhost)-1));\n  ssl::cert_lookup_tree_del(tree);\n  SSL_CTX_free(ssl_ctx);\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_ssl_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_SSL_TEST_H\n#define SHRPX_SSL_TEST_H\n\nnamespace shrpx {\n\nvoid test_shrpx_ssl_create_lookup_tree(void);\nvoid test_shrpx_ssl_cert_lookup_tree_add_cert_from_file(void);\n\n} // namespace shrpx\n\n#endif /* SHRPX_SSL_TEST_H */\n"
  },
  {
    "path": "src/shrpx_thread_event_receiver.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_thread_event_receiver.h\"\n\n#include <unistd.h>\n\n#include \"shrpx_ssl.h\"\n#include \"shrpx_log.h\"\n#include \"shrpx_client_handler.h\"\n#include \"shrpx_spdy_session.h\"\n\nnamespace shrpx {\n\nThreadEventReceiver::ThreadEventReceiver(event_base *evbase,\n                                         SSL_CTX *ssl_ctx,\n                                         SpdySession *spdy)\n  : evbase_(evbase),\n    ssl_ctx_(ssl_ctx),\n    spdy_(spdy),\n    rate_limit_group_(bufferevent_rate_limit_group_new\n                      (evbase_, get_config()->worker_rate_limit_cfg))\n{}\n\nThreadEventReceiver::~ThreadEventReceiver()\n{\n  bufferevent_rate_limit_group_free(rate_limit_group_);\n}\n\nvoid ThreadEventReceiver::on_read(bufferevent *bev)\n{\n  evbuffer *input = bufferevent_get_input(bev);\n  while(evbuffer_get_length(input) >= sizeof(WorkerEvent)) {\n    WorkerEvent wev;\n    int nread = evbuffer_remove(input, &wev, sizeof(wev));\n    if(nread != sizeof(wev)) {\n      TLOG(FATAL, this) << \"evbuffer_remove() removed fewer bytes. Expected:\"\n                        << sizeof(wev) << \" Actual:\" << nread;\n      continue;\n    }\n    if(LOG_ENABLED(INFO)) {\n      TLOG(INFO, this) << \"WorkerEvent: client_fd=\" << wev.client_fd\n                       << \", addrlen=\" << wev.client_addrlen;\n    }\n    event_base *evbase = bufferevent_get_base(bev);\n    ClientHandler *client_handler;\n    client_handler = ssl::accept_connection(evbase, rate_limit_group_,\n                                            ssl_ctx_,\n                                            wev.client_fd,\n                                            &wev.client_addr.sa,\n                                            wev.client_addrlen);\n    if(client_handler) {\n      client_handler->set_spdy_session(spdy_);\n      if(LOG_ENABLED(INFO)) {\n        TLOG(INFO, this) << \"CLIENT_HANDLER:\" << client_handler << \" created\";\n      }\n    } else {\n      if(LOG_ENABLED(INFO)) {\n        TLOG(ERROR, this) << \"ClientHandler creation failed\";\n      }\n      close(wev.client_fd);\n    }\n  }\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_thread_event_receiver.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_THREAD_EVENT_RECEIVER_H\n#define SHRPX_THREAD_EVENT_RECEIVER_H\n\n#include \"shrpx.h\"\n\n#include <openssl/ssl.h>\n\n#include <event2/bufferevent.h>\n\n#include \"shrpx_config.h\"\n\nnamespace shrpx {\n\nclass SpdySession;\n\nstruct WorkerEvent {\n  sockaddr_union client_addr;\n  size_t client_addrlen;\n  evutil_socket_t client_fd;\n};\n\nclass ThreadEventReceiver {\npublic:\n  ThreadEventReceiver(event_base *evbase, SSL_CTX *ssl_ctx,\n                      SpdySession *spdy);\n  ~ThreadEventReceiver();\n  void on_read(bufferevent *bev);\nprivate:\n  event_base *evbase_;\n  SSL_CTX *ssl_ctx_;\n  // Shared SPDY session for each thread. NULL if not client mode. Not\n  // deleted by this object.\n  SpdySession *spdy_;\n  bufferevent_rate_limit_group *rate_limit_group_;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_THREAD_EVENT_RECEIVER_H\n"
  },
  {
    "path": "src/shrpx_upstream.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_UPSTREAM_H\n#define SHRPX_UPSTREAM_H\n\n#include \"shrpx.h\"\n\n#include <event2/bufferevent.h>\n\n#include \"shrpx_io_control.h\"\n\nnamespace shrpx {\n\nclass ClientHandler;\nclass Downstream;\n\nclass Upstream {\npublic:\n  virtual ~Upstream() {}\n  virtual int on_read() = 0;\n  virtual int on_write() = 0;\n  virtual int on_event() = 0;\n  virtual bufferevent_data_cb get_downstream_readcb() = 0;\n  virtual bufferevent_data_cb get_downstream_writecb() = 0;\n  virtual bufferevent_event_cb get_downstream_eventcb() = 0;\n  virtual ClientHandler* get_client_handler() const = 0;\n\n  virtual int on_downstream_header_complete(Downstream *downstream) = 0;\n  virtual int on_downstream_body(Downstream *downstream,\n                                 const uint8_t *data, size_t len) = 0;\n  virtual int on_downstream_body_complete(Downstream *downstream) = 0;\n\n  virtual void pause_read(IOCtrlReason reason) = 0;\n  virtual int resume_read(IOCtrlReason reason, Downstream *downstream) = 0;\n};\n\n} // namespace shrpx\n\n#endif // SHRPX_UPSTREAM_H\n"
  },
  {
    "path": "src/shrpx_worker.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"shrpx_worker.h\"\n\n#include <unistd.h>\n#include <sys/socket.h>\n\n#include <event.h>\n#include <event2/bufferevent.h>\n\n#include \"shrpx_ssl.h\"\n#include \"shrpx_thread_event_receiver.h\"\n#include \"shrpx_log.h\"\n#include \"shrpx_spdy_session.h\"\n\nnamespace shrpx {\n\nWorker::Worker(WorkerInfo *info)\n  : sv_ssl_ctx_(info->sv_ssl_ctx),\n    cl_ssl_ctx_(info->cl_ssl_ctx),\n    fd_(info->sv[1])\n{}\n\nWorker::~Worker()\n{\n  shutdown(fd_, SHUT_WR);\n  close(fd_);\n}\n\nnamespace {\nvoid readcb(bufferevent *bev, void *arg)\n{\n  ThreadEventReceiver *receiver = static_cast<ThreadEventReceiver*>(arg);\n  receiver->on_read(bev);\n}\n} // namespace\n\nnamespace {\nvoid eventcb(bufferevent *bev, short events, void *arg)\n{\n  if(events & BEV_EVENT_EOF) {\n    LOG(ERROR) << \"Connection to main thread lost: eof\";\n  }\n  if(events & BEV_EVENT_ERROR) {\n    LOG(ERROR) << \"Connection to main thread lost: network error\";\n  }\n}\n} // namespace\n\nvoid Worker::run()\n{\n  event_base *evbase = event_base_new();\n  bufferevent *bev = bufferevent_socket_new(evbase, fd_,\n                                            BEV_OPT_DEFER_CALLBACKS);\n  SpdySession *spdy = 0;\n  if(get_config()->downstream_proto == PROTO_SPDY) {\n    spdy = new SpdySession(evbase, cl_ssl_ctx_);\n    if(spdy->init_notification() == -1) {\n      DIE();\n    }\n  }\n  ThreadEventReceiver *receiver = new ThreadEventReceiver(evbase, sv_ssl_ctx_,\n                                                          spdy);\n  bufferevent_enable(bev, EV_READ);\n  bufferevent_setcb(bev, readcb, 0, eventcb, receiver);\n\n  event_base_loop(evbase, 0);\n\n  delete receiver;\n}\n\nvoid* start_threaded_worker(void *arg)\n{\n  WorkerInfo *info = static_cast<WorkerInfo*>(arg);\n  Worker worker(info);\n  worker.run();\n  return 0;\n}\n\n} // namespace shrpx\n"
  },
  {
    "path": "src/shrpx_worker.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SHRPX_WORKER_H\n#define SHRPX_WORKER_H\n\n#include \"shrpx.h\"\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n\n#include \"shrpx_listen_handler.h\"\n\nnamespace shrpx {\n\nclass Worker {\npublic:\n  Worker(WorkerInfo *info);\n  ~Worker();\n  void run();\nprivate:\n  SSL_CTX *sv_ssl_ctx_;\n  SSL_CTX *cl_ssl_ctx_;\n  // Channel to the main thread\n  int fd_;\n};\n\nvoid* start_threaded_worker(void *arg);\n\n} // namespace shrpx\n\n#endif // SHRPX_WORKER_H\n"
  },
  {
    "path": "src/spdycat.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_config.h\"\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <signal.h>\n#include <getopt.h>\n#include <poll.h>\n\n#include <cassert>\n#include <cstdio>\n#include <cerrno>\n#include <cstdlib>\n#include <cstring>\n#include <string>\n#include <iostream>\n#include <string>\n#include <set>\n#include <iomanip>\n#include <fstream>\n#include <map>\n#include <vector>\n#include <sstream>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <spdylay/spdylay.h>\n\n#include \"http-parser/http_parser.h\"\n\n#include \"spdylay_ssl.h\"\n#include \"HtmlParser.h\"\n#include \"util.h\"\n\n#ifndef O_BINARY\n# define O_BINARY (0)\n#endif // O_BINARY\n\nnamespace spdylay {\n\nstruct Config {\n  std::vector<std::pair<std::string, std::string> > headers;\n  std::string certfile;\n  std::string keyfile;\n  std::string datafile;\n  std::string proxy_host;\n  int proxy_port;\n  int multiply;\n  int spdy_version;\n  // milliseconds\n  int timeout;\n  int window_bits;\n  bool null_out;\n  bool remote_name;\n  bool verbose;\n  bool get_assets;\n  bool stat;\n  bool no_tls;\n  Config()\n    : proxy_port(0),\n      multiply(1),\n      spdy_version(-1),\n      timeout(-1),\n      window_bits(-1),\n      null_out(false),\n      remote_name(false),\n      verbose(false),\n      get_assets(false),\n      stat(false),\n      no_tls(false)\n  {}\n};\n\nstruct RequestStat {\n  timeval on_syn_stream_time;\n  timeval on_syn_reply_time;\n  timeval on_complete_time;\n  RequestStat()\n  {\n    on_syn_stream_time.tv_sec = -1;\n    on_syn_stream_time.tv_usec = -1;\n    on_syn_reply_time.tv_sec = -1;\n    on_syn_reply_time.tv_usec = -1;\n    on_complete_time.tv_sec = -1;\n    on_complete_time.tv_usec = -1;\n  }\n};\n\nvoid record_time(timeval *tv)\n{\n  get_time(tv);\n}\n\nbool has_uri_field(const http_parser_url &u, http_parser_url_fields field)\n{\n  return u.field_set & (1 << field);\n}\n\nbool fieldeq(const char *uri1, const http_parser_url &u1,\n             const char *uri2, const http_parser_url &u2,\n             http_parser_url_fields field)\n{\n  if(!has_uri_field(u1, field)) {\n    if(!has_uri_field(u2, field)) {\n      return true;\n    } else {\n      return false;\n    }\n  } else if(!has_uri_field(u2, field)) {\n    return false;\n  }\n  if(u1.field_data[field].len != u2.field_data[field].len) {\n    return false;\n  }\n  return memcmp(uri1+u1.field_data[field].off,\n                uri2+u2.field_data[field].off,\n                u1.field_data[field].len) == 0;\n}\n\nbool fieldeq(const char *uri, const http_parser_url &u,\n             http_parser_url_fields field,\n             const char *t)\n{\n  if(!has_uri_field(u, field)) {\n    if(!t[0]) {\n      return true;\n    } else {\n      return false;\n    }\n  } else if(!t[0]) {\n    return false;\n  }\n  int i, len = u.field_data[field].len;\n  const char *p = uri+u.field_data[field].off;\n  for(i = 0; i < len && t[i] && p[i] == t[i]; ++i);\n  return i == len && !t[i];\n}\n\nuint16_t get_default_port(const char *uri, const http_parser_url &u)\n{\n  if(fieldeq(uri, u, UF_SCHEMA, \"https\")) {\n    return 443;\n  } else if(fieldeq(uri, u, UF_SCHEMA, \"http\")) {\n    return 80;\n  } else {\n    return 443;\n  }\n}\n\nstd::string get_uri_field(const char *uri, const http_parser_url &u,\n                          http_parser_url_fields field)\n{\n  if(has_uri_field(u, field)) {\n    return std::string(uri+u.field_data[field].off,\n                       u.field_data[field].len);\n  } else {\n    return \"\";\n  }\n}\n\nbool porteq(const char *uri1, const http_parser_url &u1,\n            const char *uri2, const http_parser_url &u2)\n{\n  uint16_t port1, port2;\n  port1 = has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1);\n  port2 = has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2);\n  return port1 == port2;\n}\n\nvoid write_uri_field(std::ostream& o,\n                     const char *uri, const http_parser_url &u,\n                     http_parser_url_fields field)\n{\n  if(has_uri_field(u, field)) {\n    o.write(uri+u.field_data[field].off, u.field_data[field].len);\n  }\n}\n\nstd::string strip_fragment(const char *raw_uri)\n{\n  const char *end;\n  for(end = raw_uri; *end && *end != '#'; ++end);\n  size_t len = end-raw_uri;\n  return std::string(raw_uri, len);\n}\n\nstruct Request {\n  http_parser_url u;\n  RequestStat stat;\n  // URI without fragment\n  std::string uri;\n  std::string status;\n  spdylay_gzip *inflater;\n  HtmlParser *html_parser;\n  const spdylay_data_provider *data_prd;\n  int64_t data_length;\n  int64_t data_offset;\n  // Recursion level: 0: first entity, 1: entity linked from first entity\n  int level;\n  Request(const std::string& uri, const http_parser_url &u,\n          const spdylay_data_provider *data_prd, int64_t data_length,\n          int level = 0)\n    : u(u), uri(uri),\n      inflater(0), html_parser(0), data_prd(data_prd),\n      data_length(data_length),data_offset(0),\n      level(level)\n  {}\n\n  ~Request()\n  {\n    spdylay_gzip_inflate_del(inflater);\n    delete html_parser;\n  }\n\n  void init_inflater()\n  {\n    int rv;\n    rv = spdylay_gzip_inflate_new(&inflater);\n    assert(rv == 0);\n  }\n\n  void init_html_parser()\n  {\n    html_parser = new HtmlParser(uri);\n  }\n\n  int update_html_parser(const uint8_t *data, size_t len, int fin)\n  {\n    if(!html_parser) {\n      return 0;\n    }\n    int rv;\n    rv = html_parser->parse_chunk(reinterpret_cast<const char*>(data), len,\n                                  fin);\n    return rv;\n  }\n\n  std::string make_reqpath() const\n  {\n    std::string path = has_uri_field(u, UF_PATH) ?\n      get_uri_field(uri.c_str(), u, UF_PATH) : \"/\";\n    if(has_uri_field(u, UF_QUERY)) {\n      path += \"?\";\n      path.append(uri.c_str()+u.field_data[UF_QUERY].off,\n                  u.field_data[UF_QUERY].len);\n    }\n    return path;\n  }\n\n  bool is_ipv6_literal_addr() const\n  {\n    if(has_uri_field(u, UF_HOST)) {\n      return memchr(uri.c_str()+u.field_data[UF_HOST].off, ':',\n                    u.field_data[UF_HOST].len);\n    } else {\n      return false;\n    }\n  }\n\n  void record_syn_stream_time()\n  {\n    record_time(&stat.on_syn_stream_time);\n  }\n\n  void record_syn_reply_time()\n  {\n    record_time(&stat.on_syn_reply_time);\n  }\n\n  void record_complete_time()\n  {\n    record_time(&stat.on_complete_time);\n  }\n};\n\nstruct SessionStat {\n  timeval on_handshake_time;\n  SessionStat()\n  {\n    on_handshake_time.tv_sec = -1;\n    on_handshake_time.tv_usec = -1;\n  }\n};\n\nConfig config;\n\nstruct SpdySession {\n  std::vector<Request*> reqvec;\n  // Map from stream ID to Request object.\n  std::map<int32_t, Request*> streams;\n  // Insert path already added in reqvec to prevent multiple request\n  // for 1 resource.\n  std::set<std::string> path_cache;\n  // The number of completed requests, including failed ones.\n  size_t complete;\n  std::string hostport;\n  Spdylay *sc;\n  SessionStat stat;\n  SpdySession():complete(0), sc(0) {}\n  ~SpdySession()\n  {\n    for(size_t i = 0; i < reqvec.size(); ++i) {\n      delete reqvec[i];\n    }\n  }\n  bool all_requests_processed() const\n  {\n    return complete == reqvec.size();\n  }\n  void update_hostport()\n  {\n    if(reqvec.empty()) {\n      return;\n    }\n    std::stringstream ss;\n    if(reqvec[0]->is_ipv6_literal_addr()) {\n      ss << \"[\";\n      write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);\n      ss << \"]\";\n    } else {\n      write_uri_field(ss, reqvec[0]->uri.c_str(), reqvec[0]->u, UF_HOST);\n    }\n    if(has_uri_field(reqvec[0]->u, UF_PORT) &&\n       reqvec[0]->u.port != get_default_port(reqvec[0]->uri.c_str(),\n                                             reqvec[0]->u)) {\n      ss << \":\" << reqvec[0]->u.port;\n    }\n    hostport = ss.str();\n  }\n  bool add_request(const std::string& uri, const http_parser_url& u,\n                   const spdylay_data_provider *data_prd,\n                   int64_t data_length,\n                   int level = 0)\n  {\n    if(path_cache.count(uri)) {\n      return false;\n    } else {\n      if(config.multiply == 1) {\n        path_cache.insert(uri);\n      }\n      reqvec.push_back(new Request(uri, u, data_prd, data_length, level));\n      return true;\n    }\n  }\n  void record_handshake_time()\n  {\n    record_time(&stat.on_handshake_time);\n  }\n};\n\nextern bool ssl_debug;\n\nvoid submit_request\n(Spdylay& sc, const std::string& hostport,\n const std::vector<std::pair<std::string, std::string> >& headers,\n Request* req,\n const std::string& proxy_host, int proxy_port)\n{\n  std::string path = req->make_reqpath();\n  int r = sc.submit_request(get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA),\n                            hostport, path, headers, 3, req->data_prd,\n                            req->data_length, req, !proxy_host.empty(), proxy_host, proxy_port);\n  assert(r == 0);\n}\n\nvoid update_html_parser(SpdySession *spdySession, Request *req,\n                        const uint8_t *data, size_t len, int fin)\n{\n  if(!req->html_parser) {\n    return;\n  }\n  req->update_html_parser(data, len, fin);\n\n  for(size_t i = 0; i < req->html_parser->get_links().size(); ++i) {\n    const std::string& raw_uri = req->html_parser->get_links()[i];\n    std::string uri = strip_fragment(raw_uri.c_str());\n    http_parser_url u;\n    if(http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&\n       fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_SCHEMA) &&\n       fieldeq(uri.c_str(), u, req->uri.c_str(), req->u, UF_HOST) &&\n       porteq(uri.c_str(), u, req->uri.c_str(), req->u)) {\n      // No POST data for assets\n      if ( spdySession->add_request(uri, u, 0, 0, req->level+1) ) {\n        submit_request(*spdySession->sc, spdySession->hostport, config.headers,\n                       spdySession->reqvec.back(), config.proxy_host, config.proxy_port);\n      }\n    }\n  }\n  req->html_parser->clear_links();\n}\n\nSpdySession* get_session(void *user_data)\n{\n  return static_cast<SpdySession*>\n    (static_cast<Spdylay*>(user_data)->user_data());\n}\n\nvoid on_data_chunk_recv_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id,\n const uint8_t *data, size_t len, void *user_data)\n{\n  SpdySession *spdySession = get_session(user_data);\n  std::map<int32_t, Request*>::iterator itr =\n    spdySession->streams.find(stream_id);\n  if(itr != spdySession->streams.end()) {\n    Request *req = (*itr).second;\n    if(req->inflater) {\n      while(len > 0) {\n        const size_t MAX_OUTLEN = 4096;\n        uint8_t out[MAX_OUTLEN];\n        size_t outlen = MAX_OUTLEN;\n        size_t tlen = len;\n        int rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen);\n        if(rv != 0) {\n          spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);\n          break;\n        }\n        if(!config.null_out) {\n          std::cout.write(reinterpret_cast<const char*>(out), outlen);\n        }\n        update_html_parser(spdySession, req, out, outlen, 0);\n        data += tlen;\n        len -= tlen;\n      }\n    } else {\n      if(!config.null_out) {\n        std::cout.write(reinterpret_cast<const char*>(data), len);\n      }\n      update_html_parser(spdySession, req, data, len, 0);\n    }\n  }\n}\n\nvoid check_stream_id(spdylay_session *session,\n                     spdylay_frame_type type, spdylay_frame *frame,\n                     void *user_data)\n{\n  SpdySession *spdySession = get_session(user_data);\n  int32_t stream_id = frame->syn_stream.stream_id;\n  Request *req = (Request*)spdylay_session_get_stream_user_data(session,\n                                                                stream_id);\n  assert(req);\n  spdySession->streams[stream_id] = req;\n  req->record_syn_stream_time();\n}\n\nvoid before_ctrl_send_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  if(type == SPDYLAY_SYN_STREAM) {\n    check_stream_id(session, type, frame, user_data);\n  }\n}\n\nvoid on_ctrl_send_callback2\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  if(config.verbose) {\n    on_ctrl_send_callback(session, type, frame, user_data);\n  }\n}\n\nvoid check_response_header\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  char **nv;\n  int32_t stream_id;\n  if(type == SPDYLAY_SYN_REPLY) {\n    nv = frame->syn_reply.nv;\n    stream_id = frame->syn_reply.stream_id;\n  } else if(type == SPDYLAY_HEADERS) {\n    nv = frame->headers.nv;\n    stream_id = frame->headers.stream_id;\n  } else {\n    return;\n  }\n  Request *req = (Request*)spdylay_session_get_stream_user_data(session,\n                                                                stream_id);\n  if(!req) {\n    // Server-pushed stream does not have stream user data\n    return;\n  }\n  bool gzip = false;\n  for(size_t i = 0; nv[i]; i += 2) {\n    if(strcmp(\"content-encoding\", nv[i]) == 0) {\n      gzip = util::strieq(\"gzip\", nv[i+1]) || util::strieq(\"deflate\", nv[i+1]);\n    } else if(strcmp(\":status\", nv[i]) == 0) {\n      req->status = nv[i+1];\n    }\n  }\n  if(gzip) {\n    if(!req->inflater) {\n      req->init_inflater();\n    }\n  }\n  if(config.get_assets && req->level == 0) {\n    if(!req->html_parser) {\n      req->init_html_parser();\n    }\n  }\n}\n\nvoid on_ctrl_recv_callback2\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  if(type == SPDYLAY_SYN_REPLY) {\n    Request *req = (Request*)spdylay_session_get_stream_user_data\n      (session, frame->syn_reply.stream_id);\n    assert(req);\n    req->record_syn_reply_time();\n  }\n  check_response_header(session, type, frame, user_data);\n  if(config.verbose) {\n    on_ctrl_recv_callback(session, type, frame, user_data);\n  }\n}\n\nvoid on_stream_close_callback\n(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,\n void *user_data)\n{\n  SpdySession *spdySession = get_session(user_data);\n  std::map<int32_t, Request*>::iterator itr =\n    spdySession->streams.find(stream_id);\n  if(itr != spdySession->streams.end()) {\n    update_html_parser(spdySession, (*itr).second, 0, 0, 1);\n    (*itr).second->record_complete_time();\n    ++spdySession->complete;\n    if(spdySession->all_requests_processed()) {\n      spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);\n    }\n  }\n}\n\nvoid print_stats(const SpdySession& spdySession)\n{\n  std::cout << \"***** Statistics *****\" << std::endl;\n  for(size_t i = 0; i < spdySession.reqvec.size(); ++i) {\n    const Request *req = spdySession.reqvec[i];\n    std::cout << \"#\" << i+1 << \": \" << req->uri << std::endl;\n    std::cout << \"    Status: \" << req->status << std::endl;\n    std::cout << \"    Delta (ms) from SSL/TLS handshake(SYN_STREAM):\"\n              << std::endl;\n    if(req->stat.on_syn_reply_time.tv_sec >= 0) {\n      std::cout << \"        SYN_REPLY: \"\n                << time_delta(req->stat.on_syn_reply_time,\n                              spdySession.stat.on_handshake_time)\n                << \"(\"\n                << time_delta(req->stat.on_syn_reply_time,\n                              req->stat.on_syn_stream_time)\n                << \")\"\n                << std::endl;\n    }\n    if(req->stat.on_complete_time.tv_sec >= 0) {\n      std::cout << \"        Completed: \"\n                << time_delta(req->stat.on_complete_time,\n                              spdySession.stat.on_handshake_time)\n                << \"(\"\n                << time_delta(req->stat.on_complete_time,\n                              req->stat.on_syn_stream_time)\n                << \")\"\n                << std::endl;\n    }\n    std::cout << std::endl;\n  }\n}\n\nint spdy_evloop(int fd, SSL *ssl, int spdy_version, SpdySession& spdySession,\n                const spdylay_session_callbacks *callbacks, int timeout)\n{\n  int result = 0;\n  Spdylay sc(fd, ssl, spdy_version, callbacks, &spdySession);\n  spdySession.sc = &sc;\n\n  nfds_t npollfds = 1;\n  pollfd pollfds[1];\n\n  if(spdy_version >= SPDYLAY_PROTO_SPDY3 && config.window_bits != -1) {\n    spdylay_settings_entry iv[1];\n    iv[0].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n    iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n    iv[0].value = 1 << config.window_bits;\n    int rv = sc.submit_settings(SPDYLAY_FLAG_SETTINGS_NONE, iv, 1);\n    assert(rv == 0);\n  }\n  for(int i = 0, n = spdySession.reqvec.size(); i < n; ++i) {\n    submit_request(sc, spdySession.hostport, config.headers,\n                   spdySession.reqvec[i], config.proxy_host, config.proxy_port);\n  }\n  pollfds[0].fd = fd;\n  ctl_poll(pollfds, &sc);\n\n  timeval tv1, tv2;\n  while(!sc.finish()) {\n    if(config.timeout != -1) {\n      get_time(&tv1);\n    }\n    int nfds = poll(pollfds, npollfds, timeout);\n    if(nfds == -1) {\n      perror(\"poll\");\n      result = -1;\n      break;\n    }\n    if(pollfds[0].revents & (POLLIN | POLLOUT)) {\n      int rv;\n\n      sc.clear_io_flags();\n\n      if((rv = sc.recv()) != 0 || (rv = sc.send()) != 0) {\n        if(rv != SPDYLAY_ERR_EOF || !spdySession.all_requests_processed()) {\n          std::cerr << \"Fatal: \" << spdylay_strerror(rv) << \"\\n\"\n                    << \"reqnum=\" << spdySession.reqvec.size()\n                    << \", completed=\" << spdySession.complete << std::endl;\n        }\n        result = -1;\n        break;\n      }\n    }\n    if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {\n      std::cerr << \"HUP\" << std::endl;\n      result = -1;\n      break;\n    }\n    if(config.timeout != -1) {\n      get_time(&tv2);\n      timeout -= time_delta(tv2, tv1);\n      if (timeout <= 0) {\n        std::cerr << \"Requests to \" << spdySession.hostport << \" timed out.\"\n                  << std::endl;\n        result = -1;\n        break;\n      }\n    }\n    ctl_poll(pollfds, &sc);\n  }\n  if(!spdySession.all_requests_processed()) {\n    std::cerr << \"Some requests were not processed. total=\"\n              << spdySession.reqvec.size()\n              << \", processed=\" << spdySession.complete << std::endl;\n  }\n  if(config.stat) {\n    print_stats(spdySession);\n  }\n  return result;\n}\n\nint communicate(const std::string& host, uint16_t port,\n                SpdySession& spdySession,\n                const spdylay_session_callbacks *callbacks)\n{\n  int result = 0;\n  int rv;\n  int spdy_version;\n  std::string next_proto;\n  int timeout = config.timeout;\n  SSL_CTX *ssl_ctx = 0;\n  SSL *ssl = 0;\n  int fd = nonblock_connect_to(host, port, timeout);\n  if(fd == -1) {\n    std::cerr << \"Could not connect to the host: \" << host << \":\" << port\n              << std::endl;\n    result = -1;\n    goto fin;\n  } else if(fd == -2) {\n    std::cerr << \"Request to \" << spdySession.hostport << \" timed out \"\n              << \"during establishing connection.\"\n              << std::endl;\n    result = -1;\n    goto fin;\n  }\n  if(set_tcp_nodelay(fd) == -1) {\n    std::cerr << \"Setting TCP_NODELAY failed: \" << strerror(errno)\n              << std::endl;\n  }\n\n  switch(config.spdy_version) {\n  case SPDYLAY_PROTO_SPDY2:\n    next_proto = \"spdy/2\";\n    break;\n  case SPDYLAY_PROTO_SPDY3:\n    next_proto = \"spdy/3\";\n    break;\n  case SPDYLAY_PROTO_SPDY3_1:\n    next_proto = \"spdy/3.1\";\n    break;\n  }\n\n  if(!config.no_tls) {\n    ssl_ctx = SSL_CTX_new(SSLv23_client_method());\n    if(!ssl_ctx) {\n      std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n      result = -1;\n      goto fin;\n    }\n    setup_ssl_ctx(ssl_ctx, &next_proto);\n    if(!config.keyfile.empty()) {\n      if(SSL_CTX_use_PrivateKey_file(ssl_ctx, config.keyfile.c_str(),\n                                     SSL_FILETYPE_PEM) != 1) {\n        std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n        result = -1;\n        goto fin;\n      }\n    }\n    if(!config.certfile.empty()) {\n      if(SSL_CTX_use_certificate_chain_file(ssl_ctx,\n                                            config.certfile.c_str()) != 1) {\n        std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n        result = -1;\n        goto fin;\n      }\n    }\n    ssl = SSL_new(ssl_ctx);\n    if(!ssl) {\n      std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n      result = -1;\n      goto fin;\n    }\n\n    // If the user overrode the :host header, use that value for the\n    // SNI extension\n    const char *host_string = 0;\n    for(size_t i = 0; i < config.headers.size(); ++i) {\n      if(util::strieq(\":host\", config.headers[i].first.c_str())) {\n        host_string = config.headers[i].second.c_str();\n      }\n    }\n\n    if(!host_string) {\n      host_string = host.c_str();\n    }\n\n    if (!SSL_set_tlsext_host_name(ssl, host_string)) {\n      std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n      result = -1;\n      goto fin;\n    }\n    rv = ssl_nonblock_handshake(ssl, fd, timeout);\n    if(rv == -1) {\n      result = -1;\n      goto fin;\n    } else if(rv == -2) {\n      std::cerr << \"Request to \" << spdySession.hostport\n                << \" timed out in SSL/TLS handshake.\"\n                << std::endl;\n      result = -1;\n      goto fin;\n    }\n  }\n\n  if ( config.verbose ) {\n    print_timer();\n    std::cout << \" Handshake complete\" << std::endl;\n  }\n\n  spdySession.record_handshake_time();\n  spdy_version = spdylay_npn_get_version(\n      reinterpret_cast<const unsigned char*>(next_proto.c_str()),\n      next_proto.size());\n  if (spdy_version <= 0) {\n    std::cerr << \"No supported SPDY version was negotiated.\" << std::endl;\n    result = -1;\n    goto fin;\n  }\n\n  result = spdy_evloop(fd, ssl, spdy_version, spdySession, callbacks, timeout);\n fin:\n  if(ssl) {\n    SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);\n    SSL_shutdown(ssl);\n    SSL_free(ssl);\n    SSL_CTX_free(ssl_ctx);\n  }\n  if(fd != -1) {\n    shutdown(fd, SHUT_WR);\n    close(fd);\n  }\n  return result;\n}\n\nssize_t file_read_callback\n(spdylay_session *session, int32_t stream_id,\n uint8_t *buf, size_t length, int *eof,\n spdylay_data_source *source, void *user_data)\n{\n  Request *req = (Request*)spdylay_session_get_stream_user_data\n    (session, stream_id);\n  assert(req);\n  int fd = source->fd;\n  ssize_t r;\n  while((r = pread(fd, buf, length, req->data_offset)) == -1 &&\n        errno == EINTR);\n  if(r == -1) {\n    return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;\n  } else {\n    if(r == 0) {\n      *eof = 1;\n    } else {\n      req->data_offset += r;\n    }\n    return r;\n  }\n}\n\nint run(char **uris, int n)\n{\n  spdylay_session_callbacks callbacks;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = send_callback;\n  callbacks.recv_callback = recv_callback;\n  callbacks.on_stream_close_callback = on_stream_close_callback;\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;\n  callbacks.before_ctrl_send_callback = before_ctrl_send_callback;\n  if(config.verbose) {\n    callbacks.on_data_recv_callback = on_data_recv_callback;\n    callbacks.on_data_send_callback = on_data_send_callback;\n    callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n    callbacks.on_ctrl_recv_parse_error_callback =\n      on_ctrl_recv_parse_error_callback;\n    callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;\n  }\n  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n  ssl_debug = config.verbose;\n  std::string prev_host;\n  uint16_t prev_port = 0;\n  int failures = 0;\n  int data_fd = -1;\n  spdylay_data_provider data_prd;\n  struct stat data_stat;\n\n  if(!config.datafile.empty()) {\n    if (config.datafile == \"-\") {\n      if (fstat(0, &data_stat) == 0 &&\n          (data_stat.st_mode & S_IFMT) == S_IFREG) {\n        // use STDIN if it is a regular file\n        data_fd = 0;\n      } else {\n        // copy the contents of STDIN to a temporary file\n        char tempfn[] = \"/tmp/nghttp.temp.XXXXXX\";\n        data_fd = mkstemp(tempfn);\n        if (data_fd == -1) {\n          std::cerr << \"[ERROR] Could not create a temporary file in /tmp\"\n                    << std::endl;\n          return 1;\n        }\n        if (unlink(tempfn) != 0) {\n          std::cerr << \"[WARNING] failed to unlink temporary file:\" << tempfn\n                    << std::endl;\n        }\n        while (1) {\n          char buf[1024];\n          ssize_t rret, wret;\n          while ((rret = read(0, buf, sizeof(buf))) == -1 && errno == EINTR)\n            ;\n          if (rret == 0)\n            break;\n          if (rret == -1) {\n            std::cerr << \"[ERROR] I/O error while reading from STDIN\"\n                      << std::endl;\n            return 1;\n          }\n          while ((wret = write(data_fd, buf, rret)) == -1 && errno == EINTR)\n            ;\n          if (wret != rret) {\n            std::cerr << \"[ERROR] I/O error while writing to temporary file\"\n                      << std::endl;\n            return 1;\n          }\n        }\n        if (fstat(data_fd, &data_stat) == -1) {\n          close(data_fd);\n          std::cerr << \"[ERROR] Could not stat temporary file\" << std::endl;\n          return 1;\n        }\n      }\n    } else {\n      data_fd = open(config.datafile.c_str(), O_RDONLY | O_BINARY);\n      if (data_fd == -1) {\n        std::cerr << \"[ERROR] Could not open file \" << config.datafile\n                  << std::endl;\n        return 1;\n      }\n      if (fstat(data_fd, &data_stat) == -1) {\n        close(data_fd);\n        std::cerr << \"[ERROR] Could not stat file \" << config.datafile\n                  << std::endl;\n        return 1;\n      }\n    }\n    data_prd.source.fd = data_fd;\n    data_prd.read_callback = file_read_callback;\n  }\n\n  SpdySession *spdySession = new SpdySession();\n\n  for(int i = 0; i < n; ++i) {\n    http_parser_url u;\n    std::string uri = strip_fragment(uris[i]);\n    if(http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) == 0 &&\n       has_uri_field(u, UF_SCHEMA)) {\n      uint16_t port = has_uri_field(u, UF_PORT) ?\n        u.port : get_default_port(uri.c_str(), u);\n      if(!fieldeq(uri.c_str(), u, UF_HOST, prev_host.c_str()) ||\n         port != prev_port) {\n        if(!spdySession->reqvec.empty()) {\n          spdySession->update_hostport();\n          if(!config.proxy_host.empty()) {\n            uint16_t port = 443;\n            if(config.proxy_port != 0) {\n              port = config.proxy_port;\n            }\n            if (communicate(config.proxy_host, port, *spdySession,\n                            &callbacks) != 0) {\n              ++failures;\n            }\n          } else {\n            if (communicate(prev_host, prev_port, *spdySession, &callbacks) !=\n                0) {\n              ++failures;\n            }\n          }\n          delete spdySession;\n          spdySession = new SpdySession();\n        }\n        prev_host = get_uri_field(uri.c_str(), u, UF_HOST);\n        prev_port = port;\n      }\n      for(int j = 0; j < config.multiply; ++j) {\n        spdySession->add_request(uri, u, data_fd == -1 ? 0 : &data_prd,\n                                 data_stat.st_size);\n      }\n    }\n  }\n  if(!spdySession->reqvec.empty()) {\n    spdySession->update_hostport();\n    if(!config.proxy_host.empty()) {\n      uint16_t port = 443;\n      if(config.proxy_port != 0) {\n        port = config.proxy_port;\n      }\n      if (communicate(config.proxy_host, port, *spdySession, &callbacks) != 0) {\n        ++failures;\n      }\n    } else {\n      if (communicate(prev_host, prev_port, *spdySession, &callbacks) != 0) {\n        ++failures;\n      }\n    }\n  }\n  delete spdySession;\n\n  return failures;\n}\n\nvoid print_usage(std::ostream& out)\n{\n  out << \"Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\\n\"\n      << \"               [--key=<KEY>] [--no-tls] [-d <FILE>] [-m <N>] [-p <PROXY_HOST>]\\n\"\n      << \"               [-P <PROXY_PORT>] <URI>...\"\n      << std::endl;\n}\n\nvoid print_help(std::ostream& out)\n{\n  print_usage(out);\n  out << \"\\n\"\n      << \"OPTIONS:\\n\"\n      << \"    -v, --verbose      Print debug information such as reception/\\n\"\n      << \"                       transmission of frames and name/value pairs.\\n\"\n      << \"    -n, --null-out     Discard downloaded data.\\n\"\n      << \"    -O, --remote-name  Save download data in the current directory.\\n\"\n      << \"                       The filename is dereived from URI. If URI\\n\"\n      << \"                       ends with '/', 'index.html' is used as a\\n\"\n      << \"                       filename. Not implemented yet.\\n\"\n      << \"    -2, --spdy2        Only use SPDY/2.\\n\"\n      << \"    -3, --spdy3        Only use SPDY/3.\\n\"\n      << \"    --spdy3-1          Only use SPDY/3.1.\\n\"\n      << \"    -t, --timeout=<N>  Timeout each request after <N> seconds.\\n\"\n      << \"    -w, --window-bits=<N>\\n\"\n      << \"                       Sets the initial window size to 2**<N>.\\n\"\n      << \"    -a, --get-assets   Download assets such as stylesheets, images\\n\"\n      << \"                       and script files linked from the downloaded\\n\"\n      << \"                       resource. Only links whose origins are the\\n\"\n      << \"                       same with the linking resource will be\\n\"\n      << \"                       downloaded.\\n\"\n      << \"    -s, --stat         Print statistics.\\n\"\n      << \"    -H, --header=<HEADER>\\n\"\n      << \"                       Add a header to the requests. Use This\\n\"\n      << \"                       option repeatedly to add multiple headers.\\n\"\n      << \"                       The hard coded headers (e.g., :method) can\\n\"\n      << \"                       be overridden by specifying same header\\n\"\n      << \"                       field name and its replacement value.\\n\"\n      << \"                       Examples:\\n\"\n      << \"                         -H':method: HEAD'\\n\"\n      << \"                         -H':host: custom-name.example.org'\\n\"\n      << \"                       Use --verbose to display all of the headers being sent\\n\"\n      << \"    --cert=<CERT>      Use the specified client certificate file.\\n\"\n      << \"                       The file must be in PEM format.\\n\"\n      << \"    --key=<KEY>        Use the client private key file. The file\\n\"\n      << \"                       must be in PEM format.\\n\"\n      << \"    --no-tls           Disable SSL/TLS. Use -2, -3 or --spdy3-1 to\\n\"\n      << \"                       specify SPDY protocol version to use.\\n\"\n      << \"    -d, --data=<FILE>  Post FILE to server. If - is given, data\\n\"\n      << \"                       will be read from stdin.\\n\"\n      << \"    -m, --multiply=<N> Request each URI <N> times. By default, same\\n\"\n      << \"                       URI is not requested twice. This option\\n\"\n      << \"                       disables it too.\\n\"\n      << \"    -p, --proxy=<HOST> Use this host as a SPDY proxy\\n\"\n      << \"    -P, --proxy-port=<PORT>\\n\"\n      << \"                       Use this as the port of the SPDY proxy if\\n\"\n      << \"                       one is set\\n\"\n      << \"    --color            Force colored log output.\\n\"\n      << std::endl;\n}\n\nint main(int argc, char **argv)\n{\n  bool color = false;\n  while(1) {\n    static int flag = 0;\n    static option long_options[] = {\n      {\"verbose\", no_argument, 0, 'v' },\n      {\"null-out\", no_argument, 0, 'n' },\n      {\"remote-name\", no_argument, 0, 'O' },\n      {\"spdy2\", no_argument, 0, '2' },\n      {\"spdy3\", no_argument, 0, '3' },\n      {\"timeout\", required_argument, 0, 't' },\n      {\"window-bits\", required_argument, 0, 'w' },\n      {\"get-assets\", no_argument, 0, 'a' },\n      {\"stat\", no_argument, 0, 's' },\n      {\"help\", no_argument, 0, 'h' },\n      {\"header\", required_argument, 0, 'H' },\n      {\"data\", required_argument, 0, 'd' },\n      {\"multiply\", required_argument, 0, 'm' },\n      {\"proxy\", required_argument, 0, 'p' },\n      {\"proxy-port\", required_argument, 0, 'P' },\n      {\"cert\", required_argument, &flag, 1 },\n      {\"key\", required_argument, &flag, 2 },\n      {\"no-tls\", no_argument, &flag, 3 },\n      {\"color\", no_argument, &flag, 4 },\n      {\"spdy3-1\", no_argument, &flag, 5 },\n      {0, 0, 0, 0 }\n    };\n    int option_index = 0;\n    int c = getopt_long(argc, argv, \"Oad:m:nhH:v23st:w:p:P:\", long_options,\n                        &option_index);\n    if(c == -1) {\n      break;\n    }\n    switch(c) {\n    case 'O':\n      config.remote_name = true;\n      break;\n    case 'h':\n      print_help(std::cout);\n      exit(EXIT_SUCCESS);\n    case 'n':\n      config.null_out = true;\n      break;\n    case 'v':\n      config.verbose = true;\n      break;\n    case '2':\n      config.spdy_version = SPDYLAY_PROTO_SPDY2;\n      break;\n    case '3':\n      config.spdy_version = SPDYLAY_PROTO_SPDY3;\n      break;\n    case 't':\n      config.timeout = atoi(optarg) * 1000;\n      break;\n    case 'w': {\n      errno = 0;\n      unsigned long int n = strtoul(optarg, 0, 10);\n      if(errno == 0 && n < 31) {\n        config.window_bits = n;\n      } else {\n        std::cerr << \"-w: specify the integer in the range [0, 30], inclusive\"\n                  << std::endl;\n        exit(EXIT_FAILURE);\n      }\n      break;\n    }\n    case 'H': {\n      char *header = optarg;\n      // Skip first possible ':' in the header name\n      char *value = strchr( optarg + 1, ':' );\n      if ( ! value || (header[0] == ':' && header + 1 == value)) {\n        std::cerr << \"-H: invalid header: \" << optarg\n                  << std::endl;\n        exit(EXIT_FAILURE);\n      }\n      *value = 0;\n      value++;\n      while( isspace( *value ) ) { value++; }\n      if ( *value == 0 ) {\n        // This could also be a valid case for suppressing a header\n        // similar to curl\n        std::cerr << \"-H: invalid header - value missing: \" << optarg\n                  << std::endl;\n        exit(EXIT_FAILURE);\n      }\n      config.headers.push_back(std::make_pair(header, value));\n\n      break;\n    }\n    case 'a':\n#ifdef HAVE_LIBXML2\n      config.get_assets = true;\n#else // !HAVE_LIBXML2\n      std::cerr << \"Warning: -a, --get-assets option cannot be used because\\n\"\n                << \"the binary was not compiled with libxml2.\"\n                << std::endl;\n#endif // !HAVE_LIBXML2\n      break;\n    case 's':\n      config.stat = true;\n      break;\n    case 'd':\n      config.datafile = optarg;\n      break;\n    case 'm':\n      config.multiply = strtoul(optarg, 0, 10);\n      break;\n    case 'p':\n      config.proxy_host = optarg;\n      break;\n    case 'P':\n      config.proxy_port = strtoul(optarg, 0, 10);\n      break;\n    case '?':\n      exit(EXIT_FAILURE);\n    case 0:\n      switch(flag) {\n      case 1:\n        // cert option\n        config.certfile = optarg;\n        break;\n      case 2:\n        // key option\n        config.keyfile = optarg;\n        break;\n      case 3:\n        // no-tls option\n        config.no_tls = true;\n        break;\n      case 4:\n        // color option\n        color = true;\n        break;\n      case 5:\n        // spdy3-1 option\n        config.spdy_version = SPDYLAY_PROTO_SPDY3_1;\n        break;\n      }\n      break;\n    default:\n      break;\n    }\n  }\n\n  if(config.no_tls) {\n    if(config.spdy_version == -1) {\n      std::cerr << \"Specify SPDY protocol version using either -2, -3 or \"\n                << \"--spdy3-1.\"\n                << std::endl;\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  set_color_output(color || isatty(fileno(stdout)));\n\n  struct sigaction act;\n  memset(&act, 0, sizeof(struct sigaction));\n  act.sa_handler = SIG_IGN;\n  sigaction(SIGPIPE, &act, 0);\n  SSL_load_error_strings();\n  SSL_library_init();\n  reset_timer();\n  return run(argv+optind, argc-optind);\n}\n\n} // namespace spdylay\n\nint main(int argc, char **argv)\n{\n  return spdylay::main(argc, argv);\n}\n"
  },
  {
    "path": "src/spdyd.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include <unistd.h>\n#include <signal.h>\n#include <getopt.h>\n\n#include <cstdlib>\n#include <cstring>\n#include <cassert>\n#include <string>\n#include <iostream>\n#include <string>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <spdylay/spdylay.h>\n\n#include \"spdylay_ssl.h\"\n#include \"SpdyServer.h\"\n\nnamespace spdylay {\n\nextern bool ssl_debug;\n\nnamespace {\nvoid print_usage(std::ostream& out)\n{\n  out << \"Usage: spdyd [-23DVhv] [-d <PATH>] [--no-tls] <PORT> [<PRIVATE_KEY> <CERT>]\"\n      << std::endl;\n}\n} // namespace\n\nnamespace {\nvoid print_help(std::ostream& out)\n{\n  print_usage(out);\n  out << \"\\n\"\n      << \"OPTIONS:\\n\"\n      << \"    -D, --daemon       Run in a background. If -D is used, the\\n\"\n      << \"                       current working directory is changed to '/'.\\n\"\n      << \"                       Therefore if this option is used, -d option\\n\"\n      << \"                       must be specified.\\n\"\n      << \"    -V, --verify-client\\n\"\n      << \"                       The server sends a client certificate\\n\"\n      << \"                       request. If the client did not return a\\n\"\n      << \"                       certificate, the handshake is terminated.\\n\"\n      << \"                       Currently, this option just requests a\\n\"\n      << \"                       client certificate and does not verify it.\\n\"\n      << \"    -d, --htdocs=<PATH>\\n\"\n      << \"                       Specify document root. If this option is\\n\"\n      << \"                       not specified, the document root is the\\n\"\n      << \"                       current working directory.\\n\"\n      << \"    -v, --verbose      Print debug information such as reception/\\n\"\n      << \"                       transmission of frames and name/value pairs.\\n\"\n      << \"    -2, --spdy2        Only use SPDY/2.\\n\"\n      << \"    -3, --spdy3        Only use SPDY/3.\\n\"\n      << \"    --spdy3-1          Only use SPDY/3.1.\\n\"\n      << \"    --no-tls           Disable SSL/TLS. Use -2, -3 or --spdy3-1 to\\n\"\n      << \"                       specify SPDY protocol version to use.\\n\"\n      << \"    --color            Force colored log output.\\n\"\n      << \"    -h, --help         Print this help.\\n\"\n      << std::endl;\n}\n} // namespace\n\nint main(int argc, char **argv)\n{\n  Config config;\n  bool color = false;\n  while(1) {\n    static int flag = 0;\n    static option long_options[] = {\n      {\"daemon\", no_argument, 0, 'D' },\n      {\"htdocs\", required_argument, 0, 'd' },\n      {\"help\", no_argument, 0, 'h' },\n      {\"verbose\", no_argument, 0, 'v' },\n      {\"spdy2\", no_argument, 0, '2' },\n      {\"spdy3\", no_argument, 0, '3' },\n      {\"verify-client\", no_argument, 0, 'V' },\n      {\"no-tls\", no_argument, &flag, 1 },\n      {\"color\", no_argument, &flag, 2 },\n      {\"spdy3-1\", no_argument, &flag, 3 },\n      {0, 0, 0, 0 }\n    };\n    int option_index = 0;\n    int c = getopt_long(argc, argv, \"DVd:hv23\", long_options, &option_index);\n    if(c == -1) {\n      break;\n    }\n    switch(c) {\n    case 'D':\n      config.daemon = true;\n      break;\n    case 'V':\n      config.verify_client = true;\n      break;\n    case 'd':\n      config.htdocs = optarg;\n      break;\n    case 'h':\n      print_help(std::cout);\n      exit(EXIT_SUCCESS);\n    case 'v':\n      config.verbose = true;\n      break;\n    case '2':\n      config.version = SPDYLAY_PROTO_SPDY2;\n      break;\n    case '3':\n      config.version = SPDYLAY_PROTO_SPDY3;\n      break;\n    case '?':\n      exit(EXIT_FAILURE);\n    case 0:\n      switch(flag) {\n      case 1:\n        // no-tls option\n        config.no_tls = true;\n        break;\n      case 2:\n        // color option\n        color = true;\n        break;\n      case 3:\n        // spdy3-1 option\n        config.version = SPDYLAY_PROTO_SPDY3_1;\n        break;\n      }\n      break;\n    default:\n      break;\n    }\n  }\n  if(argc-optind < (config.no_tls ? 1 : 3)) {\n    print_usage(std::cerr);\n    std::cerr << \"Too few arguments\" << std::endl;\n    exit(EXIT_FAILURE);\n  }\n\n  config.port = strtol(argv[optind++], 0, 10);\n\n  if(config.no_tls) {\n    if(config.version == 0) {\n      std::cerr << \"Specify SPDY protocol version using either -2, -3 or \"\n                << \"--spdy3-1.\"\n                << std::endl;\n      exit(EXIT_FAILURE);\n    }\n  } else {\n    config.private_key_file = argv[optind++];\n    config.cert_file = argv[optind++];\n  }\n\n  if(config.daemon) {\n    if(config.htdocs.empty()) {\n      print_usage(std::cerr);\n      std::cerr << \"-d option must be specified when -D is used.\" << std::endl;\n      exit(EXIT_FAILURE);\n    }\n    if(daemon(0, 0) == -1) {\n      perror(\"daemon\");\n      exit(EXIT_FAILURE);\n    }\n  }\n  if(config.htdocs.empty()) {\n    config.htdocs = \"./\";\n  }\n\n  set_color_output(color || isatty(fileno(stdout)));\n\n  struct sigaction act;\n  memset(&act, 0, sizeof(struct sigaction));\n  act.sa_handler = SIG_IGN;\n  sigaction(SIGPIPE, &act, 0);\n  OpenSSL_add_all_algorithms();\n  SSL_load_error_strings();\n  SSL_library_init();\n  reset_timer();\n  config.on_request_recv_callback = htdocs_on_request_recv_callback;\n  ssl_debug = config.verbose;\n\n  SpdyServer server(&config);\n  if(server.listen() == 0) {\n    server.run();\n  }\n  return 0;\n}\n\n} // namespace spdylay\n\nint main(int argc, char **argv)\n{\n  return spdylay::main(argc, argv);\n}\n"
  },
  {
    "path": "src/spdylay_config.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_CONFIG_H\n#define SPDYLAY_CONFIG_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif // HAVE_CONFIG_H\n\n#endif // SPDYLAY_CONFIG_H\n"
  },
  {
    "path": "src/spdylay_ssl.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifdef __sgi\n#define CLOCK_MONOTONIC CLOCK_REALTIME\n#endif // !__sgi\n\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>\n#include <poll.h>\n\n#include <cassert>\n#include <cstdio>\n#include <cerrno>\n#include <cstdlib>\n#include <cstring>\n#include <string>\n#include <iostream>\n#include <string>\n#include <set>\n#include <iomanip>\n#include <fstream>\n\n#include \"spdylay_ssl.h\"\n#include \"util.h\"\n\nnamespace spdylay {\n\nbool ssl_debug = false;\n\nSpdylay::Spdylay(int fd, SSL *ssl, uint16_t version,\n                 const spdylay_session_callbacks *callbacks,\n                 void *user_data)\n  : ssl_(ssl), user_data_(user_data), fd_(fd), version_(version),\n    io_flags_(0)\n{\n  int r = spdylay_session_client_new(&session_, version_, callbacks, this);\n  assert(r == 0);\n}\n\nSpdylay::~Spdylay()\n{\n  spdylay_session_del(session_);\n}\n\nvoid Spdylay::clear_io_flags()\n{\n  io_flags_ = 0;\n}\n\nint Spdylay::recv()\n{\n  return spdylay_session_recv(session_);\n}\n\nint Spdylay::send()\n{\n  return spdylay_session_send(session_);\n}\n\nssize_t Spdylay::send_data(const uint8_t *data, size_t len, int flags)\n{\n  ssize_t r;\n\n  if(ssl_) {\n    ERR_clear_error();\n    r = SSL_write(ssl_, data, len);\n    if(r < 0) {\n      io_flags_ = get_ssl_io_demand(ssl_, r);\n    }\n  } else {\n    while((r = ::send(fd_, data, len, 0)) == -1 && errno == EINTR);\n    if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {\n      io_flags_ |= WANT_WRITE;\n    }\n  }\n  return r;\n}\n\nssize_t Spdylay::recv_data(uint8_t *data, size_t len, int flags)\n{\n  ssize_t r;\n\n  if(ssl_) {\n    ERR_clear_error();\n    r = SSL_read(ssl_, data, len);\n    if(r < 0) {\n      io_flags_ = get_ssl_io_demand(ssl_, r);\n    }\n  } else {\n    while((r = ::recv(fd_, data, len, 0)) == -1 && errno == EINTR);\n    if(r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {\n      io_flags_ |= WANT_READ;\n    }\n  }\n  return r;\n}\n\nbool Spdylay::want_read()\n{\n  return spdylay_session_want_read(session_) || (io_flags_ & WANT_READ);\n}\n\nbool Spdylay::want_write()\n{\n  return spdylay_session_want_write(session_) || (io_flags_ & WANT_WRITE);\n}\n\nbool Spdylay::finish()\n{\n  return !spdylay_session_want_read(session_) &&\n    !spdylay_session_want_write(session_);\n}\n\nint Spdylay::fd() const\n{\n  return fd_;\n}\n\nvoid* Spdylay::user_data()\n{\n  return user_data_;\n}\n\nint Spdylay::submit_request\n(const std::string& scheme,\n const std::string& hostport,\n const std::string& path,\n const std::vector<std::pair<std::string, std::string> >& headers,\n uint8_t pri,\n const spdylay_data_provider *data_prd,\n int64_t data_length,\n void *stream_user_data,\n bool useProxy,\n const std::string& proxyHost,\n uint16_t proxyPort)\n{\n  std::string proxyHostPort = proxyHost;\n\n  proxyHostPort += \":\";\n  if(proxyPort == 0) {\n    proxyHostPort += \":443\";\n  }\n  else {\n    proxyHostPort += util::utos(proxyPort);\n  }\n\n  const char *static_nv[] = {\n    \":method\", data_prd ? \"POST\" : \"GET\",\n    \":path\", path.c_str(),\n    \":version\", \"HTTP/1.1\",\n    \":scheme\", scheme.c_str(),\n    \":host\", hostport.c_str(),\n    \"accept\", \"*/*\",\n    \"accept-encoding\", \"gzip, deflate\",\n    \"user-agent\", \"spdylay/\" SPDYLAY_VERSION\n  };\n\n  size_t hardcoded_entry_count = sizeof(static_nv) / sizeof(*static_nv);\n\n  if(data_prd) {\n    hardcoded_entry_count += 2;\n  }\n\n  size_t total_entry_count = hardcoded_entry_count + headers.size() * 2;\n\n  std::vector<const char*> nv;\n  // +1 for last NULL\n  nv.reserve(total_entry_count + 1);\n  nv.assign(static_nv, static_nv + sizeof(static_nv) / sizeof(*static_nv));\n\n  std::string content_length_str;\n  if(data_prd) {\n    std::stringstream ss;\n    ss << data_length;\n    content_length_str = ss.str();\n\n    nv.push_back(\"content-length\");\n    nv.push_back(content_length_str.c_str());\n  }\n\n  for(size_t i = 0; i < headers.size(); ++i) {\n    const char *name = headers[i].first.c_str();\n    const char *value = headers[i].second.c_str();\n\n    size_t j;\n    for(j = 0; j < hardcoded_entry_count; j += 2) {\n      if(util::strieq(nv[j], name)) {\n        nv[j + 1] = value;\n        break;\n      }\n    }\n\n    if(j < hardcoded_entry_count) {\n      continue;\n    }\n\n    nv.push_back(name);\n    nv.push_back(value);\n  }\n\n  nv.push_back(0);\n\n  int r = spdylay_submit_request(session_, pri, &nv[0], data_prd,\n                                 stream_user_data);\n\n  return r;\n}\n\nint Spdylay::submit_settings(int flags, spdylay_settings_entry *iv, size_t niv)\n{\n  return spdylay_submit_settings(session_, flags, iv, niv);\n}\n\nbool Spdylay::would_block()\n{\n  return io_flags_;\n}\n\nint connect_to(const std::string& host, uint16_t port)\n{\n  struct addrinfo hints;\n  int fd = -1;\n  int r;\n  char service[10];\n  snprintf(service, sizeof(service), \"%u\", port);\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  struct addrinfo *res;\n  r = getaddrinfo(host.c_str(), service, &hints, &res);\n  if(r != 0) {\n    std::cerr << \"getaddrinfo: \" << gai_strerror(r) << std::endl;\n    return -1;\n  }\n  for(struct addrinfo *rp = res; rp; rp = rp->ai_next) {\n    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    if(fd == -1) {\n      continue;\n    }\n    while((r = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&\n          errno == EINTR);\n    if(r == 0) {\n      break;\n    }\n    close(fd);\n    fd = -1;\n  }\n  freeaddrinfo(res);\n  return fd;\n}\n\nint nonblock_connect_to(const std::string& host, uint16_t port, int timeout)\n{\n  struct addrinfo hints;\n  int fd = -1;\n  int r;\n  char service[10];\n  snprintf(service, sizeof(service), \"%u\", port);\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  struct addrinfo *res;\n  r = getaddrinfo(host.c_str(), service, &hints, &res);\n  if(r != 0) {\n    std::cerr << \"getaddrinfo: \" << gai_strerror(r) << std::endl;\n    return -1;\n  }\n  for(struct addrinfo *rp = res; rp; rp = rp->ai_next) {\n    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    if(fd == -1) {\n      continue;\n    }\n    if(make_non_block(fd) == -1) {\n      close(fd);\n      fd = -1;\n      continue;\n    }\n    while((r = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&\n          errno == EINTR);\n    if(r == 0) {\n      break;\n    } else if(errno == EINPROGRESS) {\n      struct timeval tv1, tv2;\n      struct pollfd pfd = {fd, POLLOUT, 0};\n      if(timeout != -1) {\n        get_time(&tv1);\n      }\n      r = poll(&pfd, 1, timeout);\n      if(r == 0) {\n        freeaddrinfo(res);\n        return -2;\n      } else if(r == -1) {\n        freeaddrinfo(res);\n        return -1;\n      } else {\n        if(timeout != -1) {\n          get_time(&tv2);\n          timeout -= time_delta(tv2, tv1);\n          if(timeout <= 0) {\n            freeaddrinfo(res);\n            return -2;\n          }\n        }\n        int socket_error;\n        socklen_t optlen = sizeof(socket_error);\n        r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen);\n        if(r == 0 && socket_error == 0) {\n          break;\n        } else {\n          close(fd);\n          fd = -1;\n        }\n      }\n    } else {\n      close(fd);\n      fd = -1;\n    }\n  }\n  freeaddrinfo(res);\n  return fd;\n}\n\nint make_listen_socket(const std::string& host, uint16_t port, int family)\n{\n  addrinfo hints;\n  int fd = -1;\n  int r;\n  char service[10];\n  snprintf(service, sizeof(service), \"%u\", port);\n  memset(&hints, 0, sizeof(addrinfo));\n  hints.ai_family = family;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE;\n#ifdef AI_ADDRCONFIG\n  hints.ai_flags |= AI_ADDRCONFIG;\n#endif // AI_ADDRCONFIG\n  addrinfo *res, *rp;\n  const char* host_ptr;\n  if(host.empty()) {\n    host_ptr = 0;\n  } else {\n    host_ptr = host.c_str();\n  }\n  r = getaddrinfo(host_ptr, service, &hints, &res);\n  if(r != 0) {\n    std::cerr << \"getaddrinfo: \" << gai_strerror(r) << std::endl;\n    return -1;\n  }\n  for(rp = res; rp; rp = rp->ai_next) {\n    fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    if(fd == -1) {\n      continue;\n    }\n    int val = 1;\n    if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,\n                  static_cast<socklen_t>(sizeof(val))) == -1) {\n      close(fd);\n      continue;\n    }\n#ifdef IPV6_V6ONLY\n    if(family == AF_INET6) {\n      if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,\n                    static_cast<socklen_t>(sizeof(val))) == -1) {\n        close(fd);\n        continue;\n      }\n    }\n#endif // IPV6_V6ONLY\n\n    if(bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) {\n      break;\n    }\n    close(fd);\n  }\n  freeaddrinfo(res);\n  if(rp == 0) {\n    return -1;\n  } else {\n    if(listen(fd, 16) == -1) {\n      close(fd);\n      return -1;\n    } else {\n      return fd;\n    }\n  }\n}\n\nint make_non_block(int fd)\n{\n  int flags, r;\n  while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR);\n  if(flags == -1) {\n    return -1;\n  }\n  while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR);\n  if(r == -1) {\n    return -1;\n  }\n  return 0;\n}\n\nint set_tcp_nodelay(int fd)\n{\n  int val = 1;\n  return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));\n}\n\nssize_t send_callback(spdylay_session *session,\n                      const uint8_t *data, size_t len, int flags,\n                      void *user_data)\n{\n  Spdylay *sc = (Spdylay*)user_data;\n  ssize_t r = sc->send_data(data, len, flags);\n  if(r < 0) {\n    if(sc->would_block()) {\n      r = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      r = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(r == 0) {\n    // In OpenSSL, r == 0 means EOF because SSL_write may do read.\n    r = SPDYLAY_ERR_CALLBACK_FAILURE;\n  }\n  return r;\n}\n\nssize_t recv_callback(spdylay_session *session,\n                      uint8_t *data, size_t len, int flags, void *user_data)\n{\n  Spdylay *sc = (Spdylay*)user_data;\n  ssize_t r = sc->recv_data(data, len, flags);\n  if(r < 0) {\n    if(sc->would_block()) {\n      r = SPDYLAY_ERR_WOULDBLOCK;\n    } else {\n      r = SPDYLAY_ERR_CALLBACK_FAILURE;\n    }\n  } else if(r == 0) {\n    r = SPDYLAY_ERR_EOF;\n  }\n  return r;\n}\n\nnamespace {\nconst char *ctrl_names[] = {\n  \"SYN_STREAM\",\n  \"SYN_REPLY\",\n  \"RST_STREAM\",\n  \"SETTINGS\",\n  \"NOOP\",\n  \"PING\",\n  \"GOAWAY\",\n  \"HEADERS\",\n  \"WINDOW_UPDATE\"\n};\n} // namespace\n\nnamespace {\nvoid print_frame_attr_indent()\n{\n  printf(\"          \");\n}\n} // namespace\n\nnamespace {\nbool color_output = false;\n} // namespace\n\nvoid set_color_output(bool f)\n{\n  color_output = f;\n}\n\nnamespace {\nconst char* ansi_esc(const char *code)\n{\n  return color_output ? code : \"\";\n}\n} // namespace\n\nnamespace {\nconst char* ansi_escend()\n{\n  return color_output ? \"\\033[0m\" : \"\";\n}\n} // namespace\n\nvoid print_nv(char **nv)\n{\n  int i;\n  for(i = 0; nv[i]; i += 2) {\n    print_frame_attr_indent();\n    printf(\"%s%s%s: %s\\n\",\n           ansi_esc(\"\\033[1;34m\"), nv[i],\n           ansi_escend(), nv[i+1]);\n  }\n}\n\nvoid print_timer()\n{\n  timeval tv;\n  get_timer(&tv);\n  printf(\"%s[%3ld.%03ld]%s\",\n         ansi_esc(\"\\033[33m\"),\n         static_cast<long int>(tv.tv_sec),\n         static_cast<long int>(tv.tv_usec)/1000,\n         ansi_escend());\n}\n\nnamespace {\nvoid print_ctrl_hd(const spdylay_ctrl_hd& hd)\n{\n  printf(\"<version=%u, flags=%u, length=%d>\\n\",\n         hd.version, hd.flags, hd.length);\n}\n} // namespace\n\nenum print_type {\n  PRINT_SEND,\n  PRINT_RECV\n};\n\nnamespace {\nconst char* frame_name_ansi_esc(print_type ptype)\n{\n  return ansi_esc(ptype == PRINT_SEND ? \"\\033[1;35m\" : \"\\033[1;36m\");\n}\n} // namespace\n\nnamespace {\nvoid print_frame(print_type ptype, spdylay_frame_type type,\n                 spdylay_frame *frame)\n{\n  printf(\"%s%s%s frame \",\n         frame_name_ansi_esc(ptype),\n         ctrl_names[type-1],\n         ansi_escend());\n  print_ctrl_hd(frame->syn_stream.hd);\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    print_frame_attr_indent();\n    printf(\"(stream_id=%d, assoc_stream_id=%d, pri=%u)\\n\",\n           frame->syn_stream.stream_id, frame->syn_stream.assoc_stream_id,\n           frame->syn_stream.pri);\n    print_nv(frame->syn_stream.nv);\n    break;\n  case SPDYLAY_SYN_REPLY:\n    print_frame_attr_indent();\n    printf(\"(stream_id=%d)\\n\", frame->syn_reply.stream_id);\n    print_nv(frame->syn_reply.nv);\n    break;\n  case SPDYLAY_RST_STREAM:\n    print_frame_attr_indent();\n    printf(\"(stream_id=%d, status_code=%u)\\n\",\n           frame->rst_stream.stream_id, frame->rst_stream.status_code);\n    break;\n  case SPDYLAY_SETTINGS:\n    print_frame_attr_indent();\n    printf(\"(niv=%lu)\\n\", static_cast<unsigned long>(frame->settings.niv));\n    for(size_t i = 0; i < frame->settings.niv; ++i) {\n      print_frame_attr_indent();\n      printf(\"[%d(%u):%u]\\n\",\n             frame->settings.iv[i].settings_id,\n             frame->settings.iv[i].flags, frame->settings.iv[i].value);\n    }\n    break;\n  case SPDYLAY_PING:\n    print_frame_attr_indent();\n    printf(\"(unique_id=%d)\\n\", frame->ping.unique_id);\n    break;\n  case SPDYLAY_GOAWAY:\n    print_frame_attr_indent();\n    printf(\"(last_good_stream_id=%d)\\n\", frame->goaway.last_good_stream_id);\n    break;\n  case SPDYLAY_HEADERS:\n    print_frame_attr_indent();\n    printf(\"(stream_id=%d)\\n\", frame->headers.stream_id);\n    print_nv(frame->headers.nv);\n    break;\n  case SPDYLAY_WINDOW_UPDATE:\n    print_frame_attr_indent();\n    printf(\"(stream_id=%d, delta_window_size=%d)\\n\",\n           frame->window_update.stream_id,\n           frame->window_update.delta_window_size);\n    break;\n  default:\n    printf(\"\\n\");\n    break;\n  }\n}\n} // namespace\n\nvoid on_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  print_timer();\n  printf(\" recv \");\n  print_frame(PRINT_RECV, type, frame);\n  fflush(stdout);\n}\n\nnamespace {\nconst char* strstatus(uint32_t status_code)\n{\n  switch(status_code) {\n  case SPDYLAY_OK:\n    return \"OK\";\n  case SPDYLAY_PROTOCOL_ERROR:\n    return \"PROTOCOL_ERROR\";\n  case SPDYLAY_INVALID_STREAM:\n    return \"INVALID_STREAM\";\n  case SPDYLAY_REFUSED_STREAM:\n    return \"REFUSED_STREAM\";\n  case SPDYLAY_UNSUPPORTED_VERSION:\n    return \"UNSUPPORTED_VERSION\";\n  case SPDYLAY_CANCEL:\n    return \"CANCEL\";\n  case SPDYLAY_INTERNAL_ERROR:\n    return \"INTERNAL_ERROR\";\n  case SPDYLAY_FLOW_CONTROL_ERROR:\n    return \"FLOW_CONTROL_ERROR\";\n  case SPDYLAY_STREAM_IN_USE:\n    return \"STREAM_IN_USE\";\n  case SPDYLAY_STREAM_ALREADY_CLOSED:\n    return \"STREAM_ALREADY_CLOSED\";\n  case SPDYLAY_INVALID_CREDENTIALS:\n    return \"INVALID_CREDENTIALS\";\n  case SPDYLAY_FRAME_TOO_LARGE:\n    return \"FRAME_TOO_LARGE\";\n  default:\n    return \"Unknown status code\";\n  }\n}\n} // namespace\n\nvoid on_invalid_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n uint32_t status_code, void *user_data)\n{\n  print_timer();\n  printf(\" [INVALID; status=%s] recv \", strstatus(status_code));\n  print_frame(PRINT_RECV, type, frame);\n  fflush(stdout);\n}\n\nnamespace {\nvoid dump_header(const uint8_t *head, size_t headlen)\n{\n  size_t i;\n  print_frame_attr_indent();\n  printf(\"Header dump: \");\n  for(i = 0; i < headlen; ++i) {\n    printf(\"%02X \", head[i]);\n  }\n  printf(\"\\n\");\n}\n} // namespace\n\nvoid on_ctrl_recv_parse_error_callback(spdylay_session *session,\n                                       spdylay_frame_type type,\n                                       const uint8_t *head,\n                                       size_t headlen,\n                                       const uint8_t *payload,\n                                       size_t payloadlen,\n                                       int error_code, void *user_data)\n{\n  print_timer();\n  printf(\" [PARSE_ERROR] recv %s%s%s frame\\n\",\n         frame_name_ansi_esc(PRINT_RECV),\n         ctrl_names[type-1],\n         ansi_escend());\n  print_frame_attr_indent();\n  printf(\"Error: %s\\n\", spdylay_strerror(error_code));\n  dump_header(head, headlen);\n  fflush(stdout);\n}\n\nvoid on_unknown_ctrl_recv_callback(spdylay_session *session,\n                                   const uint8_t *head,\n                                   size_t headlen,\n                                   const uint8_t *payload,\n                                   size_t payloadlen,\n                                   void *user_data)\n{\n  print_timer();\n  printf(\" recv unknown frame\\n\");\n  dump_header(head, headlen);\n  fflush(stdout);\n}\n\nvoid on_ctrl_send_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data)\n{\n  print_timer();\n  printf(\" send \");\n  print_frame(PRINT_SEND, type, frame);\n  fflush(stdout);\n}\n\nnamespace {\nvoid print_data_frame(print_type ptype, uint8_t flags, int32_t stream_id,\n                      int32_t length)\n{\n  printf(\"%sDATA%s frame (stream_id=%d, flags=%d, length=%d)\\n\",\n         frame_name_ansi_esc(ptype), ansi_escend(),\n         stream_id, flags, length);\n}\n} // namespace\n\nvoid on_data_recv_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data)\n{\n  print_timer();\n  printf(\" recv \");\n  print_data_frame(PRINT_RECV, flags, stream_id, length);\n  fflush(stdout);\n}\n\nvoid on_data_send_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data)\n{\n  print_timer();\n  printf(\" send \");\n  print_data_frame(PRINT_SEND, flags, stream_id, length);\n  fflush(stdout);\n}\n\nvoid ctl_poll(pollfd *pollfd, Spdylay *sc)\n{\n  pollfd->events = 0;\n  if(sc->want_read()) {\n    pollfd->events |= POLLIN;\n  }\n  if(sc->want_write()) {\n    pollfd->events |= POLLOUT;\n  }\n}\n\nint select_next_proto_cb(SSL* ssl,\n                         unsigned char **out, unsigned char *outlen,\n                         const unsigned char *in, unsigned int inlen,\n                         void *arg)\n{\n  if(ssl_debug) {\n    print_timer();\n    std::cout << \" NPN select next protocol: the remote server offers:\"\n              << std::endl;\n  }\n  for(unsigned int i = 0; i < inlen; i += in[i]+1) {\n    if(ssl_debug) {\n      std::cout << \"          * \";\n      std::cout.write(reinterpret_cast<const char*>(&in[i+1]), in[i]);\n      std::cout << std::endl;\n    }\n  }\n  std::string& next_proto = *(std::string*)arg;\n  if(next_proto.empty()) {\n    if(spdylay_select_next_protocol(out, outlen, in, inlen) <= 0) {\n      std::cerr << \"Server did not advertise SPDY protocol.\"\n                << std::endl;\n      return SSL_TLSEXT_ERR_NOACK;\n    } else {\n      next_proto.assign(&(*out)[0], &(*out)[*outlen]);\n    }\n  } else {\n    *out = (unsigned char*)(next_proto.c_str());\n    *outlen = next_proto.size();\n  }\n  if(ssl_debug) {\n    std::cout << \"          NPN selected the protocol: \"\n              << std::string((const char*)*out, (size_t)*outlen) << std::endl;\n  }\n  return SSL_TLSEXT_ERR_OK;\n}\n\nvoid setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg)\n{\n  /* Disable SSLv2 and enable all workarounds for buggy servers */\n  SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);\n  SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);\n  SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb,\n                                   next_proto_select_cb_arg);\n}\n\nint ssl_handshake(SSL *ssl, int fd)\n{\n  if(SSL_set_fd(ssl, fd) == 0) {\n    std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n    return -1;\n  }\n  ERR_clear_error();\n  int r = SSL_connect(ssl);\n  if(r <= 0) {\n    std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n    return -1;\n  }\n  return 0;\n}\n\nint ssl_nonblock_handshake(SSL *ssl, int fd, int& timeout)\n{\n  if(SSL_set_fd(ssl, fd) == 0) {\n    std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n    return -1;\n  }\n  ERR_clear_error();\n  pollfd pfd;\n  pfd.fd = fd;\n  pfd.events = POLLOUT;\n  timeval tv1 = {}, tv2 = {};\n  while(1) {\n    if(timeout != -1) {\n      get_time(&tv1);\n    }\n    int rv = poll(&pfd, 1, timeout);\n    if(rv == 0) {\n      return -2;\n    } else if(rv == -1) {\n      return -1;\n    }\n    ERR_clear_error();\n    rv = SSL_connect(ssl);\n    if(rv == 0) {\n      std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n      return -1;\n    } else if(rv < 0) {\n      if(timeout != -1) {\n        get_time(&tv2);\n        timeout -= time_delta(tv2, tv1);\n        if(timeout <= 0) {\n          return -2;\n        }\n      }\n      switch(SSL_get_error(ssl, rv)) {\n      case SSL_ERROR_WANT_READ:\n        pfd.events = POLLIN;\n        break;\n      case SSL_ERROR_WANT_WRITE:\n        pfd.events = POLLOUT;\n        break;\n      default:\n        std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl;\n        return -1;\n      }\n    } else {\n      break;\n    }\n  }\n  return 0;\n}\n\nint64_t time_delta(const timeval& a, const timeval& b)\n{\n  int64_t res = (a.tv_sec - b.tv_sec) * 1000;\n  res += (a.tv_usec - b.tv_usec) / 1000;\n  return res;\n}\n\nuint8_t get_ssl_io_demand(SSL *ssl, ssize_t r)\n{\n  switch(SSL_get_error(ssl, r)) {\n  case SSL_ERROR_WANT_WRITE:\n    return WANT_WRITE;\n  case SSL_ERROR_WANT_READ:\n    return WANT_READ;\n  default:\n    return 0;\n  }\n}\n\nnamespace {\ntimeval base_tv;\n} // namespace\n\nvoid reset_timer()\n{\n  get_time(&base_tv);\n}\n\nvoid get_timer(timeval* tv)\n{\n  get_time(tv);\n  tv->tv_usec -= base_tv.tv_usec;\n  tv->tv_sec -= base_tv.tv_sec;\n  if(tv->tv_usec < 0) {\n    tv->tv_usec += 1000000;\n    --tv->tv_sec;\n  }\n}\n\nint get_time(timeval *tv)\n{\n  int rv;\n#ifdef HAVE_CLOCK_GETTIME\n  timespec ts;\n  rv = clock_gettime(CLOCK_MONOTONIC, &ts);\n  tv->tv_sec = ts.tv_sec;\n  tv->tv_usec = ts.tv_nsec/1000;\n#else // !HAVE_CLOCK_GETTIME\n  rv = gettimeofday(tv, 0);\n#endif // !HAVE_CLOCK_GETTIME\n  return rv;\n}\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/spdylay_ssl.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_SSL_H\n#define SPDYLAY_SSL_H\n\n#include \"spdylay_config.h\"\n\n#include <stdint.h>\n#include <cstdlib>\n#include <sys/time.h>\n#include <poll.h>\n#include <vector>\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <spdylay/spdylay.h>\n\nnamespace spdylay {\n\nextern bool ssl_debug;\n\nclass Spdylay {\npublic:\n  Spdylay(int fd, SSL *ssl, uint16_t version,\n          const spdylay_session_callbacks *callbacks,\n          void *user_data);\n  ~Spdylay();\n  void clear_io_flags();\n  int recv();\n  int send();\n  ssize_t send_data(const uint8_t *data, size_t len, int flags);\n  ssize_t recv_data(uint8_t *data, size_t len, int flags);\n  bool want_read();\n  bool want_write();\n  bool finish();\n  int fd() const;\n  int submit_request\n  (const std::string& scheme,\n   const std::string& hostport, const std::string& path,\n   const std::vector<std::pair<std::string, std::string> >& headers,\n   uint8_t pri,\n   const spdylay_data_provider *data_prd,\n   int64_t data_length,\n   void *stream_user_data,\n   bool useProxy,\n   const std::string& proxyHost,\n   uint16_t proxyPort);\n  int submit_settings(int flags, spdylay_settings_entry *iv, size_t niv);\n  bool would_block();\n  void* user_data();\nprivate:\n  SSL *ssl_;\n  spdylay_session *session_;\n  void *user_data_;\n  int fd_;\n  uint16_t version_;\n  uint8_t io_flags_;\n};\n\nint connect_to(const std::string& host, uint16_t port);\n\nint nonblock_connect_to(const std::string& host, uint16_t port, int timeout);\n\nint make_listen_socket(const std::string& host, uint16_t port, int family);\n\nint make_non_block(int fd);\n\nint set_tcp_nodelay(int fd);\n\nssize_t send_callback(spdylay_session *session,\n                      const uint8_t *data, size_t len, int flags,\n                      void *user_data);\n\nssize_t recv_callback(spdylay_session *session,\n                      uint8_t *data, size_t len, int flags, void *user_data);\n\nvoid print_nv(char **nv);\n\nvoid on_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data);\n\nvoid on_invalid_ctrl_recv_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n uint32_t status_code, void *user_data);\n\nvoid on_ctrl_recv_parse_error_callback(spdylay_session *session,\n                                       spdylay_frame_type type,\n                                       const uint8_t *head,\n                                       size_t headlen,\n                                       const uint8_t *payload,\n                                       size_t payloadlen,\n                                       int error_code, void *user_data);\n\nvoid on_unknown_ctrl_recv_callback(spdylay_session *session,\n                                   const uint8_t *head,\n                                   size_t headlen,\n                                   const uint8_t *payload,\n                                   size_t payloadlen,\n                                   void *user_data);\n\nvoid on_ctrl_send_callback\n(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,\n void *user_data);\n\nvoid on_data_recv_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data);\n\nvoid on_data_send_callback\n(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length,\n void *user_data);\n\nvoid ctl_poll(pollfd *pollfd, Spdylay *sc);\n\nint select_next_proto_cb(SSL* ssl,\n                         unsigned char **out, unsigned char *outlen,\n                         const unsigned char *in, unsigned int inlen,\n                         void *arg);\n\nvoid setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg);\n\nint ssl_handshake(SSL *ssl, int fd);\n\nint ssl_nonblock_handshake(SSL *ssl, int fd, int& timeout);\n\n// Returns difference between |a| and |b| in milliseconds, assuming\n// |a| is more recent than |b|.\nint64_t time_delta(const timeval& a, const timeval& b);\n\nvoid reset_timer();\n\nvoid get_timer(timeval *tv);\n\nint get_time(timeval *tv);\n\nvoid print_timer();\n\nenum {\n  WANT_READ = 1,\n  WANT_WRITE = 1 << 1\n};\n\nuint8_t get_ssl_io_demand(SSL *ssl, ssize_t r);\n\n// Setting true will print characters with ANSI color escape codes\n// when printing SPDY frames. This function changes a static variable.\nvoid set_color_output(bool f);\n\n} // namespace spdylay\n\n#endif // SPDYLAY_SSL_H\n"
  },
  {
    "path": "src/timegm.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"timegm.h\"\n\n#ifndef HAVE_TIMEGM\n\n#include <stdint.h>\n\n/* Counter the number of leap year in the range [0, y). The |y| is the\n   year, including century (e.g., 2012) */\nstatic int count_leap_year(int y)\n{\n  y -= 1;\n  return y/4-y/100+y/400;\n}\n\n/* Returns nonzero if the |y| is the leap year. The |y| is the year,\n   including century (e.g., 2012) */\nstatic int is_leap_year(int y)\n{\n  return y%4 == 0 && (y%100 != 0 || y%400 == 0);\n}\n\n/* The number of days before ith month begins */\nstatic int daysum[] = {\n  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334\n};\n\n/* Based on the algorithm of Python 2.7 calendar.timegm. */\ntime_t timegm(struct tm *tm)\n{\n  int days;\n  int num_leap_year;\n  int64_t t;\n  if(tm->tm_mon > 11) {\n    return -1;\n  }\n  num_leap_year = count_leap_year(tm->tm_year + 1900) - count_leap_year(1970);\n  days = (tm->tm_year - 70) * 365 +\n    num_leap_year + daysum[tm->tm_mon] + tm->tm_mday-1;\n  if(tm->tm_mon >= 2 && is_leap_year(tm->tm_year + 1900)) {\n    ++days;\n  }\n  t = ((int64_t)days * 24 + tm->tm_hour) * 3600 + tm->tm_min * 60 + tm->tm_sec;\n  if(sizeof(time_t) == 4) {\n    if(t < INT32_MIN || t > INT32_MAX) {\n      return -1;\n    }\n  }\n  return t;\n}\n\n#endif /* !HAVE_TIMEGM */\n"
  },
  {
    "path": "src/timegm.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2013 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef TIMEGM_H\n#define TIMEGM_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif // HAVE_CONFIG_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\n#include <time.h>\n\n#ifndef HAVE_TIMEGM\n\ntime_t timegm(struct tm *tm);\n\n#endif /* HAVE_TIMEGM */\n\n#ifdef __cplusplus\n}\n#endif /* __cplusplus */\n\n#endif /* TIMEGM_H */\n"
  },
  {
    "path": "src/util.cc",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"util.h\"\n\n#include <time.h>\n\n#include <cstdio>\n#include <cstring>\n\n#include \"timegm.h\"\n\nnamespace spdylay {\n\nnamespace util {\n\nconst char DEFAULT_STRIP_CHARSET[] = \"\\r\\n\\t \";\n\nbool isAlpha(const char c)\n{\n  return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');\n}\n\nbool isDigit(const char c)\n{\n  return '0' <= c && c <= '9';\n}\n\nbool isHexDigit(const char c)\n{\n  return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');\n}\n\nbool inRFC3986UnreservedChars(const char c)\n{\n  static const char unreserved[] = { '-', '.', '_', '~' };\n  return isAlpha(c) || isDigit(c) ||\n    std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4];\n}\n\nstd::string percentEncode(const unsigned char* target, size_t len)\n{\n  std::string dest;\n  for(size_t i = 0; i < len; ++i) {\n    if(inRFC3986UnreservedChars(target[i])) {\n      dest += target[i];\n    } else {\n      char temp[4];\n      snprintf(temp, sizeof(temp), \"%%%02X\", target[i]);\n      dest.append(temp);\n      //dest.append(fmt(\"%%%02X\", target[i]));\n    }\n  }\n  return dest;\n}\n\nstd::string percentEncode(const std::string& target)\n{\n  return percentEncode(reinterpret_cast<const unsigned char*>(target.c_str()),\n                       target.size());\n}\n\nstd::string percentDecode\n(std::string::const_iterator first, std::string::const_iterator last)\n{\n  std::string result;\n  for(; first != last; ++first) {\n    if(*first == '%') {\n      if(first+1 != last && first+2 != last &&\n         isHexDigit(*(first+1)) && isHexDigit(*(first+2))) {\n        std::string numstr(first+1, first+3);\n        result += strtol(numstr.c_str(), 0, 16);\n        first += 2;\n      } else {\n        result += *first;\n      }\n    } else {\n      result += *first;\n    }\n  }\n  return result;\n}\n\nstd::string http_date(time_t t)\n{\n  char buf[32];\n  tm* tms = gmtime(&t); // returned struct is statically allocated.\n  size_t r = strftime(buf, sizeof(buf), \"%a, %d %b %Y %H:%M:%S GMT\", tms);\n  return std::string(&buf[0], &buf[r]);\n}\n\ntime_t parse_http_date(const std::string& s)\n{\n  tm tm;\n  memset(&tm, 0, sizeof(tm));\n  char* r = strptime(s.c_str(), \"%a, %d %b %Y %H:%M:%S GMT\", &tm);\n  if(r == 0) {\n    return 0;\n  }\n  return timegm(&tm);\n}\n\nbool startsWith(const std::string& a, const std::string& b)\n{\n  return startsWith(a.begin(), a.end(), b.begin(), b.end());\n}\n\nbool istartsWith(const std::string& a, const std::string& b)\n{\n  return istartsWith(a.begin(), a.end(), b.begin(), b.end());\n}\n\nnamespace {\nvoid streq_advance(const char **ap, const char **bp)\n{\n  for(; **ap && **bp && lowcase(**ap) == lowcase(**bp); ++*ap, ++*bp);\n}\n} // namespace\n\nbool istartsWith(const char *a, const char* b)\n{\n  if(!a || !b) {\n    return false;\n  }\n  streq_advance(&a, &b);\n  return !*b;\n}\n\nbool endsWith(const std::string& a, const std::string& b)\n{\n  return endsWith(a.begin(), a.end(), b.begin(), b.end());\n}\nbool strieq(const char *a, const char *b)\n{\n  if(!a || !b) {\n    return false;\n  }\n  for(; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b);\n  return !*a && !*b;\n}\n\nbool strifind(const char *a, const char *b)\n{\n  if(!a || !b) {\n    return false;\n  }\n  for(size_t i = 0; a[i]; ++i) {\n    const char *ap = &a[i], *bp = b;\n    for(; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp);\n    if(!*bp) {\n      return true;\n    }\n  }\n  return false;\n}\n\nchar upcase(char c)\n{\n  if('a' <= c && c <= 'z') {\n    return c-'a'+'A';\n  } else {\n    return c;\n  }\n}\n\n} // namespace util\n\n} // namespace spdylay\n"
  },
  {
    "path": "src/util.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef UTIL_H\n#define UTIL_H\n\n#include \"spdylay_config.h\"\n\n#include <cstring>\n#include <vector>\n#include <string>\n#include <algorithm>\n#include <sstream>\n\nnamespace spdylay {\n\nnamespace util {\n\ntemplate<typename T>\nclass auto_delete {\nprivate:\n  T obj_;\n  void (*deleter_)(T);\npublic:\n  auto_delete(T obj, void (*deleter)(T)):obj_(obj), deleter_(deleter) {}\n\n  ~auto_delete()\n  {\n    deleter_(obj_);\n  }\n};\n\ntemplate<typename T>\nclass auto_delete_d {\nprivate:\n  T obj_;\npublic:\n  auto_delete_d(T obj):obj_(obj) {}\n\n  ~auto_delete_d()\n  {\n    delete obj_;\n  }\n};\n\ntemplate<typename T, typename R>\nclass auto_delete_r {\nprivate:\n  T obj_;\n  R (*deleter_)(T);\npublic:\n  auto_delete_r(T obj, R (*deleter)(T)):obj_(obj), deleter_(deleter) {}\n\n  ~auto_delete_r()\n  {\n    (void)deleter_(obj_);\n  }\n};\n\nextern const char DEFAULT_STRIP_CHARSET[];\n\ntemplate<typename InputIterator>\nstd::pair<InputIterator, InputIterator> stripIter\n(InputIterator first, InputIterator last,\n const char* chars = DEFAULT_STRIP_CHARSET)\n{\n  for(; first != last && strchr(chars, *first) != 0; ++first);\n  if(first == last) {\n    return std::make_pair(first, last);\n  }\n  InputIterator left = last-1;\n  for(; left != first && strchr(chars, *left) != 0; --left);\n  return std::make_pair(first, left+1);\n}\n\ntemplate<typename InputIterator, typename OutputIterator>\nOutputIterator splitIter\n(InputIterator first,\n InputIterator last,\n OutputIterator out,\n char delim,\n bool doStrip = false,\n bool allowEmpty = false)\n{\n  for(InputIterator i = first; i != last;) {\n    InputIterator j = std::find(i, last, delim);\n    std::pair<InputIterator, InputIterator> p(i, j);\n    if(doStrip) {\n      p = stripIter(i, j);\n    }\n    if(allowEmpty || p.first != p.second) {\n      *out++ = p;\n    }\n    i = j;\n    if(j != last) {\n      ++i;\n    }\n  }\n  if(allowEmpty &&\n     (first == last || *(last-1) == delim)) {\n    *out++ = std::make_pair(last, last);\n  }\n  return out;\n}\n\ntemplate<typename InputIterator, typename OutputIterator>\nOutputIterator split\n(InputIterator first,\n InputIterator last,\n OutputIterator out,\n char delim,\n bool doStrip = false,\n bool allowEmpty = false)\n{\n  for(InputIterator i = first; i != last;) {\n    InputIterator j = std::find(i, last, delim);\n    std::pair<InputIterator, InputIterator> p(i, j);\n    if(doStrip) {\n      p = stripIter(i, j);\n    }\n    if(allowEmpty || p.first != p.second) {\n      *out++ = std::string(p.first, p.second);\n    }\n    i = j;\n    if(j != last) {\n      ++i;\n    }\n  }\n  if(allowEmpty &&\n     (first == last || *(last-1) == delim)) {\n    *out++ = std::string(last, last);\n  }\n  return out;\n}\n\ntemplate<typename InputIterator, typename DelimiterType>\nstd::string strjoin(InputIterator first, InputIterator last,\n                    const DelimiterType& delim)\n{\n  std::string result;\n  if(first == last) {\n    return result;\n  }\n  InputIterator beforeLast = last-1;\n  for(; first != beforeLast; ++first) {\n    result += *first;\n    result += delim;\n  }\n  result += *beforeLast;\n  return result;\n}\n\ntemplate<typename InputIterator>\nstd::string joinPath(InputIterator first, InputIterator last)\n{\n  std::vector<std::string> elements;\n  for(;first != last; ++first) {\n    if(*first == \"..\") {\n      if(!elements.empty()) {\n        elements.pop_back();\n      }\n    } else if(*first == \".\") {\n      // do nothing\n    } else {\n      elements.push_back(*first);\n    }\n  }\n  return strjoin(elements.begin(), elements.end(), \"/\");\n}\n\nbool isAlpha(const char c);\n\nbool isDigit(const char c);\n\nbool isHexDigit(const char c);\n\nbool inRFC3986UnreservedChars(const char c);\n\nstd::string percentEncode(const unsigned char* target, size_t len);\n\nstd::string percentEncode(const std::string& target);\n\nstd::string percentDecode\n(std::string::const_iterator first, std::string::const_iterator last);\n\nstd::string http_date(time_t t);\n\ntime_t parse_http_date(const std::string& s);\n\ntemplate<typename T>\nstd::string to_str(T value)\n{\n  std::stringstream ss;\n  ss << value;\n  return ss.str();\n}\n\ntemplate<typename InputIterator1, typename InputIterator2>\nbool startsWith\n(InputIterator1 first1,\n InputIterator1 last1,\n InputIterator2 first2,\n InputIterator2 last2)\n{\n  if(last1-first1 < last2-first2) {\n    return false;\n  }\n  return std::equal(first2, last2, first1);\n}\n\nbool startsWith(const std::string& a, const std::string& b);\n\nstruct CaseCmp {\n  bool operator()(char lhs, char rhs) const\n  {\n    if('A' <= lhs && lhs <= 'Z') {\n      lhs += 'a'-'A';\n    }\n    if('A' <= rhs && rhs <= 'Z') {\n      rhs += 'a'-'A';\n    }\n    return lhs == rhs;\n  }\n};\n\ntemplate<typename InputIterator1, typename InputIterator2>\nbool istartsWith\n(InputIterator1 first1,\n InputIterator1 last1,\n InputIterator2 first2,\n InputIterator2 last2)\n{\n  if(last1-first1 < last2-first2) {\n    return false;\n  }\n  return std::equal(first2, last2, first1, CaseCmp());\n}\n\nbool istartsWith(const std::string& a, const std::string& b);\nbool istartsWith(const char *a, const char* b);\n\ntemplate<typename InputIterator1, typename InputIterator2>\nbool endsWith\n(InputIterator1 first1,\n InputIterator1 last1,\n InputIterator2 first2,\n InputIterator2 last2)\n{\n  if(last1-first1 < last2-first2) {\n    return false;\n  }\n  return std::equal(first2, last2, last1-(last2-first2));\n}\n\ntemplate<typename InputIterator1, typename InputIterator2>\nbool iendsWith\n(InputIterator1 first1,\n InputIterator1 last1,\n InputIterator2 first2,\n InputIterator2 last2)\n{\n  if(last1-first1 < last2-first2) {\n    return false;\n  }\n  return std::equal(first2, last2, last1-(last2-first2), CaseCmp());\n}\n\nbool endsWith(const std::string& a, const std::string& b);\n\nbool strieq(const std::string& a, const std::string& b);\n\nbool strieq(const char *a, const char *b);\n\nbool strifind(const char *a, const char *b);\n\nchar upcase(char c);\n\nchar lowcase(char c);\n\ninline char lowcase(char c)\n{\n  static unsigned char tbl[] = {\n    0, 1, 2, 3, 4, 5, 6, 7,\n    8, 9, 10, 11, 12, 13, 14, 15,\n    16, 17, 18, 19, 20, 21, 22, 23,\n    24, 25, 26, 27, 28, 29, 30, 31,\n    32, 33, 34, 35, 36, 37, 38, 39,\n    40, 41, 42, 43, 44, 45, 46, 47,\n    48, 49, 50, 51, 52, 53, 54, 55,\n    56, 57, 58, 59, 60, 61, 62, 63,\n    64, 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',\n    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\n    'x', 'y', 'z', 91, 92, 93, 94, 95,\n    96, 97, 98, 99, 100, 101, 102, 103,\n    104, 105, 106, 107, 108, 109, 110, 111,\n    112, 113, 114, 115, 116, 117, 118, 119,\n    120, 121, 122, 123, 124, 125, 126, 127,\n    128, 129, 130, 131, 132, 133, 134, 135,\n    136, 137, 138, 139, 140, 141, 142, 143,\n    144, 145, 146, 147, 148, 149, 150, 151,\n    152, 153, 154, 155, 156, 157, 158, 159,\n    160, 161, 162, 163, 164, 165, 166, 167,\n    168, 169, 170, 171, 172, 173, 174, 175,\n    176, 177, 178, 179, 180, 181, 182, 183,\n    184, 185, 186, 187, 188, 189, 190, 191,\n    192, 193, 194, 195, 196, 197, 198, 199,\n    200, 201, 202, 203, 204, 205, 206, 207,\n    208, 209, 210, 211, 212, 213, 214, 215,\n    216, 217, 218, 219, 220, 221, 222, 223,\n    224, 225, 226, 227, 228, 229, 230, 231,\n    232, 233, 234, 235, 236, 237, 238, 239,\n    240, 241, 242, 243, 244, 245, 246, 247,\n    248, 249, 250, 251, 252, 253, 254, 255,\n  };\n  return tbl[static_cast<unsigned char>(c)];\n}\n\ntemplate<typename T>\nstd::string utos(T n)\n{\n  std::string res;\n  if(n == 0) {\n    res = \"0\";\n    return res;\n  }\n  int i = 0;\n  T t = n;\n  for(; t; t /= 10, ++i);\n  res.resize(i);\n  --i;\n  for(; n; --i, n /= 10) {\n    res[i] = (n%10) + '0';\n  }\n  return res;\n}\n\n} // namespace util\n\n} // namespace spdylay\n\n#endif // UTIL_H\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "end_to_end.py.log\nend_to_end.py.trs\nfailmalloc.log\nfailmalloc.trs\nmain.log\nmain.trs\ntest-suite.log\n"
  },
  {
    "path": "tests/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nSUBDIRS = testdata\n\nif HAVE_CUNIT\n\ncheck_PROGRAMS = main failmalloc\n\nOBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \\\n\tspdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \\\n\tspdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c \\\n\tspdylay_gzip_test.c \\\n\tspdylay_test_helper.c\n\nHFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \\\n\tspdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \\\n\tspdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h \\\n\tspdylay_gzip_test.h \\\n\tspdylay_test_helper.h\n\nmain_SOURCES = $(HFILES) $(OBJECTS)\n\nmain_LDADD = ${top_builddir}/lib/libspdylay.la\nmain_LDFLAGS = -static @CUNIT_LIBS@ @TESTS_LIBS@\n\nfailmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \\\n\tmalloc_wrapper.c malloc_wrapper.h \\\n\tspdylay_test_helper.c spdylay_test_helper.h\nfailmalloc_LDADD = $(main_LDADD)\nfailmalloc_LDFLAGS = $(main_LDFLAGS)\n\nAM_CFLAGS = -Wall -I${top_srcdir}/lib -I${top_srcdir}/lib/includes -I${top_builddir}/lib/includes \\\n\t@CUNIT_CFLAGS@ @DEFS@\n\nTESTS = main failmalloc\n\nif ENABLE_SRC\n\nEXTRA_DIST = end_to_end.py\nTESTS += end_to_end.py\n\nendif # ENABLE_SRC\n\nendif # HAVE_CUNIT\n"
  },
  {
    "path": "tests/end_to_end.py",
    "content": "#!/usr/bin/env python\n\"\"\"End to end tests for the utility programs.\n\nThis test assumes the utilities inside src directory have already been\nbuilt.\n\nAt the moment top_buiddir is not in the environment, but top_builddir would be\nmore reliable than '..', so it's worth trying to pull it from the environment.\n\"\"\"\n\n__author__ = 'Jim Morrison <jim@twist.com>'\n\n\nimport os\nimport subprocess\nimport time\nimport unittest\n\n\n_PORT = 9893\n\n\ndef _run_server(port, args):\n  srcdir = os.environ.get('srcdir', '.')\n  testdata = '%s/testdata' % srcdir\n  top_builddir = os.environ.get('top_builddir', '..')\n  base_args = ['%s/src/spdyd' % top_builddir, '-d', testdata]\n  if args:\n    base_args.extend(args)\n  base_args.extend([str(port), '%s/privkey.pem' % testdata,\n                    '%s/cacert.pem' % testdata])\n  return subprocess.Popen(base_args)\n\ndef _check_server_up(port):\n  # Check this check for now.\n  time.sleep(1)\n\ndef _kill_server(server):\n  while server.returncode is None:\n    server.terminate()\n    time.sleep(1)\n    server.poll()\n\n\nclass EndToEndSpdyTests(unittest.TestCase):\n  @classmethod\n  def setUpClass(cls):\n    cls.setUpServer([])\n\n  @classmethod\n  def setUpServer(cls, args):\n    cls.server = _run_server(_PORT, args)\n    _check_server_up(_PORT)\n\n  @classmethod\n  def tearDownClass(cls):\n    _kill_server(cls.server)\n\n  def setUp(self):\n    build_dir = os.environ.get('top_builddir', '..')\n    self.client = '%s/src/spdycat' % build_dir\n    self.stdout = 'No output'\n\n  def call(self, path, args):\n     full_args = [self.client,'http://localhost:%d%s' % (_PORT, path)] + args\n     p = subprocess.Popen(full_args, stdout=subprocess.PIPE,\n                          stdin=subprocess.PIPE)\n     self.stdout, self.stderr = p.communicate()\n     return p.returncode\n\n\nclass EndToEndSpdy2Tests(EndToEndSpdyTests):\n  def testSimpleRequest(self):\n    self.assertEquals(0, self.call('/', []))\n\n  def testSimpleRequestSpdy3(self):\n    self.assertEquals(0, self.call('/', ['-v', '-3']))\n    self.assertIn('NPN selected the protocol: spdy/3', self.stdout)\n\n  def testFailedRequests(self):\n    self.assertEquals(\n        2, self.call('/', ['https://localhost:25/', 'http://localhost:79']))\n\n  def testOneFailedRequest(self):\n    self.assertEquals(1, subprocess.call([self.client, 'http://localhost:2/']))\n\n  def testOneTimedOutRequest(self):\n    self.assertEquals(1, self.call('/?spdyd_do_not_respond_to_req=yes',\n                                   ['--timeout=2']))\n    self.assertEquals(0, self.call('/', ['--timeout=20']))\n\n\nclass EndToEndSpdy3Tests(EndToEndSpdyTests):\n  @classmethod\n  def setUpClass(cls):\n    cls.setUpServer(['-3'])\n\n  def testSimpleRequest(self):\n    self.assertEquals(0, self.call('/', ['-v']))\n    self.assertIn('NPN selected the protocol: spdy/3', self.stdout)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "tests/failmalloc.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include <stdio.h>\n#include <string.h>\n#include <CUnit/Basic.h>\n#include \"config.h\"\n/* include test cases' include files here */\n#include \"failmalloc_test.h\"\n\nstatic int init_suite1(void)\n{\n  return 0;\n}\n\nstatic int clean_suite1(void)\n{\n  return 0;\n}\n\nint main(int argc _U_, char* argv[] _U_)\n{\n   CU_pSuite pSuite = NULL;\n   unsigned int num_tests_failed;\n\n   /* initialize the CUnit test registry */\n   if (CUE_SUCCESS != CU_initialize_registry())\n      return CU_get_error();\n\n   /* add a suite to the registry */\n   pSuite = CU_add_suite(\"libspdylay_TestSuite\", init_suite1, clean_suite1);\n   if (NULL == pSuite) {\n      CU_cleanup_registry();\n      return CU_get_error();\n   }\n\n   /* add the tests to the suite */\n   if(!CU_add_test(pSuite, \"failmalloc_session_send\",\n                   test_spdylay_session_send) ||\n      !CU_add_test(pSuite, \"failmalloc_session_recv\",\n                   test_spdylay_session_recv) ||\n      !CU_add_test(pSuite, \"failmalloc_frame\", test_spdylay_frame)) {\n     CU_cleanup_registry();\n     return CU_get_error();\n   }\n\n   /* Run all tests using the CUnit Basic interface */\n   CU_basic_set_mode(CU_BRM_VERBOSE);\n   CU_basic_run_tests();\n   num_tests_failed = CU_get_number_of_tests_failed();\n   CU_cleanup_registry();\n   if(CU_get_error() == CUE_SUCCESS) {\n     return num_tests_failed;\n   } else {\n     printf(\"CUnit Error: %s\\n\", CU_get_error_msg());\n     return CU_get_error();\n   }\n}\n"
  },
  {
    "path": "tests/failmalloc_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"failmalloc_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"spdylay_session.h\"\n#include \"spdylay_stream.h\"\n#include \"spdylay_frame.h\"\n#include \"spdylay_helper.h\"\n#include \"malloc_wrapper.h\"\n#include \"spdylay_test_helper.h\"\n\ntypedef struct {\n  uint8_t data[8192];\n  uint8_t *datamark, *datalimit;\n} data_feed;\n\ntypedef struct {\n  data_feed *df;\n  size_t data_source_length;\n} my_user_data;\n\nstatic void data_feed_init(data_feed *df, uint8_t *data, size_t data_length)\n{\n  assert(data_length <= sizeof(df->data));\n  memcpy(df->data, data, data_length);\n  df->datamark = df->data;\n  df->datalimit = df->data+data_length;\n}\n\nstatic ssize_t null_send_callback(spdylay_session *session _U_,\n                                  const uint8_t* data _U_, size_t len, int flags _U_,\n                                  void *user_data  _U_)\n{\n  return len;\n}\n\nstatic ssize_t data_feed_recv_callback(spdylay_session *session _U_,\n                                       uint8_t* data, size_t len, int flags _U_,\n                                       void *user_data)\n{\n  data_feed *df = ((my_user_data*)user_data)->df;\n  size_t avail = df->datalimit - df->datamark;\n  size_t wlen = spdylay_min(avail, len);\n  memcpy(data, df->datamark, wlen);\n  df->datamark += wlen;\n  return wlen;\n}\n\nstatic ssize_t fixed_length_data_source_read_callback\n(spdylay_session *session _U_, int32_t stream_id _U_,\n uint8_t *buf _U_, size_t len, int *eof,\n spdylay_data_source *source _U_, void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  size_t wlen;\n  if(len < ud->data_source_length) {\n    wlen = len;\n  } else {\n    wlen = ud->data_source_length;\n  }\n  ud->data_source_length -= wlen;\n  if(ud->data_source_length == 0) {\n    *eof = 1;\n  }\n  return wlen;\n}\n\n#define TEST_FAILMALLOC_RUN(FUN)                        \\\n  size_t nmalloc, i;                                    \\\n                                                        \\\n  spdylay_failmalloc = 0;                               \\\n  spdylay_nmalloc = 0;                                  \\\n  FUN();                                                \\\n  nmalloc = spdylay_nmalloc;                            \\\n                                                        \\\n  spdylay_failmalloc = 1;                               \\\n  for(i = 0; i < nmalloc; ++i) {                        \\\n    spdylay_nmalloc = 0;                                \\\n    spdylay_failstart = (int)i;                         \\\n    /* printf(\"i=%zu\\n\", i); */                         \\\n    FUN();                                              \\\n    /* printf(\"nmalloc=%d\\n\", spdylay_nmalloc); */      \\\n  }                                                     \\\n  spdylay_failmalloc = 0;\n\nstatic void run_spdylay_session_send(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \":host\", \"example.org\",\n                       \":scheme\", \"https\",\n                       NULL };\n  spdylay_data_provider data_prd;\n  spdylay_settings_entry iv[2];\n  my_user_data ud;\n  int rv;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n  ud.data_source_length = 64*1024;\n\n  iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE;\n  iv[0].value = 256;\n  iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[1].value = 100;\n\n  rv = spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,\n                                  &callbacks, &ud);\n  if(rv != 0) {\n    goto client_new_fail;\n  }\n  rv = spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_NONE,\n                                 0, 3, nv, NULL);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_session_send(session);\n  if(rv != 0) {\n    goto fail;\n  }\n  /* The SYN_STREAM submitted by the previous\n     spdylay_submit_syn_stream will have stream ID 3. Send HEADERS to\n     that stream. */\n  rv = spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_NONE, 3, nv);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_data(session, 3, SPDYLAY_DATA_FLAG_FIN, &data_prd);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_session_send(session);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_rst_stream(session, 3, SPDYLAY_CANCEL);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_session_send(session);\n  if(rv != 0) {\n    goto fail;\n  }\n  /* Sending against half-closed stream */\n  rv = spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_NONE, 3, nv);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_data(session, 3, SPDYLAY_DATA_FLAG_FIN, &data_prd);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_ping(session);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_settings(session, SPDYLAY_FLAG_SETTINGS_NONE, iv, 2);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_session_send(session);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);\n  if(rv != 0) {\n    goto fail;\n  }\n  rv = spdylay_session_send(session);\n  if(rv != 0) {\n    goto fail;\n  }\n fail:\n  spdylay_session_del(session);\n client_new_fail:\n  ;\n}\n\nvoid test_spdylay_session_send(void)\n{\n  TEST_FAILMALLOC_RUN(run_spdylay_session_send);\n}\n\nstatic void run_spdylay_session_recv(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  spdylay_zlib deflater;\n  spdylay_frame frame;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  const char *nv[] = { \":host\", \"example.org\",\n                       \":scheme\", \"https\",\n                       NULL };\n  spdylay_settings_entry iv[2];\n  my_user_data ud;\n  data_feed df;\n  int rv;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.recv_callback = data_feed_recv_callback;\n  ud.df = &df;\n\n  spdylay_failmalloc_pause();\n  spdylay_zlib_deflate_hd_init(&deflater, 1, SPDYLAY_PROTO_SPDY3);\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);\n  spdylay_failmalloc_unpause();\n\n  /* SYN_STREAM */\n  spdylay_failmalloc_pause();\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY3,\n                                SPDYLAY_CTRL_FLAG_FIN, 1, 0, 2,\n                                spdylay_frame_nv_copy(nv));\n  framelen = spdylay_frame_pack_syn_stream(&buf, &buflen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream, &deflater);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  data_feed_init(&df, buf, framelen);\n  spdylay_failmalloc_unpause();\n\n  rv = spdylay_session_recv(session);\n  if(rv != 0) {\n    goto fail;\n  }\n\n  /* PING */\n  spdylay_failmalloc_pause();\n  spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY3, 1);\n  framelen = spdylay_frame_pack_ping(&buf, &buflen, &frame.ping);\n  spdylay_frame_ping_free(&frame.ping);\n  data_feed_init(&df, buf, framelen);\n  spdylay_failmalloc_unpause();\n\n  rv = spdylay_session_recv(session);\n  if(rv != 0) {\n    goto fail;\n  }\n\n  /* RST_STREAM */\n  spdylay_failmalloc_pause();\n  spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY3, 1,\n                                SPDYLAY_PROTOCOL_ERROR);\n  framelen = spdylay_frame_pack_rst_stream(&buf, &buflen, &frame.rst_stream);\n  spdylay_frame_rst_stream_free(&frame.rst_stream);\n  spdylay_failmalloc_unpause();\n\n  rv = spdylay_session_recv(session);\n  if(rv != 0) {\n    goto fail;\n  }\n\n  /* SETTINGS */\n  spdylay_failmalloc_pause();\n  iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE;\n  iv[0].value = 256;\n  iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[1].value = 100;\n  spdylay_frame_settings_init(&frame.settings, SPDYLAY_PROTO_SPDY3,\n                              SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS,\n                              spdylay_frame_iv_copy(iv, 2), 2);\n  framelen = spdylay_frame_pack_settings(&buf, &buflen, &frame.settings);\n  spdylay_frame_settings_free(&frame.settings);\n  spdylay_failmalloc_unpause();\n\n  rv = spdylay_session_recv(session);\n  if(rv != 0) {\n    goto fail;\n  }\n\n fail:\n  free(buf);\n  free(nvbuf);\n  spdylay_session_del(session);\n  spdylay_zlib_deflate_free(&deflater);\n}\n\nvoid test_spdylay_session_recv(void)\n{\n  TEST_FAILMALLOC_RUN(run_spdylay_session_recv);\n}\n\nstatic void run_spdylay_frame_pack_syn_stream(void)\n{\n  spdylay_zlib deflater, inflater;\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  const char *nv[] = { \":host\", \"example.org\",\n                       \":scheme\", \"https\",\n                       NULL };\n  char **nv_copy;\n  int rv;\n\n  rv = spdylay_zlib_deflate_hd_init(&deflater, 1, SPDYLAY_PROTO_SPDY3);\n  if(rv != 0) {\n    goto deflate_init_fail;\n  }\n  rv = spdylay_zlib_inflate_hd_init(&inflater, SPDYLAY_PROTO_SPDY3);\n  if(rv != 0) {\n    goto inflate_init_fail;\n  }\n  nv_copy = spdylay_frame_nv_copy(nv);\n  if(nv_copy == NULL) {\n    goto nv_copy_fail;\n  }\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY3,\n                                SPDYLAY_CTRL_FLAG_FIN, 1, 0, 2, nv_copy);\n  framelen = spdylay_frame_pack_syn_stream(&buf, &buflen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream, &deflater);\n  if(framelen < 0) {\n    goto fail;\n  }\n  rv = (int)unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM, SPDYLAY_PROTO_SPDY3,\n                                  &oframe, &inflater, buf, framelen);\n  if(rv != 0) {\n    goto fail;\n  }\n  spdylay_frame_syn_stream_free(&oframe.syn_stream);\n fail:\n  free(buf);\n  free(nvbuf);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n nv_copy_fail:\n  spdylay_zlib_inflate_free(&inflater);\n inflate_init_fail:\n  spdylay_zlib_deflate_free(&deflater);\n deflate_init_fail:\n  ;\n}\n\nstatic void run_spdylay_frame_pack_ping(void)\n{\n  spdylay_frame frame;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY3, 1);\n  spdylay_frame_pack_ping(&buf, &buflen, &frame.ping);\n  free(buf);\n  spdylay_frame_ping_free(&frame.ping);\n}\n\nstatic void run_spdylay_frame_pack_goaway(void)\n{\n  spdylay_frame frame;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY3, 1000000007,\n                            SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n  spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway);\n  free(buf);\n  spdylay_frame_goaway_free(&frame.goaway);\n}\n\nstatic void run_spdylay_frame_pack_rst_stream(void)\n{\n  spdylay_frame frame;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY3, 1,\n                                SPDYLAY_PROTOCOL_ERROR);\n  spdylay_frame_pack_rst_stream(&buf, &buflen, &frame.rst_stream);\n  free(buf);\n  spdylay_frame_rst_stream_free(&frame.rst_stream);\n}\n\nstatic void run_spdylay_frame_pack_window_update(void)\n{\n  spdylay_frame frame;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1000000007, 4096);\n  spdylay_frame_pack_window_update(&buf, &buflen,\n                                   &frame.window_update);\n  free(buf);\n  spdylay_frame_window_update_free(&frame.window_update);\n}\n\nstatic void run_spdylay_frame_pack_settings(void)\n{\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  ssize_t framelen;\n  spdylay_settings_entry iv[2], *iv_copy;\n  int rv;\n\n  iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE;\n  iv[0].value = 256;\n  iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[1].value = 100;\n\n  iv_copy = spdylay_frame_iv_copy(iv, 2);\n  if(iv_copy == NULL) {\n    goto iv_copy_fail;\n  }\n  spdylay_frame_settings_init(&frame.settings, SPDYLAY_PROTO_SPDY3,\n                              SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS,\n                              iv_copy, 2);\n  framelen = spdylay_frame_pack_settings(&buf, &buflen, &frame.settings);\n  if(framelen < 0) {\n    goto fail;\n  }\n  rv = spdylay_frame_unpack_settings(&oframe.settings,\n                                     &buf[0], SPDYLAY_FRAME_HEAD_LENGTH,\n                                     &buf[SPDYLAY_FRAME_HEAD_LENGTH],\n                                     framelen-SPDYLAY_FRAME_HEAD_LENGTH);\n  if(rv != 0) {\n    goto fail;\n  }\n  spdylay_frame_settings_free(&oframe.settings);\n fail:\n  free(buf);\n  spdylay_frame_settings_free(&frame.settings);\n iv_copy_fail:\n  ;\n}\n\nstatic void run_spdylay_frame(void)\n{\n  run_spdylay_frame_pack_syn_stream();\n  run_spdylay_frame_pack_ping();\n  run_spdylay_frame_pack_goaway();\n  run_spdylay_frame_pack_rst_stream();\n  run_spdylay_frame_pack_window_update();\n  run_spdylay_frame_pack_settings();\n}\n\nvoid test_spdylay_frame(void)\n{\n  TEST_FAILMALLOC_RUN(run_spdylay_frame);\n}\n"
  },
  {
    "path": "tests/failmalloc_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef FAILMALLOC_TEST_H\n#define FAILMALLOC_TEST_H\n\nvoid test_spdylay_session_send(void);\nvoid test_spdylay_session_recv(void);\nvoid test_spdylay_frame(void);\n\n#endif /* FAILMALLOC_TEST_H */\n"
  },
  {
    "path": "tests/main.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include <stdio.h>\n#include <string.h>\n#include <CUnit/Basic.h>\n#include \"config.h\"\n/* include test cases' include files here */\n#include \"spdylay_pq_test.h\"\n#include \"spdylay_map_test.h\"\n#include \"spdylay_queue_test.h\"\n#include \"spdylay_buffer_test.h\"\n#include \"spdylay_zlib_test.h\"\n#include \"spdylay_session_test.h\"\n#include \"spdylay_frame_test.h\"\n#include \"spdylay_stream_test.h\"\n#include \"spdylay_npn_test.h\"\n#include \"spdylay_gzip_test.h\"\n\nstatic int init_suite1(void)\n{\n  return 0;\n}\n\nstatic int clean_suite1(void)\n{\n  return 0;\n}\n\n\nint main(int argc _U_, char* argv[] _U_)\n{\n   CU_pSuite pSuite = NULL;\n   unsigned int num_tests_failed;\n\n   /* initialize the CUnit test registry */\n   if (CUE_SUCCESS != CU_initialize_registry())\n      return CU_get_error();\n\n   /* add a suite to the registry */\n   pSuite = CU_add_suite(\"libspdylay_TestSuite\", init_suite1, clean_suite1);\n   if (NULL == pSuite) {\n      CU_cleanup_registry();\n      return CU_get_error();\n   }\n\n   /* add the tests to the suite */\n   if(!CU_add_test(pSuite, \"pq\", test_spdylay_pq) ||\n      !CU_add_test(pSuite, \"map\", test_spdylay_map) ||\n      !CU_add_test(pSuite, \"map_functional\", test_spdylay_map_functional) ||\n      !CU_add_test(pSuite, \"map_each_free\", test_spdylay_map_each_free) ||\n      !CU_add_test(pSuite, \"queue\", test_spdylay_queue) ||\n      !CU_add_test(pSuite, \"buffer\", test_spdylay_buffer) ||\n      !CU_add_test(pSuite, \"buffer_reader\", test_spdylay_buffer_reader) ||\n      !CU_add_test(pSuite, \"zlib_spdy2\", test_spdylay_zlib_spdy2) ||\n      !CU_add_test(pSuite, \"zlib_spdy3\", test_spdylay_zlib_spdy3) ||\n      !CU_add_test(pSuite, \"npn\", test_spdylay_npn) ||\n      !CU_add_test(pSuite, \"npn_get_proto_list\",\n                   test_spdylay_npn_get_proto_list) ||\n      !CU_add_test(pSuite, \"npn_get_version\", test_spdylay_npn_get_version) ||\n      !CU_add_test(pSuite, \"session_recv\", test_spdylay_session_recv) ||\n      !CU_add_test(pSuite, \"session_recv_invalid_stream_id\",\n                   test_spdylay_session_recv_invalid_stream_id) ||\n      !CU_add_test(pSuite, \"session_add_frame\",\n                   test_spdylay_session_add_frame) ||\n      !CU_add_test(pSuite, \"session_on_syn_stream_received\",\n                   test_spdylay_session_on_syn_stream_received) ||\n      !CU_add_test(pSuite, \"session_on_syn_stream_received_with_push\",\n                   test_spdylay_session_on_syn_stream_received_with_push) ||\n      !CU_add_test(pSuite, \"session_on_syn_reply_received\",\n                   test_spdylay_session_on_syn_reply_received) ||\n      !CU_add_test(pSuite, \"session_send_syn_stream\",\n                   test_spdylay_session_send_syn_stream) ||\n      !CU_add_test(pSuite, \"session_send_syn_reply\",\n                   test_spdylay_session_send_syn_reply) ||\n      !CU_add_test(pSuite, \"submit_response\", test_spdylay_submit_response) ||\n      !CU_add_test(pSuite, \"submit_response_without_data\",\n                   test_spdylay_submit_response_with_null_data_read_callback) ||\n      !CU_add_test(pSuite, \"submit_request_with_data\",\n                   test_spdylay_submit_request_with_data) ||\n      !CU_add_test(pSuite, \"submit_request_without_data\",\n                   test_spdylay_submit_request_with_null_data_read_callback) ||\n      !CU_add_test(pSuite, \"submit_syn_stream\",\n                   test_spdylay_submit_syn_stream) ||\n      !CU_add_test(pSuite, \"submit_syn_reply\", test_spdylay_submit_syn_reply) ||\n      !CU_add_test(pSuite, \"submit_headers\", test_spdylay_submit_headers) ||\n      !CU_add_test(pSuite, \"submit_invalid_nv\",\n                   test_spdylay_submit_invalid_nv) ||\n      !CU_add_test(pSuite, \"session_reply_fail\",\n                   test_spdylay_session_reply_fail) ||\n      !CU_add_test(pSuite, \"session_on_headers_received\",\n                   test_spdylay_session_on_headers_received) ||\n      !CU_add_test(pSuite, \"session_on_window_update_received\",\n                   test_spdylay_session_on_window_update_received) ||\n      !CU_add_test(pSuite, \"session_on_ping_received\",\n                   test_spdylay_session_on_ping_received) ||\n      !CU_add_test(pSuite, \"session_on_goaway_received\",\n                   test_spdylay_session_on_goaway_received) ||\n      !CU_add_test(pSuite, \"session_on_data_received\",\n                   test_spdylay_session_on_data_received) ||\n      !CU_add_test(pSuite, \"session_on_rst_stream_received\",\n                   test_spdylay_session_on_rst_received) ||\n      !CU_add_test(pSuite, \"session_is_my_stream_id\",\n                   test_spdylay_session_is_my_stream_id) ||\n      !CU_add_test(pSuite, \"session_send_rst_stream\",\n                   test_spdylay_session_send_rst_stream) ||\n      !CU_add_test(pSuite, \"session_get_next_ob_item\",\n                   test_spdylay_session_get_next_ob_item) ||\n      !CU_add_test(pSuite, \"session_pop_next_ob_item\",\n                   test_spdylay_session_pop_next_ob_item) ||\n      !CU_add_test(pSuite, \"session_on_request_recv_callback\",\n                   test_spdylay_session_on_request_recv_callback) ||\n      !CU_add_test(pSuite, \"session_on_stream_close\",\n                   test_spdylay_session_on_stream_close) ||\n      !CU_add_test(pSuite, \"session_max_concurrent_streams\",\n                   test_spdylay_session_max_concurrent_streams) ||\n      !CU_add_test(pSuite, \"session_data_backoff_by_high_pri_frame\",\n                   test_spdylay_session_data_backoff_by_high_pri_frame) ||\n      !CU_add_test(pSuite, \"session_stop_data_with_rst_stream\",\n                   test_spdylay_session_stop_data_with_rst_stream) ||\n      !CU_add_test(pSuite, \"session_stream_close_on_syn_stream\",\n                   test_spdylay_session_stream_close_on_syn_stream) ||\n      !CU_add_test(pSuite, \"session_recv_invalid_frame\",\n                   test_spdylay_session_recv_invalid_frame) ||\n      !CU_add_test(pSuite, \"session_defer_data\",\n                   test_spdylay_session_defer_data) ||\n      !CU_add_test(pSuite, \"session_flow_control\",\n                   test_spdylay_session_flow_control) ||\n      !CU_add_test(pSuite, \"session_connection_flow_control\",\n                   test_spdylay_session_connection_flow_control) ||\n      !CU_add_test(pSuite, \"session_on_ctrl_not_send\",\n                   test_spdylay_session_on_ctrl_not_send) ||\n      !CU_add_test(pSuite, \"session_on_settings_received\",\n                   test_spdylay_session_on_settings_received) ||\n      !CU_add_test(pSuite, \"session_submit_settings\",\n                   test_spdylay_submit_settings) ||\n      !CU_add_test(pSuite, \"session_get_outbound_queue_size\",\n                   test_spdylay_session_get_outbound_queue_size) ||\n      !CU_add_test(pSuite, \"session_set_option\",\n                   test_spdylay_session_set_option) ||\n      !CU_add_test(pSuite, \"submit_window_update\",\n                   test_spdylay_submit_window_update) ||\n      !CU_add_test(pSuite, \"session_data_read_temporal_failure\",\n                   test_spdylay_session_data_read_temporal_failure) ||\n      !CU_add_test(pSuite, \"session_recv_eof\",\n                   test_spdylay_session_recv_eof) ||\n      !CU_add_test(pSuite, \"session_recv_data\",\n                   test_spdylay_session_recv_data) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_spdy2\",\n                   test_spdylay_frame_unpack_nv_spdy2) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_spdy3\",\n                   test_spdylay_frame_unpack_nv_spdy3) ||\n      !CU_add_test(pSuite, \"frame_count_nv_space\",\n                   test_spdylay_frame_count_nv_space) ||\n      !CU_add_test(pSuite, \"frame_count_unpack_nv_space\",\n                   test_spdylay_frame_count_unpack_nv_space) ||\n      !CU_add_test(pSuite, \"frame_pack_ping\", test_spdylay_frame_pack_ping) ||\n      !CU_add_test(pSuite, \"frame_pack_goaway_spdy2\",\n                   test_spdylay_frame_pack_goaway_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_goaway_spdy3\",\n                   test_spdylay_frame_pack_goaway_spdy3) ||\n      !CU_add_test(pSuite, \"frame_pack_syn_stream_spdy2\",\n                   test_spdylay_frame_pack_syn_stream_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_syn_stream_spdy3\",\n                   test_spdylay_frame_pack_syn_stream_spdy3) ||\n      !CU_add_test(pSuite, \"frame_pack_syn_stream_frame_too_large\",\n                   test_spdylay_frame_pack_syn_stream_frame_too_large) ||\n      !CU_add_test(pSuite, \"frame_pack_syn_reply_spdy2\",\n                   test_spdylay_frame_pack_syn_reply_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_syn_reply_spdy3\",\n                   test_spdylay_frame_pack_syn_reply_spdy3) ||\n      !CU_add_test(pSuite, \"frame_pack_headers_spdy2\",\n                   test_spdylay_frame_pack_headers_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_headers_spdy3\",\n                   test_spdylay_frame_pack_headers_spdy3) ||\n      !CU_add_test(pSuite, \"frame_pack_window_update\",\n                   test_spdylay_frame_pack_window_update) ||\n      !CU_add_test(pSuite, \"frame_pack_settings_spdy2\",\n                   test_spdylay_frame_pack_settings_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_settings_spdy3\",\n                   test_spdylay_frame_pack_settings_spdy3) ||\n      !CU_add_test(pSuite, \"frame_nv_sort\", test_spdylay_frame_nv_sort) ||\n      !CU_add_test(pSuite, \"frame_nv_downcase\",\n                   test_spdylay_frame_nv_downcase) ||\n      !CU_add_test(pSuite, \"frame_pack_nv_duplicate_keys\",\n                   test_spdylay_frame_pack_nv_duplicate_keys) ||\n      !CU_add_test(pSuite, \"frame_pack_nv_empty_value_spdy2\",\n                   test_spdylay_frame_pack_nv_empty_value_spdy2) ||\n      !CU_add_test(pSuite, \"frame_pack_nv_empty_value_spdy3\",\n                   test_spdylay_frame_pack_nv_empty_value_spdy3) ||\n      !CU_add_test(pSuite, \"frame_nv_2to3\", test_spdylay_frame_nv_2to3) ||\n      !CU_add_test(pSuite, \"frame_nv_3to2\", test_spdylay_frame_nv_3to2) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_check_name_spdy2\",\n                   test_spdylay_frame_unpack_nv_check_name_spdy2) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_check_name_spdy3\",\n                   test_spdylay_frame_unpack_nv_check_name_spdy3) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_last_empty_value_spdy2\",\n                   test_spdylay_frame_unpack_nv_last_empty_value_spdy2) ||\n      !CU_add_test(pSuite, \"frame_unpack_nv_last_empty_value_spdy3\",\n                   test_spdylay_frame_unpack_nv_last_empty_value_spdy3) ||\n      !CU_add_test(pSuite, \"frame_nv_check_null\",\n                   test_spdylay_frame_nv_check_null) ||\n      !CU_add_test(pSuite, \"stream_add_pushed_stream\",\n                   test_spdylay_stream_add_pushed_stream) ||\n      !CU_add_test(pSuite, \"gzip_inflate\", test_spdylay_gzip_inflate)) {\n     CU_cleanup_registry();\n     return CU_get_error();\n   }\n\n   /* Run all tests using the CUnit Basic interface */\n   CU_basic_set_mode(CU_BRM_VERBOSE);\n   CU_basic_run_tests();\n   num_tests_failed = CU_get_number_of_tests_failed();\n   CU_cleanup_registry();\n   if(CU_get_error() == CUE_SUCCESS) {\n     return num_tests_failed;\n   } else {\n     printf(\"CUnit Error: %s\\n\", CU_get_error_msg());\n     return CU_get_error();\n   }\n}\n"
  },
  {
    "path": "tests/malloc_wrapper.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"malloc_wrapper.h\"\n\n#define __USE_GNU\n#include <dlfcn.h>\n\nint spdylay_failmalloc = 0;\nint spdylay_failstart = 0;\nint spdylay_countmalloc = 1;\nint spdylay_nmalloc = 0;\n\nstatic void* (*real_malloc)(size_t) = NULL;\n\nstatic void init(void)\n{\n  real_malloc = dlsym(RTLD_NEXT, \"malloc\");\n}\n\nvoid* malloc(size_t size)\n{\n  if(real_malloc == NULL) {\n    init();\n  }\n  if(spdylay_failmalloc && spdylay_nmalloc >= spdylay_failstart) {\n    return NULL;\n  } else {\n    if(spdylay_countmalloc) {\n      ++spdylay_nmalloc;\n    }\n    return real_malloc(size);\n  }\n}\n\nstatic int failmalloc_bk, countmalloc_bk;\n\nvoid spdylay_failmalloc_pause(void)\n{\n  failmalloc_bk = spdylay_failmalloc;\n  countmalloc_bk = spdylay_countmalloc;\n  spdylay_failmalloc = 0;\n  spdylay_countmalloc = 0;\n}\n\nvoid spdylay_failmalloc_unpause(void)\n{\n  spdylay_failmalloc = failmalloc_bk;\n  spdylay_countmalloc = countmalloc_bk;\n}\n"
  },
  {
    "path": "tests/malloc_wrapper.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef MALLOC_WRAPPER_H\n#define MALLOC_WRAPPER_H\n\n#include <stdlib.h>\n\n/* Global variables to control the behavior of malloc() */\n\n/* If nonzero, malloc failure mode is on */\nextern int spdylay_failmalloc;\n/* If spdylay_failstart <= spdylay_nmalloc and spdylay_failmalloc is\n   nonzero, malloc() fails. */\nextern int spdylay_failstart;\n/* If nonzero, spdylay_nmalloc is incremented if malloc() succeeds. */\nextern int spdylay_countmalloc;\n/* The number of successful invocation of malloc(). This value is only\n   incremented if spdylay_nmalloc is nonzero. */\nextern int spdylay_nmalloc;\n\nvoid* malloc(size_t size);\n\n/* Copies spdylay_failmalloc and spdylay_countmalloc to statically\n   allocated space and sets 0 to them. This will effectively make\n   malloc() work like normal malloc(). This is useful when you want to\n   disable malloc() failure mode temporarily. */\nvoid spdylay_failmalloc_pause(void);\n\n/* Restores the values of spdylay_failmalloc and spdylay_countmalloc\n   with the values saved by the previous\n   spdylay_failmalloc_pause(). */\nvoid spdylay_failmalloc_unpause(void);\n\n#endif /* MALLOC_WRAPPER_H */\n"
  },
  {
    "path": "tests/spdylay_buffer_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_buffer_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include <stdio.h>\n\n#include \"spdylay_buffer.h\"\n#include \"spdylay_net.h\"\n\nvoid test_spdylay_buffer(void)\n{\n  spdylay_buffer buffer;\n  uint8_t out[1024];\n  spdylay_buffer_init(&buffer, 8);\n  CU_ASSERT(0 == spdylay_buffer_length(&buffer));\n  CU_ASSERT(0 == spdylay_buffer_avail(&buffer));\n  CU_ASSERT(NULL == spdylay_buffer_get(&buffer));\n  CU_ASSERT(0 == spdylay_buffer_alloc(&buffer));\n\n  CU_ASSERT(8 == spdylay_buffer_avail(&buffer));\n  CU_ASSERT(NULL != spdylay_buffer_get(&buffer));\n  memcpy(spdylay_buffer_get(&buffer), \"012\", 3);\n  spdylay_buffer_advance(&buffer, 3);\n  CU_ASSERT(3 == spdylay_buffer_length(&buffer));\n\n  CU_ASSERT(5 == spdylay_buffer_avail(&buffer));\n  memcpy(spdylay_buffer_get(&buffer), \"34567\", 5);\n  spdylay_buffer_advance(&buffer, 5);\n  CU_ASSERT(8 == spdylay_buffer_length(&buffer));\n\n  CU_ASSERT(0 == spdylay_buffer_avail(&buffer));\n  CU_ASSERT(0 == spdylay_buffer_alloc(&buffer));\n  memcpy(spdylay_buffer_get(&buffer), \"89ABCDE\", 7);\n  spdylay_buffer_advance(&buffer, 7);\n  CU_ASSERT(15 == spdylay_buffer_length(&buffer));\n\n  CU_ASSERT(1 == spdylay_buffer_avail(&buffer));\n\n  spdylay_buffer_serialize(&buffer, out);\n  CU_ASSERT(0 == memcmp(\"0123456789ABCDE\", out, 15));\n\n  spdylay_buffer_reset(&buffer);\n\n  CU_ASSERT(0 == spdylay_buffer_length(&buffer));\n  CU_ASSERT(0 == spdylay_buffer_avail(&buffer));\n  CU_ASSERT(NULL == spdylay_buffer_get(&buffer));\n  CU_ASSERT(0 == spdylay_buffer_alloc(&buffer));\n\n  CU_ASSERT(8 == spdylay_buffer_avail(&buffer));\n  memcpy(spdylay_buffer_get(&buffer), \"Hello\", 5);\n  spdylay_buffer_advance(&buffer, 5);\n  CU_ASSERT(5 == spdylay_buffer_length(&buffer));\n\n  spdylay_buffer_serialize(&buffer, out);\n  CU_ASSERT(0 == memcmp(\"Hello\", out, 5));\n\n  spdylay_buffer_free(&buffer);\n}\n\nvoid test_spdylay_buffer_reader(void)\n{\n  spdylay_buffer buffer;\n  spdylay_buffer_reader reader;\n  uint16_t val16;\n  uint32_t val32;\n  uint8_t temp[256];\n\n  spdylay_buffer_init(&buffer, 3);\n  spdylay_buffer_write(&buffer, (const uint8_t*)\"hello\", 5);\n  val16 = htons(678);\n  spdylay_buffer_write(&buffer, (const uint8_t*)&val16, sizeof(uint16_t));\n  val32 = htonl(1000000007);\n  spdylay_buffer_write(&buffer, (const uint8_t*)&val32, sizeof(uint32_t));\n  spdylay_buffer_write(&buffer, (const uint8_t*)\"world\", 5);\n\n  CU_ASSERT(5+2+4+5 == spdylay_buffer_length(&buffer));\n\n  spdylay_buffer_reader_init(&reader, &buffer);\n\n  spdylay_buffer_reader_data(&reader, temp, 5);\n  CU_ASSERT(memcmp(temp, \"hello\", 5) == 0);\n  CU_ASSERT(678 == spdylay_buffer_reader_uint16(&reader));\n  CU_ASSERT(1000000007 == spdylay_buffer_reader_uint32(&reader));\n  CU_ASSERT('w' == spdylay_buffer_reader_uint8(&reader));\n  CU_ASSERT('o' == spdylay_buffer_reader_uint8(&reader));\n  CU_ASSERT('r' == spdylay_buffer_reader_uint8(&reader));\n  CU_ASSERT('l' == spdylay_buffer_reader_uint8(&reader));\n  CU_ASSERT('d' == spdylay_buffer_reader_uint8(&reader));\n\n  spdylay_buffer_reader_init(&reader, &buffer);\n  spdylay_buffer_reader_advance(&reader, 5);\n  CU_ASSERT(678 == spdylay_buffer_reader_uint16(&reader));\n  spdylay_buffer_reader_advance(&reader, 1);\n  spdylay_buffer_reader_advance(&reader, 1);\n  spdylay_buffer_reader_advance(&reader, 1);\n  spdylay_buffer_reader_advance(&reader, 1);\n  CU_ASSERT('w' == spdylay_buffer_reader_uint8(&reader));\n\n  spdylay_buffer_free(&buffer);\n}\n"
  },
  {
    "path": "tests/spdylay_buffer_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_BUFFER_TEST_H\n#define SPDYLAY_BUFFER_TEST_H\n\nvoid test_spdylay_buffer(void);\nvoid test_spdylay_buffer_reader(void);\n\n#endif /* SPDYLAY_BUFFER_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_frame_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_frame_test.h\"\n\n#include <assert.h>\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_frame.h\"\n#include \"spdylay_helper.h\"\n#include \"spdylay_test_helper.h\"\n\n/* Reads |len_size| byte from |data| as |len_size| byte network byte\n   order integer, and returns it in host byte order. Currently, we\n   only support len_size == 2 or 4 */\nstatic int get_packed_hd_len(uint8_t *data, size_t len_size)\n{\n  if(len_size == 2) {\n    return spdylay_get_uint16(data);\n  } else if(len_size == 4) {\n    return spdylay_get_uint32(data);\n  } else {\n    /* Not supported */\n    assert(0);\n    return 0;\n  }\n}\n\nstatic const char *headers[] = {\n  \"method\", \"GET\",\n  \"scheme\", \"https\",\n  \"url\", \"/\",\n  \"x-head\", \"foo\",\n  \"x-head\", \"bar\",\n  \"version\", \"HTTP/1.1\",\n  \"x-empty\", \"\",\n  NULL\n};\n\nstatic void test_spdylay_frame_unpack_nv_with(size_t len_size)\n{\n  uint8_t out[1024];\n  char **nv;\n  size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size);\n  spdylay_buffer buffer;\n\n  spdylay_buffer_init(&buffer, 4096);\n  spdylay_buffer_write(&buffer, out, inlen);\n\n  CU_ASSERT(0 == spdylay_frame_unpack_nv(&nv, &buffer, len_size));\n  CU_ASSERT(strcmp(\"method\", nv[0]) == 0);\n  CU_ASSERT(strcmp(\"GET\", nv[1]) == 0);\n  CU_ASSERT(strcmp(\"scheme\", nv[2]) == 0);\n  CU_ASSERT(strcmp(\"https\", nv[3]) == 0);\n  CU_ASSERT(strcmp(\"url\", nv[4]) == 0);\n  CU_ASSERT(strcmp(\"/\", nv[5]) == 0);\n  CU_ASSERT(strcmp(\"version\", nv[6]) == 0);\n  CU_ASSERT(strcmp(\"HTTP/1.1\", nv[7]) == 0);\n  CU_ASSERT(strcmp(\"x-empty\", nv[8]) == 0);\n  CU_ASSERT(strcmp(\"\", nv[9]) == 0);\n  CU_ASSERT(strcmp(\"x-head\", nv[10]) == 0);\n  CU_ASSERT(strcmp(\"foo\", nv[11]) == 0);\n  CU_ASSERT(strcmp(\"x-head\", nv[12]) == 0);\n  CU_ASSERT(strcmp(\"bar\", nv[13]) == 0);\n  spdylay_frame_nv_del(nv);\n\n  /* Create in-sequence NUL bytes */\n  /* Assuming first chunk has enough space to store 1st name/value\n     pair. */\n  memcpy(&buffer.root.next->data[len_size +\n                                 len_size + strlen(headers[0]) +\n                                 len_size + strlen(headers[1])-2],\n         \"\\0\\0\", 2);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==\n            spdylay_frame_unpack_nv(&nv, &buffer, len_size));\n\n  spdylay_frame_nv_del(nv);\n  spdylay_buffer_free(&buffer);\n}\n\nvoid test_spdylay_frame_unpack_nv_spdy2(void)\n{\n  test_spdylay_frame_unpack_nv_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));\n}\n\nvoid test_spdylay_frame_unpack_nv_spdy3(void)\n{\n  test_spdylay_frame_unpack_nv_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));\n}\n\nvoid test_spdylay_frame_pack_nv_duplicate_keys(void)\n{\n  uint8_t out[1024];\n  size_t len_size = 2;\n  const char *nv_src[] = {\n    \"method\", \"GET\",\n    \"scheme\", \"https\",\n    \"url\", \"/\",\n    \"X-hEad\", \"foo\",\n    \"x-heaD\", \"bar\",\n    \"version\", \"HTTP/1.1\",\n    NULL\n  };\n  char **nv = spdylay_frame_nv_norm_copy(nv_src);\n  const uint8_t *outptr;\n  int pairs, len;\n  /* size_t inlen = */ spdylay_frame_pack_nv(out, nv, len_size);\n  outptr = out;\n\n  pairs = spdylay_get_uint16(outptr);\n  CU_ASSERT(pairs == 5);\n  outptr += 2;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 6);\n  CU_ASSERT(memcmp(outptr, \"method\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 3);\n  CU_ASSERT(memcmp(outptr, \"GET\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 6);\n  CU_ASSERT(memcmp(outptr, \"scheme\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 5);\n  CU_ASSERT(memcmp(outptr, \"https\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 3);\n  CU_ASSERT(memcmp(outptr, \"url\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 1);\n  CU_ASSERT(memcmp(outptr, \"/\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 7);\n  CU_ASSERT(memcmp(outptr, \"version\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 8);\n  CU_ASSERT(memcmp(outptr, \"HTTP/1.1\", len) == 0);\n  outptr += len;\n\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 6);\n  CU_ASSERT(memcmp(outptr, \"x-head\", len) == 0);\n  outptr += len;\n\n  len = spdylay_get_uint16(outptr);\n  outptr += 2;\n  CU_ASSERT(len == 7);\n  CU_ASSERT(memcmp(outptr, \"foo\\0bar\", len) == 0);\n  outptr += len;\n\n  spdylay_frame_nv_del(nv);\n}\n\nstatic const char *multi_empty_headers1[] = {\n  \"a\", \"\",\n  \"a\", \"\",\n  NULL\n};\n\nstatic const char *multi_empty_headers2[] = {\n  \"a\", \"/\",\n  \"a\", \"\",\n  NULL\n};\n\nstatic const char *multi_empty_headers3[] = {\n  \"a\", \"\",\n  \"a\", \"/\",\n  NULL\n};\n\nvoid test_spdylay_frame_count_nv_space(void)\n{\n  size_t len_size = 2;\n  CU_ASSERT(85 == spdylay_frame_count_nv_space((char**)headers, len_size));\n  len_size = 4;\n  CU_ASSERT(111 == spdylay_frame_count_nv_space((char**)headers, len_size));\n  /* only (\"a\", \"\") is counted */\n  CU_ASSERT(13 == spdylay_frame_count_nv_space((char**)multi_empty_headers1,\n                                               len_size));\n  /* only (\"a\", \"/\") is counted */\n  CU_ASSERT(14 == spdylay_frame_count_nv_space((char**)multi_empty_headers2,\n                                               len_size));\n  /* only (\"a\", \"/\") is counted */\n  CU_ASSERT(14 == spdylay_frame_count_nv_space((char**)multi_empty_headers3,\n                                               len_size));\n}\n\nstatic void frame_pack_nv_empty_value_check(uint8_t *outptr,\n                                            int vallen,\n                                            const char *val,\n                                            size_t len_size)\n{\n  int len;\n  len = get_packed_hd_len(outptr, len_size);\n  CU_ASSERT(1 == len);\n  outptr += len_size;\n  len = get_packed_hd_len(outptr, len_size);\n  CU_ASSERT(1 == len);\n  outptr += len_size;\n  CU_ASSERT(0 == memcmp(\"a\", outptr, len));\n  outptr += len;\n  len = get_packed_hd_len(outptr, len_size);\n  CU_ASSERT(vallen == len);\n  len += len_size;\n  if(vallen == len) {\n    CU_ASSERT(0 == memcmp(val, outptr, vallen));\n  }\n}\n\nstatic void test_spdylay_frame_pack_nv_empty_value_with(size_t len_size)\n{\n  uint8_t out[256];\n  char **nv;\n  ssize_t rv;\n  int off = (len_size == 2 ? -6 : 0);\n\n  nv = spdylay_frame_nv_copy(multi_empty_headers1);\n  rv = spdylay_frame_pack_nv(out, nv, len_size);\n  CU_ASSERT(13+off == rv);\n  frame_pack_nv_empty_value_check(out, 0, NULL, len_size);\n  spdylay_frame_nv_del(nv);\n\n  nv = spdylay_frame_nv_copy(multi_empty_headers2);\n  rv = spdylay_frame_pack_nv(out, nv, len_size);\n  CU_ASSERT(14+off == rv);\n  frame_pack_nv_empty_value_check(out, 1, \"/\", len_size);\n  spdylay_frame_nv_del(nv);\n\n  nv = spdylay_frame_nv_copy(multi_empty_headers3);\n  rv = spdylay_frame_pack_nv(out, nv, len_size);\n  CU_ASSERT(14+off == rv);\n  frame_pack_nv_empty_value_check(out, 1, \"/\", len_size);\n  spdylay_frame_nv_del(nv);\n}\n\nvoid test_spdylay_frame_pack_nv_empty_value_spdy2(void)\n{\n  test_spdylay_frame_pack_nv_empty_value_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));\n}\n\nvoid test_spdylay_frame_pack_nv_empty_value_spdy3(void)\n{\n  test_spdylay_frame_pack_nv_empty_value_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));\n}\n\nvoid test_spdylay_frame_count_unpack_nv_space(void)\n{\n  size_t nvlen, buflen;\n  uint8_t out[1024];\n  size_t len_size = 2;\n  size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size);\n  uint16_t temp;\n  size_t expected_buflen;\n  spdylay_buffer buffer;\n  uint8_t *chunk;\n\n  spdylay_buffer_init(&buffer, 4096);\n  spdylay_buffer_write(&buffer, out, inlen);\n\n  CU_ASSERT(0 == spdylay_frame_count_unpack_nv_space(&nvlen, &buflen,\n                                                     &buffer, len_size));\n  CU_ASSERT(7 == nvlen);\n  expected_buflen = 71+(nvlen*2+1)*sizeof(char*);\n  CU_ASSERT(expected_buflen == buflen);\n\n  chunk = buffer.root.next->data;\n  /* Change number of nv pair to a bogus value */\n  temp = spdylay_get_uint16(chunk);\n  spdylay_put_uint16be(chunk, temp+1);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME ==\n            spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer,\n                                                len_size));\n  spdylay_put_uint16be(chunk, temp);\n\n  /* Change the length of name to a bogus value */\n  temp = spdylay_get_uint16(chunk+2);\n  spdylay_put_uint16be(chunk+2, temp+1);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME ==\n            spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer,\n                                                len_size));\n  spdylay_put_uint16be(chunk+2, 65535);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME ==\n            spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer,\n                                                len_size));\n\n  /* Trailing garbage */\n  spdylay_buffer_advance(&buffer, 2);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME ==\n            spdylay_frame_count_unpack_nv_space(&nvlen, &buflen,\n                                                &buffer, len_size));\n  /* We advanced buffer 2 bytes, so it is not valid any more. */\n  spdylay_buffer_free(&buffer);\n}\n\nvoid test_spdylay_frame_pack_ping(void)\n{\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  ssize_t framelen;\n  spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY2, 1);\n  framelen = spdylay_frame_pack_ping(&buf, &buflen, &frame.ping);\n  CU_ASSERT(0 == spdylay_frame_unpack_ping\n            (&oframe.ping,\n             &buf[0], SPDYLAY_FRAME_HEAD_LENGTH,\n             &buf[SPDYLAY_FRAME_HEAD_LENGTH],\n             framelen-SPDYLAY_FRAME_HEAD_LENGTH));\n  CU_ASSERT(1 == oframe.ping.unique_id);\n  free(buf);\n  spdylay_frame_ping_free(&oframe.ping);\n  spdylay_frame_ping_free(&frame.ping);\n}\n\nstatic void test_spdylay_frame_pack_goaway_version(uint16_t version)\n{\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  ssize_t framelen;\n  spdylay_frame_goaway_init(&frame.goaway, version, 1000000007,\n                            SPDYLAY_GOAWAY_PROTOCOL_ERROR);\n  framelen = spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway);\n  CU_ASSERT(0 == spdylay_frame_unpack_goaway\n            (&oframe.goaway,\n             &buf[0], SPDYLAY_FRAME_HEAD_LENGTH,\n             &buf[SPDYLAY_FRAME_HEAD_LENGTH],\n             framelen-SPDYLAY_FRAME_HEAD_LENGTH));\n  CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id);\n  if(version == SPDYLAY_PROTO_SPDY2) {\n    /* The status code is ignored in SPDY/2 */\n    CU_ASSERT(0 == oframe.goaway.status_code);\n  } else if(version == SPDYLAY_PROTO_SPDY3) {\n    CU_ASSERT(SPDYLAY_GOAWAY_PROTOCOL_ERROR == oframe.goaway.status_code);\n  }\n  CU_ASSERT(version == oframe.goaway.hd.version);\n  CU_ASSERT(SPDYLAY_GOAWAY == oframe.goaway.hd.type);\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.goaway.hd.flags);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.goaway.hd.length);\n  free(buf);\n  spdylay_frame_goaway_free(&oframe.goaway);\n  spdylay_frame_goaway_free(&frame.goaway);\n}\n\nvoid test_spdylay_frame_pack_goaway_spdy2(void)\n{\n  test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_frame_pack_goaway_spdy3(void)\n{\n  test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY3);\n}\n\nstatic void test_spdylay_frame_pack_syn_stream_version(uint16_t version)\n{\n  spdylay_zlib deflater, inflater;\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  uint8_t pri;\n  if(version == SPDYLAY_PROTO_SPDY2) {\n    pri = 3;\n  } else {\n    pri = 7;\n  }\n  spdylay_zlib_deflate_hd_init(&deflater, 1, version);\n  spdylay_zlib_inflate_hd_init(&inflater, version);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, version,\n                                SPDYLAY_CTRL_FLAG_FIN, 65536, 1000000007, pri,\n                                spdylay_frame_nv_copy(headers));\n  framelen = spdylay_frame_pack_syn_stream(&buf, &buflen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream, &deflater);\n\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM,\n                                            version,\n                                            &oframe,\n                                            &inflater,\n                                            buf, framelen));\n  CU_ASSERT(65536 == oframe.syn_stream.stream_id);\n  CU_ASSERT(1000000007 == oframe.syn_stream.assoc_stream_id);\n  CU_ASSERT(version == oframe.syn_stream.hd.version);\n  CU_ASSERT(SPDYLAY_SYN_STREAM == oframe.syn_stream.hd.type);\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.syn_stream.hd.flags);\n  CU_ASSERT(pri == oframe.syn_stream.pri);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.syn_stream.hd.length);\n  CU_ASSERT(strcmp(\"method\", oframe.syn_stream.nv[0]) == 0);\n  CU_ASSERT(strcmp(\"GET\", oframe.syn_stream.nv[1]) == 0);\n  CU_ASSERT(NULL == oframe.syn_stream.nv[14]);\n  free(buf);\n  free(nvbuf);\n  spdylay_frame_syn_stream_free(&oframe.syn_stream);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  spdylay_zlib_inflate_free(&inflater);\n  spdylay_zlib_deflate_free(&deflater);\n}\n\nvoid test_spdylay_frame_pack_syn_stream_spdy2(void)\n{\n  test_spdylay_frame_pack_syn_stream_version(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_frame_pack_syn_stream_spdy3(void)\n{\n  test_spdylay_frame_pack_syn_stream_version(SPDYLAY_PROTO_SPDY3);\n}\n\nvoid test_spdylay_frame_pack_syn_stream_frame_too_large(void)\n{\n  spdylay_zlib deflater;\n  spdylay_frame frame;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  size_t big_vallen = 16777215;\n  char *big_val = malloc(big_vallen + 1);\n  const char *big_hds[] = { \"header\", big_val, NULL };\n  memset(big_val, '0', big_vallen);\n  big_val[big_vallen] = '\\0';\n  /* No compression */\n  spdylay_zlib_deflate_hd_init(&deflater, 0, SPDYLAY_PROTO_SPDY3);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY3,\n                                SPDYLAY_CTRL_FLAG_FIN, 65536, 1000000007, 3,\n                                spdylay_frame_nv_copy(big_hds));\n  framelen = spdylay_frame_pack_syn_stream(&buf, &buflen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream, &deflater);\n  CU_ASSERT_EQUAL(SPDYLAY_ERR_FRAME_TOO_LARGE, framelen);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  free(buf);\n  free(nvbuf);\n  free(big_val);\n  spdylay_zlib_deflate_free(&deflater);\n}\n\nstatic void test_spdylay_frame_pack_syn_reply_version(uint16_t version)\n{\n  spdylay_zlib deflater, inflater;\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  spdylay_zlib_deflate_hd_init(&deflater, 1, version);\n  spdylay_zlib_inflate_hd_init(&inflater, version);\n  spdylay_frame_syn_reply_init(&frame.syn_reply, version,\n                               SPDYLAY_CTRL_FLAG_FIN, 3,\n                               spdylay_frame_nv_copy(headers));\n  framelen = spdylay_frame_pack_syn_reply(&buf, &buflen,\n                                          &nvbuf, &nvbuflen,\n                                          &frame.syn_reply, &deflater);\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_REPLY,\n                                            version,\n                                            &oframe,\n                                            &inflater,\n                                            buf, framelen));\n  CU_ASSERT(3 == oframe.syn_reply.stream_id);\n  CU_ASSERT(version == oframe.syn_reply.hd.version);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == oframe.syn_reply.hd.type);\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.syn_reply.hd.flags);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.syn_reply.hd.length);\n  CU_ASSERT(strcmp(\"method\", oframe.syn_reply.nv[0]) == 0);\n  CU_ASSERT(strcmp(\"GET\", oframe.syn_reply.nv[1]) == 0);\n  CU_ASSERT(NULL == oframe.syn_reply.nv[14]);\n  free(buf);\n  free(nvbuf);\n  spdylay_frame_syn_reply_free(&oframe.syn_reply);\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n  spdylay_zlib_inflate_free(&inflater);\n  spdylay_zlib_deflate_free(&deflater);\n}\n\nvoid test_spdylay_frame_pack_syn_reply_spdy2(void)\n{\n  test_spdylay_frame_pack_syn_reply_version(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_frame_pack_syn_reply_spdy3(void)\n{\n  test_spdylay_frame_pack_syn_reply_version(SPDYLAY_PROTO_SPDY3);\n}\n\nstatic void test_spdylay_frame_pack_headers_version(uint16_t version)\n{\n  spdylay_zlib deflater, inflater;\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL, *nvbuf = NULL;\n  size_t buflen = 0, nvbuflen = 0;\n  spdylay_buffer inflatebuf;\n  ssize_t framelen;\n  spdylay_buffer_init(&inflatebuf, 4096);\n  spdylay_zlib_deflate_hd_init(&deflater, 1, version);\n  spdylay_zlib_inflate_hd_init(&inflater, version);\n  spdylay_frame_headers_init(&frame.headers, version,\n                             SPDYLAY_CTRL_FLAG_FIN, 3,\n                             spdylay_frame_nv_copy(headers));\n  framelen = spdylay_frame_pack_headers(&buf, &buflen,\n                                        &nvbuf, &nvbuflen,\n                                        &frame.headers, &deflater);\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_HEADERS,\n                                            version,\n                                            &oframe,\n                                            &inflater,\n                                            buf, framelen));\n  CU_ASSERT(3 == oframe.headers.stream_id);\n  CU_ASSERT(version == oframe.headers.hd.version);\n  CU_ASSERT(SPDYLAY_HEADERS == oframe.headers.hd.type);\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.headers.hd.flags);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.headers.hd.length);\n  CU_ASSERT(strcmp(\"method\", oframe.headers.nv[0]) == 0);\n  CU_ASSERT(strcmp(\"GET\", oframe.headers.nv[1]) == 0);\n  CU_ASSERT(NULL == oframe.headers.nv[14]);\n  free(buf);\n  free(nvbuf);\n  spdylay_frame_headers_free(&oframe.headers);\n  spdylay_frame_headers_free(&frame.headers);\n  spdylay_zlib_inflate_free(&inflater);\n  spdylay_zlib_deflate_free(&deflater);\n  spdylay_buffer_free(&inflatebuf);\n}\n\nvoid test_spdylay_frame_pack_headers_spdy2(void)\n{\n  test_spdylay_frame_pack_headers_version(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_frame_pack_headers_spdy3(void)\n{\n  test_spdylay_frame_pack_headers_version(SPDYLAY_PROTO_SPDY3);\n}\n\nvoid test_spdylay_frame_pack_window_update(void)\n{\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  ssize_t framelen;\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1000000007, 4096);\n  framelen = spdylay_frame_pack_window_update(&buf, &buflen,\n                                              &frame.window_update);\n  CU_ASSERT(0 == spdylay_frame_unpack_window_update\n            (&oframe.window_update,\n             &buf[0], SPDYLAY_FRAME_HEAD_LENGTH,\n             &buf[SPDYLAY_FRAME_HEAD_LENGTH],\n             framelen-SPDYLAY_FRAME_HEAD_LENGTH));\n  CU_ASSERT(1000000007 == oframe.window_update.stream_id);\n  CU_ASSERT(4096 == oframe.window_update.delta_window_size);\n  CU_ASSERT(SPDYLAY_PROTO_SPDY3 == oframe.window_update.hd.version);\n  CU_ASSERT(SPDYLAY_WINDOW_UPDATE == oframe.window_update.hd.type);\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.window_update.hd.flags);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH ==\n            oframe.window_update.hd.length);\n  free(buf);\n  spdylay_frame_window_update_free(&oframe.window_update);\n  spdylay_frame_window_update_free(&frame.window_update);\n}\n\n\nstatic void test_spdylay_frame_pack_settings_version(uint16_t version)\n{\n  spdylay_frame frame, oframe;\n  uint8_t *buf = NULL;\n  size_t buflen = 0;\n  ssize_t framelen;\n  int i;\n  spdylay_settings_entry iv[3];\n  iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE;\n  iv[0].value = 256;\n  iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[1].value = 100;\n  iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[2].value = 65536;\n\n  spdylay_frame_settings_init\n    (&frame.settings, version, SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS,\n     spdylay_frame_iv_copy(iv, 3), 3);\n  framelen = spdylay_frame_pack_settings(&buf, &buflen, &frame.settings);\n  CU_ASSERT(8+4+3*8 == framelen);\n\n  CU_ASSERT(0 == spdylay_frame_unpack_settings\n            (&oframe.settings,\n             &buf[0], SPDYLAY_FRAME_HEAD_LENGTH,\n             &buf[SPDYLAY_FRAME_HEAD_LENGTH],\n             framelen-SPDYLAY_FRAME_HEAD_LENGTH));\n\n  CU_ASSERT(version == oframe.settings.hd.version);\n  CU_ASSERT(SPDYLAY_SETTINGS == oframe.settings.hd.type);\n  CU_ASSERT(SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS == oframe.settings.hd.flags);\n  CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.settings.hd.length);\n\n  CU_ASSERT(3 == oframe.settings.niv);\n  for(i = 0; i < 3; ++i) {\n    CU_ASSERT(iv[i].settings_id == oframe.settings.iv[i].settings_id);\n    CU_ASSERT(iv[i].flags == oframe.settings.iv[i].flags);\n    CU_ASSERT(iv[i].value == oframe.settings.iv[i].value);\n  }\n\n  free(buf);\n  spdylay_frame_settings_free(&frame.settings);\n  spdylay_frame_settings_free(&oframe.settings);\n}\n\nvoid test_spdylay_frame_pack_settings_spdy2(void)\n{\n  test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_frame_pack_settings_spdy3(void)\n{\n  test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY3);\n}\n\nvoid test_spdylay_frame_nv_sort(void)\n{\n  char *nv[7];\n  nv[0] = (char*)\"version\";\n  nv[1] = (char*)\"HTTP/1.1\";\n  nv[2] = (char*)\"method\";\n  nv[3] = (char*)\"GET\";\n  nv[4] = (char*)\"scheme\";\n  nv[5] = (char*)\"https\";\n  nv[6] = NULL;\n  spdylay_frame_nv_sort(nv);\n  CU_ASSERT(strcmp(\"method\", nv[0]) == 0);\n  CU_ASSERT(strcmp(\"GET\", nv[1]) == 0);\n  CU_ASSERT(strcmp(\"scheme\", nv[2]) == 0);\n  CU_ASSERT(strcmp(\"https\", nv[3]) == 0);\n  CU_ASSERT(strcmp(\"version\", nv[4]) == 0);\n  CU_ASSERT(strcmp(\"HTTP/1.1\", nv[5]) == 0);\n}\n\nvoid test_spdylay_frame_nv_downcase(void)\n{\n  const char *nv_src[] = {\n    \"VERSION\", \"HTTP/1.1\",\n    \"Content-Length\", \"1000000007\",\n    NULL\n  };\n  char **nv;\n  nv = spdylay_frame_nv_copy(nv_src);\n  spdylay_frame_nv_downcase(nv);\n  CU_ASSERT(0 == strcmp(\"version\", nv[0]));\n  CU_ASSERT(0 == strcmp(\"HTTP/1.1\", nv[1]));\n  CU_ASSERT(0 == strcmp(\"content-length\", nv[2]));\n  CU_ASSERT(0 == strcmp(\"1000000007\", nv[3]));\n  spdylay_frame_nv_del(nv);\n}\n\nvoid test_spdylay_frame_nv_2to3(void)\n{\n  const char *nv_src[] = {\n    \"host\", \"localhost\",\n    \"method\", \"GET\",\n    \"url\", \"/\",\n    \"accept\", \"*/*\",\n    \"scheme\", \"https\",\n    \"status\", \"200 OK\",\n    \"version\", \"HTTP/1.1\",\n    NULL\n  };\n  char **nv;\n  nv = spdylay_frame_nv_copy(nv_src);\n  spdylay_frame_nv_2to3(nv);\n  CU_ASSERT(0 == strcmp(\":host\", nv[0]));\n  CU_ASSERT(0 == strcmp(\":method\", nv[2]));\n  CU_ASSERT(0 == strcmp(\":path\", nv[4]));\n  CU_ASSERT(0 == strcmp(\"accept\", nv[6]));\n  CU_ASSERT(0 == strcmp(\":scheme\", nv[8]));\n  CU_ASSERT(0 == strcmp(\":status\", nv[10]));\n  CU_ASSERT(0 == strcmp(\":version\", nv[12]));\n  spdylay_frame_nv_del(nv);\n}\n\nvoid test_spdylay_frame_nv_3to2(void)\n{\n  const char *nv_src[] = {\n    \":host\", \"localhost\",\n    \":method\", \"GET\",\n    \":path\", \"/\",\n    \"accept\", \"*/*\",\n    \":scheme\", \"https\",\n    \":status\", \"200 OK\",\n    \":version\", \"HTTP/1.1\",\n    NULL\n  };\n  char **nv;\n  nv = spdylay_frame_nv_copy(nv_src);\n  spdylay_frame_nv_3to2(nv);\n  CU_ASSERT(0 == strcmp(\"host\", nv[0]));\n  CU_ASSERT(0 == strcmp(\"method\", nv[2]));\n  CU_ASSERT(0 == strcmp(\"url\", nv[4]));\n  CU_ASSERT(0 == strcmp(\"accept\", nv[6]));\n  CU_ASSERT(0 == strcmp(\"scheme\", nv[8]));\n  CU_ASSERT(0 == strcmp(\"status\", nv[10]));\n  CU_ASSERT(0 == strcmp(\"version\", nv[12]));\n  spdylay_frame_nv_del(nv);\n}\n\n/* This function intentionally does not merge same header field into\n   one */\nstatic size_t spdylay_pack_nv(uint8_t *buf, size_t buflen _U_, const char **nv,\n                              size_t len_size)\n{\n  size_t i, n;\n  uint8_t *buf_ptr;\n  buf_ptr = buf;\n  for(n = 0; nv[n]; ++n);\n  spdylay_frame_put_nv_len(buf_ptr, (uint32_t)(n/2), len_size);\n  buf_ptr += len_size;\n  for(i = 0; i < n; ++i) {\n    size_t len = strlen(nv[i]);\n    spdylay_frame_put_nv_len(buf_ptr, (uint32_t)len, len_size);\n    buf_ptr += len_size;\n    memcpy(buf_ptr, nv[i], len);\n    buf_ptr += len;\n  }\n  return buf_ptr-buf;\n}\n\nstatic const char *empty_name_headers[] = {\n  \"method\", \"GET\",\n  \"\", \"https\",\n  \"url\", \"/\",\n  NULL\n};\n\nstatic const char non_ascii_header_name[] = { (char)0xff, '\\0' };\n\nstatic const char *non_ascii_headers[] = {\n  non_ascii_header_name, \"foo\",\n  NULL\n};\n\nstatic void test_spdylay_frame_unpack_nv_check_name_with(size_t len_size)\n{\n  uint8_t nvbuf[1024];\n  size_t nvbuflen;\n  spdylay_buffer buffer;\n  char **nv;\n\n  spdylay_buffer_init(&buffer, 32);\n\n  nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), headers, len_size);\n  spdylay_buffer_write(&buffer, nvbuf, nvbuflen);\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==\n            spdylay_frame_unpack_nv(&nv, &buffer, len_size));\n\n  spdylay_frame_nv_del(nv);\n  spdylay_buffer_reset(&buffer);\n\n  nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), empty_name_headers,\n                             len_size);\n  spdylay_buffer_write(&buffer, nvbuf, nvbuflen);\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==\n            spdylay_frame_unpack_nv(&nv, &buffer, len_size));\n\n  spdylay_frame_nv_del(nv);\n  spdylay_buffer_reset(&buffer);\n\n  nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), non_ascii_headers,\n                             len_size);\n  spdylay_buffer_write(&buffer, nvbuf, nvbuflen);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==\n            spdylay_frame_unpack_nv(&nv, &buffer, len_size));\n\n  spdylay_frame_nv_del(nv);\n  spdylay_buffer_free(&buffer);\n}\n\nvoid test_spdylay_frame_unpack_nv_check_name_spdy2(void)\n{\n  test_spdylay_frame_unpack_nv_check_name_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));\n}\n\nvoid test_spdylay_frame_unpack_nv_check_name_spdy3(void)\n{\n  test_spdylay_frame_unpack_nv_check_name_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));\n}\n\nstatic void test_spdylay_frame_unpack_nv_last_empty_value_with(size_t len_size)\n{\n  size_t nvbuflen;\n  uint8_t nvbuf[256];\n  uint8_t *nvbufptr;\n  spdylay_buffer buffer;\n  char **outnv = 0;\n  const char hdname[] = \"method\";\n\n  nvbufptr = nvbuf;\n  spdylay_frame_put_nv_len(nvbufptr, 1, len_size);\n  nvbufptr += len_size;\n  spdylay_frame_put_nv_len(nvbufptr, sizeof(hdname)-1, len_size);\n  nvbufptr += len_size;\n  memcpy(nvbufptr, hdname, sizeof(hdname)-1);\n  nvbufptr += sizeof(hdname)-1;\n  spdylay_frame_put_nv_len(nvbufptr, 4, len_size);\n  nvbufptr += len_size;\n  /* Copy including terminating NULL */\n  memcpy(nvbufptr, \"GET\", 4);\n  nvbufptr += 4;\n  nvbuflen = nvbufptr - nvbuf;\n\n  spdylay_buffer_init(&buffer, 32);\n\n  spdylay_buffer_write(&buffer, nvbuf, nvbuflen);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK ==\n            spdylay_frame_unpack_nv(&outnv, &buffer, len_size));\n\n  spdylay_frame_nv_del(outnv);\n  spdylay_buffer_free(&buffer);\n}\n\nvoid test_spdylay_frame_unpack_nv_last_empty_value_spdy2(void)\n{\n  test_spdylay_frame_unpack_nv_last_empty_value_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));\n}\n\nvoid test_spdylay_frame_unpack_nv_last_empty_value_spdy3(void)\n{\n  test_spdylay_frame_unpack_nv_last_empty_value_with\n    (spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));\n}\n\nvoid test_spdylay_frame_nv_check_null(void)\n{\n  const char *headers1[] = { \"path\", \"/\", \"host\", \"a\", NULL };\n  const char *headers2[] = { \"\", \"/\", \"host\", \"a\", NULL };\n  const char *headers3[] = { \"path\", \"/\", \"host\\x01\", \"a\", NULL };\n  const char *headers4[] = { \"path\", \"/\", \"host\", NULL, NULL };\n\n  CU_ASSERT(spdylay_frame_nv_check_null(headers1));\n  CU_ASSERT(0 == spdylay_frame_nv_check_null(headers2));\n  CU_ASSERT(0 == spdylay_frame_nv_check_null(headers3));\n  CU_ASSERT(0 == spdylay_frame_nv_check_null(headers4));\n}\n"
  },
  {
    "path": "tests/spdylay_frame_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_FRAME_TEST_H\n#define SPDYLAY_FRAME_TEST_H\n\nvoid test_spdylay_frame_unpack_nv_spdy2(void);\nvoid test_spdylay_frame_unpack_nv_spdy3(void);\nvoid test_spdylay_frame_pack_nv_duplicate_keys(void);\nvoid test_spdylay_frame_pack_nv_empty_value_spdy2(void);\nvoid test_spdylay_frame_pack_nv_empty_value_spdy3(void);\nvoid test_spdylay_frame_count_nv_space(void);\nvoid test_spdylay_frame_count_unpack_nv_space(void);\nvoid test_spdylay_frame_pack_ping(void);\nvoid test_spdylay_frame_pack_goaway_spdy2(void);\nvoid test_spdylay_frame_pack_goaway_spdy3(void);\nvoid test_spdylay_frame_pack_syn_stream_spdy2(void);\nvoid test_spdylay_frame_pack_syn_stream_spdy3(void);\nvoid test_spdylay_frame_pack_syn_stream_frame_too_large(void);\nvoid test_spdylay_frame_pack_syn_reply_spdy2(void);\nvoid test_spdylay_frame_pack_syn_reply_spdy3(void);\nvoid test_spdylay_frame_pack_headers_spdy2(void);\nvoid test_spdylay_frame_pack_headers_spdy3(void);\nvoid test_spdylay_frame_pack_window_update(void);\nvoid test_spdylay_frame_pack_settings_spdy2(void);\nvoid test_spdylay_frame_pack_settings_spdy3(void);\nvoid test_spdylay_frame_nv_sort(void);\nvoid test_spdylay_frame_nv_downcase(void);\nvoid test_spdylay_frame_nv_2to3(void);\nvoid test_spdylay_frame_nv_3to2(void);\nvoid test_spdylay_frame_unpack_nv_check_name_spdy2(void);\nvoid test_spdylay_frame_unpack_nv_check_name_spdy3(void);\nvoid test_spdylay_frame_unpack_nv_last_empty_value_spdy2(void);\nvoid test_spdylay_frame_unpack_nv_last_empty_value_spdy3(void);\nvoid test_spdylay_frame_nv_check_null(void);\n\n#endif /* SPDYLAY_FRAME_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_gzip_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_gzip_test.h\"\n\n#include <stdio.h>\n#include <assert.h>\n\n#include <CUnit/CUnit.h>\n\n#include <zlib.h>\n\n#include \"spdylay_gzip.h\"\n\nstatic ssize_t deflate_data(uint8_t *out, size_t outlen,\n                            const uint8_t *in, size_t inlen)\n{\n  int rv;\n  z_stream zst;\n  zst.next_in = Z_NULL;\n  zst.zalloc = Z_NULL;\n  zst.zfree = Z_NULL;\n  zst.opaque = Z_NULL;\n\n  rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION);\n  assert(rv == Z_OK);\n\n  zst.avail_in = (uint)inlen;\n  zst.next_in = (uint8_t*)in;\n  zst.avail_out = (uint)outlen;\n  zst.next_out = out;\n  rv = deflate(&zst, Z_SYNC_FLUSH);\n  assert(rv == Z_OK);\n\n  deflateEnd(&zst);\n\n  return outlen-zst.avail_out;\n}\n\nstatic const char input[] =\n  \"THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND \"\n  \"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \"\n  \"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \"\n  \"NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE \"\n  \"LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION \"\n  \"OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION \"\n  \"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\";\n\nvoid test_spdylay_gzip_inflate(void)\n{\n  spdylay_gzip *inflater;\n  uint8_t in[4096], out[4096], *inptr;\n  size_t inlen = sizeof(in);\n  size_t inproclen, outproclen;\n  const char *inputptr = input;\n\n  inlen = deflate_data(in, inlen, (const uint8_t*)input, sizeof(input)-1);\n\n  CU_ASSERT(0 == spdylay_gzip_inflate_new(&inflater));\n  /* First 16 bytes */\n  inptr = in;\n  inproclen = inlen;\n  outproclen = 16;\n  CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen,\n                                      inptr, &inproclen));\n  CU_ASSERT(16 == outproclen);\n  CU_ASSERT(inproclen > 0);\n  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));\n  /* Next 32 bytes */\n  inptr += inproclen;\n  inlen -= inproclen;\n  inproclen = inlen;\n  inputptr += outproclen;\n  outproclen = 32;\n  CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen,\n                                      inptr, &inproclen));\n  CU_ASSERT(32 == outproclen);\n  CU_ASSERT(inproclen > 0);\n  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));\n  /* Rest */\n  inptr += inproclen;\n  inlen -= inproclen;\n  inproclen = inlen;\n  inputptr += outproclen;\n  outproclen = sizeof(out);\n  CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen,\n                                      inptr, &inproclen));\n  CU_ASSERT(sizeof(input)-49 == outproclen);\n  CU_ASSERT(inproclen > 0);\n  CU_ASSERT(0 == memcmp(inputptr, out, outproclen));\n\n  inlen -= inproclen;\n  CU_ASSERT(0 == inlen);\n\n  spdylay_gzip_inflate_del(inflater);\n}\n"
  },
  {
    "path": "tests/spdylay_gzip_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_GZIP_TEST_H\n#define SPDYLAY_GZIP_TEST_H\n\nvoid test_spdylay_gzip_inflate(void);\n\n#endif /* SPDYLAY_GZIP_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_map_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_map_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_map.h\"\n\ntypedef struct strentry {\n  spdylay_map_entry map_entry;\n  const char *str;\n} strentry;\n\nstatic void strentry_init(strentry *entry, key_type key, const char *str)\n{\n  spdylay_map_entry_init(&entry->map_entry, key);\n  entry->str = str;\n}\n\nvoid test_spdylay_map(void)\n{\n  strentry foo, FOO, bar, baz, shrubbery;\n  spdylay_map map;\n  spdylay_map_init(&map);\n\n  strentry_init(&foo, 1, \"foo\");\n  strentry_init(&FOO, 1, \"FOO\");\n  strentry_init(&bar, 2, \"bar\");\n  strentry_init(&baz, 3, \"baz\");\n  strentry_init(&shrubbery, 4, \"shrubbery\");\n\n  CU_ASSERT(0 == spdylay_map_insert(&map, &foo.map_entry));\n  CU_ASSERT(strcmp(\"foo\", ((strentry*)spdylay_map_find(&map, 1))->str) == 0);\n  CU_ASSERT(1 == spdylay_map_size(&map));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_map_insert(&map, &FOO.map_entry));\n\n  CU_ASSERT(1 == spdylay_map_size(&map));\n  CU_ASSERT(strcmp(\"foo\", ((strentry*)spdylay_map_find(&map, 1))->str) == 0);\n\n  CU_ASSERT(0 == spdylay_map_insert(&map, &bar.map_entry));\n  CU_ASSERT(2 == spdylay_map_size(&map));\n\n  CU_ASSERT(0 == spdylay_map_insert(&map, &baz.map_entry));\n  CU_ASSERT(3 == spdylay_map_size(&map));\n\n  CU_ASSERT(0 == spdylay_map_insert(&map, &shrubbery.map_entry));\n  CU_ASSERT(4 == spdylay_map_size(&map));\n\n  CU_ASSERT(strcmp(\"baz\", ((strentry*)spdylay_map_find(&map, 3))->str) == 0);\n\n  spdylay_map_remove(&map, 3);\n  CU_ASSERT(3 == spdylay_map_size(&map));\n  CU_ASSERT(NULL == spdylay_map_find(&map, 3));\n\n  spdylay_map_remove(&map, 1);\n  CU_ASSERT(2 == spdylay_map_size(&map));\n  CU_ASSERT(NULL == spdylay_map_find(&map, 1));\n\n  /* Erasing non-existent entry */\n  spdylay_map_remove(&map, 1);\n  CU_ASSERT(2 == spdylay_map_size(&map));\n  CU_ASSERT(NULL == spdylay_map_find(&map, 1));\n\n  CU_ASSERT(strcmp(\"bar\", ((strentry*)spdylay_map_find(&map, 2))->str) == 0);\n  CU_ASSERT(strcmp(\"shrubbery\",\n                   ((strentry*)spdylay_map_find(&map, 4))->str) == 0);\n\n  spdylay_map_free(&map);\n}\n\nstatic void shuffle(int *a, int n)\n{\n  int i;\n  for(i = n - 1; i >= 1; --i) {\n    size_t j = (int)((double)(i + 1) * rand() / (RAND_MAX + 1.0));\n    int t = a[j];\n    a[j] = a[i];\n    a[i] = t;\n  }\n}\n\nstatic int eachfun(spdylay_map_entry *entry _U_, void *ptr _U_)\n{\n  return 0;\n}\n\n#define NUM_ENT 6000\nstrentry arr[NUM_ENT];\nint order[NUM_ENT];\n\nvoid test_spdylay_map_functional(void)\n{\n  spdylay_map map;\n  int i;\n\n  spdylay_map_init(&map);\n  for(i = 0; i < NUM_ENT; ++i) {\n    strentry_init(&arr[i], i + 1, \"foo\");\n    order[i] = i + 1;\n  }\n  /* insertion */\n  shuffle(order, NUM_ENT);\n  for(i = 0; i < NUM_ENT; ++i) {\n    CU_ASSERT(0 == spdylay_map_insert(&map, &arr[order[i] - 1].map_entry));\n  }\n  /* traverse */\n  spdylay_map_each(&map, eachfun, NULL);\n  /* find */\n  shuffle(order, NUM_ENT);\n  for(i = 0; i < NUM_ENT; ++i) {\n    spdylay_map_find(&map, order[i]);\n  }\n  /* remove */\n  shuffle(order, NUM_ENT);\n  for(i = 0; i < NUM_ENT; ++i) {\n    CU_ASSERT(0 == spdylay_map_remove(&map, order[i]));\n  }\n\n  /* each_free (but no op function for testing purpose) */\n  for(i = 0; i < NUM_ENT; ++i) {\n    strentry_init(&arr[i], i + 1, \"foo\");\n  }\n  /* insert once again */\n  for(i = 0; i < NUM_ENT; ++i) {\n    CU_ASSERT(0 == spdylay_map_insert(&map, &arr[i].map_entry));\n  }\n  spdylay_map_each_free(&map, eachfun, NULL);\n  spdylay_map_free(&map);\n}\n\nstatic int entry_free(spdylay_map_entry *entry, void *ptr _U_)\n{\n  free(entry);\n  return 0;\n}\n\nvoid test_spdylay_map_each_free(void)\n{\n  strentry *foo = malloc(sizeof(strentry)),\n    *bar = malloc(sizeof(strentry)),\n    *baz = malloc(sizeof(strentry)),\n    *shrubbery = malloc(sizeof(strentry));\n  spdylay_map map;\n  spdylay_map_init(&map);\n\n  strentry_init(foo, 1, \"foo\");\n  strentry_init(bar, 2, \"bar\");\n  strentry_init(baz, 3, \"baz\");\n  strentry_init(shrubbery, 4, \"shrubbery\");\n\n  spdylay_map_insert(&map, &foo->map_entry);\n  spdylay_map_insert(&map, &bar->map_entry);\n  spdylay_map_insert(&map, &baz->map_entry);\n  spdylay_map_insert(&map, &shrubbery->map_entry);\n\n  spdylay_map_each_free(&map, entry_free, NULL);\n  spdylay_map_free(&map);\n}\n"
  },
  {
    "path": "tests/spdylay_map_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_MAP_TEST_H\n#define SPDYLAY_MAP_TEST_H\n\nvoid test_spdylay_map(void);\nvoid test_spdylay_map_functional(void);\nvoid test_spdylay_map_each_free(void);\n\n#endif /* SPDYLAY_MAP_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_npn_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Twist Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_npn_test.h\"\n\n#include <CUnit/CUnit.h>\n#include <spdylay/spdylay.h>\n#include <string.h>\n\nstatic void spdy2(void)\n{\n  const unsigned char spdy[] = {\n    8, 'h', 't', 't', 'p', '/', '1', '.', '1',\n    6, 's', 'p', 'd', 'y', '/', '2',\n    6, 's', 'p', 'd', 'y', '/', '3'\n  };\n  unsigned char outlen;\n  unsigned char* out;\n  CU_ASSERT(SPDYLAY_PROTO_SPDY3 ==\n            spdylay_select_next_protocol(&out, &outlen, spdy, sizeof(spdy)));\n  CU_ASSERT(6 == outlen);\n  CU_ASSERT(memcmp(\"spdy/3\", out, outlen) == 0);\n}\n\nstatic void http11(void)\n{\n  const unsigned char spdy[] = {\n    6, 's', 'p', 'd', 'y', '/', '4',\n    8, 's', 'p', 'd', 'y', '/', '2', '.', '1',\n    8, 'h', 't', 't', 'p', '/', '1', '.', '1',\n  };\n  unsigned char outlen;\n  unsigned char* out;\n  CU_ASSERT(0 == spdylay_select_next_protocol(&out, &outlen,\n                                              spdy, sizeof(spdy)));\n  CU_ASSERT(8 == outlen);\n  CU_ASSERT(memcmp(\"http/1.1\", out, outlen) == 0);\n}\n\nstatic void no_overlap(void)\n{\n  const unsigned char spdy[] = {\n    6, 's', 'p', 'd', 'y', '/', '4',\n    8, 's', 'p', 'd', 'y', '/', '2', '.', '1',\n    8, 'h', 't', 't', 'p', '/', '1', '.', '0',\n  };\n  unsigned char outlen = 0;\n  unsigned char* out = NULL;\n  CU_ASSERT(-1 == spdylay_select_next_protocol(&out, &outlen,\n                                               spdy, sizeof(spdy)));\n  CU_ASSERT(0 == outlen);\n  CU_ASSERT(NULL == out);\n}\n\nvoid test_spdylay_npn(void)\n{\n  spdy2();\n  http11();\n  no_overlap();\n}\n\nvoid test_spdylay_npn_get_proto_list(void)\n{\n  size_t len;\n  const spdylay_npn_proto *list = spdylay_npn_get_proto_list(&len);\n  CU_ASSERT_EQUAL(3, len);\n\n  CU_ASSERT_STRING_EQUAL(\"spdy/3.1\", list[0].proto);\n  CU_ASSERT_EQUAL(8, list[0].len);\n  CU_ASSERT_EQUAL(SPDYLAY_PROTO_SPDY3_1, list[0].version);\n\n  CU_ASSERT_STRING_EQUAL(\"spdy/3\", list[1].proto);\n  CU_ASSERT_EQUAL(6, list[1].len);\n  CU_ASSERT_EQUAL(SPDYLAY_PROTO_SPDY3, list[1].version);\n\n  CU_ASSERT_STRING_EQUAL(\"spdy/2\", list[2].proto);\n  CU_ASSERT_EQUAL(6, list[2].len);\n  CU_ASSERT_EQUAL(SPDYLAY_PROTO_SPDY2, list[2].version);\n}\n\nvoid test_spdylay_npn_get_version(void)\n{\n  CU_ASSERT(SPDYLAY_PROTO_SPDY3_1 ==\n            spdylay_npn_get_version((const unsigned char*)\"spdy/3.1\", 8));\n  CU_ASSERT(SPDYLAY_PROTO_SPDY3 ==\n            spdylay_npn_get_version((const unsigned char*)\"spdy/3\", 6));\n  CU_ASSERT(SPDYLAY_PROTO_SPDY2 ==\n            spdylay_npn_get_version((const unsigned char*)\"spdy/2\", 6));\n  CU_ASSERT(0 == spdylay_npn_get_version((const unsigned char*)\"spdy/4\", 6));\n}\n"
  },
  {
    "path": "tests/spdylay_npn_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Twist Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_NPN_TEST_H\n#define SPDYLAY_NPN_TEST_H\n\nvoid test_spdylay_npn(void);\nvoid test_spdylay_npn_get_proto_list(void);\nvoid test_spdylay_npn_get_version(void);\n\n#endif /* SPDYLAY_NPN_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_pq_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_pq_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_pq.h\"\n\nstatic int pq_compar(const void *lhs, const void *rhs)\n{\n  return strcmp(lhs, rhs);\n}\n\nvoid test_spdylay_pq(void)\n{\n  int i;\n  spdylay_pq pq;\n  spdylay_pq_init(&pq, pq_compar);\n  CU_ASSERT(spdylay_pq_empty(&pq));\n  CU_ASSERT(0 == spdylay_pq_size(&pq));\n  CU_ASSERT(0 == spdylay_pq_push(&pq, (void*)\"foo\"));\n  CU_ASSERT(0 == spdylay_pq_empty(&pq));\n  CU_ASSERT(1 == spdylay_pq_size(&pq));\n  CU_ASSERT(strcmp(\"foo\", spdylay_pq_top(&pq)) == 0);\n  CU_ASSERT(0 == spdylay_pq_push(&pq, (void*)\"bar\"));\n  CU_ASSERT(strcmp(\"bar\", spdylay_pq_top(&pq)) == 0);\n  CU_ASSERT(0 == spdylay_pq_push(&pq, (void*)\"baz\"));\n  CU_ASSERT(strcmp(\"bar\", spdylay_pq_top(&pq)) == 0);\n  CU_ASSERT(0 == spdylay_pq_push(&pq, (void*)\"C\"));\n  CU_ASSERT(4 == spdylay_pq_size(&pq));\n  CU_ASSERT(strcmp(\"C\", spdylay_pq_top(&pq)) == 0);\n  spdylay_pq_pop(&pq);\n  CU_ASSERT(3 == spdylay_pq_size(&pq));\n  CU_ASSERT(strcmp(\"bar\", spdylay_pq_top(&pq)) == 0);\n  spdylay_pq_pop(&pq);\n  CU_ASSERT(strcmp(\"baz\", spdylay_pq_top(&pq)) == 0);\n  spdylay_pq_pop(&pq);\n  CU_ASSERT(strcmp(\"foo\", spdylay_pq_top(&pq)) == 0);\n  spdylay_pq_pop(&pq);\n  CU_ASSERT(spdylay_pq_empty(&pq));\n  CU_ASSERT(0 == spdylay_pq_size(&pq));\n  CU_ASSERT(NULL == spdylay_pq_top(&pq));\n\n  /* Add bunch of entry to see realloc works */\n  for(i = 0; i < 10000; ++i) {\n    CU_ASSERT(0 == spdylay_pq_push(&pq, (void*)\"foo\"));\n    CU_ASSERT((size_t)(i+1) == spdylay_pq_size(&pq));\n  }\n  for(i = 10000; i > 0; --i) {\n    CU_ASSERT(NULL != spdylay_pq_top(&pq));\n    spdylay_pq_pop(&pq);\n    CU_ASSERT((size_t)(i-1) == spdylay_pq_size(&pq));\n  }\n\n  spdylay_pq_free(&pq);\n}\n\n"
  },
  {
    "path": "tests/spdylay_pq_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_PQ_TEST_H\n#define SPDYLAY_PQ_TEST_H\n\nvoid test_spdylay_pq(void);\n\n#endif /* SPDYLAY_PQ_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_queue_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_queue_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_queue.h\"\n\nvoid test_spdylay_queue(void)\n{\n  int ints[] = { 1, 2, 3, 4, 5 };\n  int i;\n  spdylay_queue queue;\n  spdylay_queue_init(&queue);\n  CU_ASSERT(spdylay_queue_empty(&queue));\n  for(i = 0; i < 5; ++i) {\n    spdylay_queue_push(&queue, &ints[i]);\n    CU_ASSERT_EQUAL(ints[0], *(int*)(spdylay_queue_front(&queue)));\n    CU_ASSERT(!spdylay_queue_empty(&queue));\n  }\n  for(i = 0; i < 5; ++i) {\n    CU_ASSERT_EQUAL(ints[i], *(int*)(spdylay_queue_front(&queue)));\n    spdylay_queue_pop(&queue);\n  }\n  CU_ASSERT(spdylay_queue_empty(&queue));\n  spdylay_queue_free(&queue);\n}\n"
  },
  {
    "path": "tests/spdylay_queue_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_QUEUE_TEST_H\n#define SPDYLAY_QUEUE_TEST_H\n\nvoid test_spdylay_queue(void);\n\n#endif /* SPDYLAY_QUEUE_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_session_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_session_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include <stdio.h>\n#include <assert.h>\n\n#include \"spdylay_session.h\"\n#include \"spdylay_stream.h\"\n#include \"spdylay_net.h\"\n#include \"spdylay_helper.h\"\n#include \"spdylay_test_helper.h\"\n\n#define OB_CTRL(ITEM) spdylay_outbound_item_get_ctrl_frame(ITEM)\n#define OB_CTRL_TYPE(ITEM) spdylay_outbound_item_get_ctrl_frame_type(ITEM)\n#define OB_DATA(ITEM) spdylay_outbound_item_get_data_frame(ITEM)\n\ntypedef struct {\n  uint8_t buf[4096];\n  size_t length;\n} accumulator;\n\ntypedef struct {\n  uint8_t data[8192];\n  uint8_t* datamark;\n  uint8_t* datalimit;\n  size_t feedseq[8192];\n  size_t seqidx;\n} scripted_data_feed;\n\ntypedef struct {\n  accumulator *acc;\n  scripted_data_feed *df;\n  int ctrl_recv_cb_called, invalid_ctrl_recv_cb_called;\n  int ctrl_send_cb_called;\n  spdylay_frame_type sent_frame_type;\n  int ctrl_not_send_cb_called;\n  spdylay_frame_type not_sent_frame_type;\n  int not_sent_error;\n  int stream_close_cb_called;\n  size_t data_source_length;\n  int32_t stream_id;\n  size_t block_count;\n  int data_chunk_recv_cb_called;\n  int data_recv_cb_called;\n  size_t fixed_sendlen;\n} my_user_data;\n\nstatic void scripted_data_feed_init(scripted_data_feed *df,\n                                    uint8_t *data, size_t data_length)\n{\n  memset(df, 0, sizeof(scripted_data_feed));\n  memcpy(df->data, data, data_length);\n  df->datamark = df->data;\n  df->datalimit = df->data+data_length;\n  df->feedseq[0] = data_length;\n}\n\nstatic ssize_t null_send_callback(spdylay_session *session _U_,\n                                  const uint8_t* data _U_, size_t len, int flags _U_,\n                                  void *user_data _U_)\n{\n  return len;\n}\n\nstatic ssize_t fail_send_callback(spdylay_session *session _U_,\n                                  const uint8_t *data _U_, size_t len _U_, int flags _U_,\n                                  void *user_data _U_)\n{\n  return SPDYLAY_ERR_CALLBACK_FAILURE;\n}\n\nstatic ssize_t fixed_bytes_send_callback(spdylay_session *session _U_,\n                                         const uint8_t *data _U_, size_t len,\n                                         int flags _U_, void *user_data)\n{\n  size_t fixed_sendlen = ((my_user_data*)user_data)->fixed_sendlen;\n  return fixed_sendlen < len ? fixed_sendlen : len;\n}\n\nstatic ssize_t scripted_recv_callback(spdylay_session *session _U_,\n                                      uint8_t* data, size_t len, int flags _U_,\n                                      void *user_data)\n{\n  scripted_data_feed *df = ((my_user_data*)user_data)->df;\n  size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];\n  memcpy(data, df->datamark, wlen);\n  df->datamark += wlen;\n  df->feedseq[df->seqidx] -= wlen;\n  if(df->feedseq[df->seqidx] == 0) {\n    ++df->seqidx;\n  }\n  return wlen;\n}\n\nstatic ssize_t eof_recv_callback(spdylay_session *session _U_,\n                                      uint8_t* data _U_, size_t len _U_, int flags _U_,\n                                      void *user_data _U_)\n{\n  return SPDYLAY_ERR_EOF;\n}\n\nstatic ssize_t accumulator_send_callback(spdylay_session *session _U_,\n                                         const uint8_t *buf, size_t len,\n                                         int flags _U_, void* user_data)\n{\n  accumulator *acc = ((my_user_data*)user_data)->acc;\n  assert(acc->length+len < sizeof(acc->buf));\n  memcpy(acc->buf+acc->length, buf, len);\n  acc->length += len;\n  return len;\n}\n\nstatic void on_ctrl_recv_callback(spdylay_session *session _U_,\n                                  spdylay_frame_type type _U_,\n                                  spdylay_frame *frame _U_,\n                                  void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->ctrl_recv_cb_called;\n}\n\nstatic void on_invalid_ctrl_recv_callback(spdylay_session *session _U_,\n                                          spdylay_frame_type type _U_,\n                                          spdylay_frame *frame _U_,\n                                          uint32_t status_code _U_,\n                                          void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->invalid_ctrl_recv_cb_called;\n}\n\nstatic void on_ctrl_send_callback(spdylay_session *session _U_,\n                                  spdylay_frame_type type,\n                                  spdylay_frame *frame _U_,\n                                  void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->ctrl_send_cb_called;\n  ud->sent_frame_type = type;\n}\n\nstatic void on_ctrl_not_send_callback(spdylay_session *session _U_,\n                                      spdylay_frame_type type,\n                                      spdylay_frame *frame _U_,\n                                      int error,\n                                      void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->ctrl_not_send_cb_called;\n  ud->not_sent_frame_type = type;\n  ud->not_sent_error = error;\n}\n\nstatic void on_data_chunk_recv_callback(spdylay_session *session _U_,\n                                        uint8_t flags _U_, int32_t stream_id _U_,\n                                        const uint8_t *data _U_, size_t len _U_,\n                                        void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->data_chunk_recv_cb_called;\n}\n\nstatic void on_data_recv_callback(spdylay_session *session _U_,\n                                  uint8_t flags _U_, int32_t stream_id _U_,\n                                  int32_t length _U_, void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ++ud->data_recv_cb_called;\n}\n\nstatic ssize_t fixed_length_data_source_read_callback\n(spdylay_session *session _U_, int32_t stream_id _U_,\n uint8_t *buf _U_, size_t len, int *eof,\n spdylay_data_source *source _U_, void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  size_t wlen;\n  if(len < ud->data_source_length) {\n    wlen = len;\n  } else {\n    wlen = ud->data_source_length;\n  }\n  ud->data_source_length -= wlen;\n  if(ud->data_source_length == 0) {\n    *eof = 1;\n  }\n  return wlen;\n}\n\nstatic ssize_t temporal_failure_data_source_read_callback\n(spdylay_session *session _U_, int32_t stream_id _U_,\n uint8_t *buf _U_, size_t len _U_, int *eof _U_,\n spdylay_data_source *source _U_, void *user_data _U_)\n{\n  return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;\n}\n\nstatic ssize_t fail_data_source_read_callback\n(spdylay_session *session _U_, int32_t stream_id _U_,\n uint8_t *buf _U_, size_t len _U_, int *eof _U_,\n spdylay_data_source *source _U_, void *user_data _U_)\n{\n  return SPDYLAY_ERR_CALLBACK_FAILURE;\n}\n\nstatic void on_request_recv_callback(spdylay_session *session _U_,\n                                     int32_t stream_id,\n                                     void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  ud->stream_id = stream_id;\n}\n\nstatic void no_stream_user_data_stream_close_callback\n(spdylay_session *session _U_,\n int32_t stream_id _U_,\n spdylay_status_code status_code _U_,\n void *user_data)\n{\n  my_user_data* my_data = (my_user_data*)user_data;\n  ++my_data->stream_close_cb_called;\n}\n\nstatic char** dup_nv(const char **src)\n{\n  return spdylay_frame_nv_copy(src);\n}\n\nstatic spdylay_settings_entry* dup_iv(const spdylay_settings_entry *iv,\n                                      size_t niv)\n{\n  return spdylay_frame_iv_copy(iv, niv);\n}\n\nstatic const char *empty_name_nv[] = { \"Version\", \"HTTP/1.1\",\n                                       \"\", \"empty name\",\n                                       NULL };\n\nstatic const char *null_val_nv[] = { \"Version\", \"HTTP/1.1\",\n                                     \"Foo\", NULL,\n                                     NULL };\n\nvoid test_spdylay_session_recv(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  scripted_data_feed df;\n  my_user_data user_data;\n  const char *nv[] = {\n    \"url\", \"/\", NULL\n  };\n  const char *upcase_nv[] = {\n    \"URL\", \"/\", NULL\n  };\n  const char *mid_nv[] = {\n    \"method\", \"GET\",\n    \"scheme\", \"https\",\n    \"url\", \"/\",\n    \"x-head\", \"foo\",\n    \"x-head\", \"bar\",\n    \"version\", \"HTTP/1.1\",\n    \"x-empty\", \"\",\n    NULL\n  };\n  uint8_t *framedata = NULL, *nvbuf = NULL;\n  size_t framedatalen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  spdylay_frame frame;\n  int i;\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.recv_callback = scripted_recv_callback;\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  user_data.df = &df;\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                1, 0, 3, dup_nv(nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  scripted_data_feed_init(&df, framedata, framelen);\n  /* Send 1 byte per each read */\n  for(i = 0; i < framelen; ++i) {\n    df.feedseq[i] = 1;\n  }\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  user_data.ctrl_recv_cb_called = 0;\n  while((ssize_t)df.seqidx < framelen) {\n    CU_ASSERT(0 == spdylay_session_recv(session));\n  }\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n\n  /* Receive SYN_STREAM with invalid header block */\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                3, 0, 3, dup_nv(upcase_nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* Received SYN_STREAM without name/value header block */\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                5, 0, 3, dup_nv(upcase_nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  /* Use bytes that come before name/value header block */\n  spdylay_put_uint32be(&framedata[4],\n                       SPDYLAY_SYN_STREAM_NV_OFFSET - SPDYLAY_HEAD_LEN);\n  scripted_data_feed_init(&df, framedata, SPDYLAY_SYN_STREAM_NV_OFFSET);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_GOAWAY == OB_CTRL_TYPE(item));\n\n  spdylay_session_del(session);\n\n  /* Some tests for frame too large */\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,\n                             &user_data);\n  /* made max buffer small to cause error intentionally */\n  /* Inflated wire format of mid_nv will be 111 in SPDY/3. So payload\n     length will be 121. Setting max buffer size to 110 will cause\n     error while inflating name/value header block. */\n  session->max_recv_ctrl_frame_buf = 110;\n\n  /* Receive SYN_STREAM with too large payload */\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY3,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                1, 0, 3, dup_nv(mid_nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code);\n  CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* For SYN_REPLY and SYN_HEADERS, make max buffer even smaller */\n  session->max_recv_ctrl_frame_buf = 8;\n\n  /* Receive SYN_REPLY with too large payload */\n  spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY3,\n                               SPDYLAY_CTRL_FLAG_NONE,\n                               1, dup_nv(mid_nv));\n  framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen,\n                                          &nvbuf, &nvbuflen,\n                                          &frame.syn_reply,\n                                          &session->hd_deflater);\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code);\n  CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* Receive HEADERS with too large payload */\n  spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY3,\n                             SPDYLAY_CTRL_FLAG_NONE,\n                             1, dup_nv(mid_nv));\n  framelen = spdylay_frame_pack_headers(&framedata, &framedatalen,\n                                        &nvbuf, &nvbuflen,\n                                        &frame.headers,\n                                        &session->hd_deflater);\n  spdylay_frame_headers_free(&frame.headers);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_FRAME_TOO_LARGE == OB_CTRL(item)->rst_stream.status_code);\n  CU_ASSERT(1 == OB_CTRL(item)->rst_stream.stream_id);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* Receive PING with too large payload */\n  spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY3, 1);\n  spdylay_reserve_buffer(&framedata, &framedatalen, 77);\n  framelen = spdylay_frame_pack_ping(&framedata, &framedatalen, &frame.ping);\n  spdylay_frame_ping_free(&frame.ping);\n  spdylay_put_uint32be(&framedata[4], (uint32_t)(framedatalen - SPDYLAY_HEAD_LEN));\n  scripted_data_feed_init(&df, framedata, framedatalen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_GOAWAY == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_GOAWAY_PROTOCOL_ERROR ==\n            OB_CTRL(item)->rst_stream.status_code);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  spdylay_session_del(session);\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  /* Receive SYN_REPLY with invalid header block */\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2,\n                               SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(upcase_nv));\n  framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen,\n                                          &nvbuf, &nvbuflen,\n                                          &frame.syn_reply,\n                                          &session->hd_deflater);\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code);\n\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* Receive HEADERS with invalid header block */\n  spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2,\n                             SPDYLAY_CTRL_FLAG_NONE, 3, dup_nv(upcase_nv));\n  framelen = spdylay_frame_pack_headers(&framedata, &framedatalen,\n                                        &nvbuf, &nvbuflen,\n                                        &frame.headers,\n                                        &session->hd_deflater);\n  spdylay_frame_headers_free(&frame.headers);\n  scripted_data_feed_init(&df, framedata, framelen);\n  user_data.ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == user_data.ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code);\n\n  free(framedata);\n  free(nvbuf);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_add_frame(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  accumulator acc;\n  my_user_data user_data;\n  const char *nv[] = {\n    \"method\", \"GET\",\n    \"scheme\", \"https\",\n    \"url\", \"/\",\n    \"version\", \"HTTP/1.1\",\n    NULL\n  };\n  spdylay_frame *frame;\n  spdylay_syn_stream_aux_data *aux_data =\n    malloc(sizeof(spdylay_syn_stream_aux_data));\n  const uint8_t hd_ans1[] = {\n    0x80, 0x02, 0x00, 0x01\n  };\n  uint32_t temp32;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = accumulator_send_callback;\n  memset(aux_data, 0, sizeof(spdylay_syn_stream_aux_data));\n  acc.length = 0;\n  user_data.acc = &acc;\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &user_data));\n\n  frame = malloc(sizeof(spdylay_frame));\n  spdylay_frame_syn_stream_init(&frame->syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv));\n  session->next_stream_id += 2;\n\n  CU_ASSERT(0 == spdylay_session_add_frame(session, SPDYLAY_CTRL, frame,\n                                           aux_data));\n  CU_ASSERT(0 == spdylay_pq_empty(&session->ob_ss_pq));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(memcmp(hd_ans1, acc.buf, 4) == 0);\n  /* check stream id */\n  memcpy(&temp32, &acc.buf[8], 4);\n  temp32 = ntohl(temp32);\n  CU_ASSERT(1 == temp32);\n  /* check assoc stream id */\n  memcpy(&temp32, &acc.buf[12], 4);\n  temp32 = ntohl(temp32);\n  CU_ASSERT(0 == temp32);\n  /* check pri */\n  temp32 = (acc.buf[16] >> 6) & 0x3;\n  CU_ASSERT(3 == temp32);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_recv_invalid_stream_id(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  scripted_data_feed df;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  uint8_t *framedata = NULL, *nvbuf = NULL;\n  size_t framedatalen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  spdylay_frame frame;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.recv_callback = scripted_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n\n  user_data.df = &df;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  scripted_data_feed_init(&df, framedata, framelen);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2,\n                               SPDYLAY_CTRL_FLAG_NONE, 100, dup_nv(nv));\n  framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen,\n                                          &nvbuf, &nvbuflen,\n                                          &frame.syn_reply,\n                                          &session->hd_deflater);\n  scripted_data_feed_init(&df, framedata, framelen);\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called);\n\n  free(framedata);\n  free(nvbuf);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_syn_stream_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  spdylay_frame frame;\n  spdylay_stream *stream;\n  int32_t stream_id = 1;\n  uint8_t pri = 3;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                stream_id, 0, pri, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  stream = spdylay_session_get_stream(session, stream_id);\n  CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state);\n  CU_ASSERT(pri == stream->pri);\n\n  /* Same stream ID twice leads stream error */\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n  CU_ASSERT(SPDYLAY_STREAM_CLOSING == stream->state);\n\n  /* assoc_stream_id != 0 from client is invalid. */\n  frame.syn_stream.stream_id = 3;\n  frame.syn_stream.assoc_stream_id = 1;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n\n  /* More than max concurrent streams leads REFUSED_STREAM */\n  session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                5, 0, 3, dup_nv(nv));\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =\n    SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS;\n\n  /* Stream ID less than previouly received SYN_STREAM leads session\n     error */\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE,\n                                1, 0, 3, dup_nv(nv));\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n  CU_ASSERT(session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_syn_stream_received_with_push(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  spdylay_frame frame;\n  spdylay_stream *stream;\n  int32_t stream_id = 2;\n  int32_t assoc_stream_id = 1;\n  uint8_t pri = 3;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_session_open_stream(session, assoc_stream_id, SPDYLAY_CTRL_FLAG_NONE,\n                              pri, SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL,\n                                stream_id, assoc_stream_id, pri, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  stream = spdylay_session_get_stream(session, stream_id);\n  CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state);\n\n  /* assoc_stream_id == 0 is invalid */\n  frame.syn_stream.stream_id = 4;\n  frame.syn_stream.assoc_stream_id = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  /* Push without SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL is invalid */\n  frame.syn_stream.stream_id = 6;\n  frame.syn_stream.assoc_stream_id = 1;\n  frame.syn_stream.hd.flags = SPDYLAY_CTRL_FLAG_FIN;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called);\n\n  /* Push to non-existent stream is invalid */\n  frame.syn_stream.stream_id = 8;\n  frame.syn_stream.assoc_stream_id = 3;\n  frame.syn_stream.hd.flags = SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL;\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_syn_reply_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  spdylay_frame frame;\n  spdylay_stream *stream;\n  spdylay_outbound_item *item;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2,\n                               SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(SPDYLAY_STREAM_OPENED ==\n            ((spdylay_stream*)spdylay_map_find(&session->streams, 1))->state);\n\n  CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n  CU_ASSERT(SPDYLAY_STREAM_CLOSING ==\n            ((spdylay_stream*)spdylay_map_find(&session->streams, 1))->state);\n\n  /* Check the situation when SYN_REPLY is received after peer sends\n     FIN */\n  stream = spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 0,\n                                       SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);\n  frame.syn_reply.stream_id = 3;\n\n  CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame));\n  CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n\n  spdylay_session_del(session);\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,\n                             &user_data);\n\n  /* Multiple SYN_REPLY frames for the same active stream ID */\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY3,\n                               SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv));\n  user_data.invalid_ctrl_recv_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_STREAM_IN_USE == OB_CTRL(item)->rst_stream.status_code);\n\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_send_syn_stream(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  spdylay_frame *frame = malloc(sizeof(spdylay_frame));\n  spdylay_stream *stream;\n  spdylay_syn_stream_aux_data *aux_data =\n    malloc(sizeof(spdylay_syn_stream_aux_data));\n  memset(aux_data, 0, sizeof(spdylay_syn_stream_aux_data));\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n  spdylay_frame_syn_stream_init(&frame->syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv));\n  session->next_stream_id += 2;\n  spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, aux_data);\n  CU_ASSERT(0 == spdylay_session_send(session));\n  stream = spdylay_session_get_stream(session, 1);\n  CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_send_syn_reply(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  spdylay_frame *frame = malloc(sizeof(spdylay_frame));\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, NULL));\n  spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_frame_syn_reply_init(&frame->syn_reply, SPDYLAY_PROTO_SPDY2,\n                               SPDYLAY_CTRL_FLAG_NONE, 2, dup_nv(nv));\n  spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  CU_ASSERT(0 == spdylay_session_send(session));\n  stream = spdylay_session_get_stream(session, 2);\n  CU_ASSERT(SPDYLAY_STREAM_OPENED == stream->state);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_response(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \"Content-Length\", \"1024\", NULL };\n  int32_t stream_id = 2;\n  spdylay_data_provider data_prd;\n  my_user_data ud;\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n  ud.data_source_length = 64*1024;\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  spdylay_session_open_stream(session, stream_id, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  CU_ASSERT(0 == spdylay_submit_response(session, stream_id, nv, &data_prd));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\"content-length\", OB_CTRL(item)->syn_reply.nv[0]));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_response_with_null_data_read_callback(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  accumulator acc;\n  const char *nv[] = { \":Version\", \"HTTP/1.1\", NULL };\n  spdylay_data_provider data_prd = {{-1}, NULL};\n  spdylay_outbound_item *item;\n  my_user_data ud;\n  spdylay_frame frame;\n\n  acc.length = 0;\n  ud.acc = &acc;\n  memset(&callbacks, 0, sizeof(callbacks));\n  callbacks.send_callback = accumulator_send_callback;\n  CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_FIN, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  CU_ASSERT(0 == spdylay_submit_response(session, 1, nv, &data_prd));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\":version\", OB_CTRL(item)->syn_reply.nv[0]));\n  CU_ASSERT(OB_CTRL(item)->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN);\n\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_REPLY,\n                                            SPDYLAY_PROTO_SPDY2,\n                                            &frame,\n                                            &session->hd_inflater,\n                                            acc.buf, acc.length));\n  CU_ASSERT(0 == strcmp(\"version\", frame.syn_reply.nv[0]));\n  spdylay_frame_syn_reply_free(&frame.syn_reply);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_request_with_data(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \"Version\", \"HTTP/1.1\", NULL };\n  spdylay_data_provider data_prd;\n  my_user_data ud;\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n  ud.data_source_length = 64*1024;\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  CU_ASSERT(0 == spdylay_submit_request(session, 3, nv, &data_prd, NULL));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\"version\", OB_CTRL(item)->syn_stream.nv[0]));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == ud.data_source_length);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_request_with_null_data_read_callback(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  accumulator acc;\n  const char *nv[] = { \":Version\", \"HTTP/1.1\", NULL };\n  spdylay_data_provider data_prd = {{-1}, NULL};\n  spdylay_outbound_item *item;\n  my_user_data ud;\n  spdylay_frame frame;\n\n  acc.length = 0;\n  ud.acc = &acc;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = accumulator_send_callback;\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  CU_ASSERT(0 == spdylay_submit_request(session, 3, nv, &data_prd, NULL));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\":version\", OB_CTRL(item)->syn_stream.nv[0]));\n  CU_ASSERT(OB_CTRL(item)->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN);\n\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM,\n                                            SPDYLAY_PROTO_SPDY2,\n                                            &frame,\n                                            &session->hd_inflater,\n                                            acc.buf, acc.length));\n  CU_ASSERT(0 == strcmp(\"version\", frame.syn_stream.nv[0]));\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_syn_stream(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \"version\", \"HTTP/1.1\", NULL };\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, NULL));\n  CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 3,\n                                           nv, NULL));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\"version\", OB_CTRL(item)->syn_stream.nv[0]));\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == OB_CTRL(item)->syn_stream.hd.flags);\n  /* See assoc-stream-ID is ignored */\n  CU_ASSERT(0 == OB_CTRL(item)->syn_stream.assoc_stream_id);\n  CU_ASSERT(3 == OB_CTRL(item)->syn_stream.pri);\n\n  spdylay_session_del(session);\n\n  CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, NULL));\n  CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 3,\n                                           nv, NULL));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\"version\", OB_CTRL(item)->syn_stream.nv[0]));\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == OB_CTRL(item)->syn_stream.hd.flags);\n  CU_ASSERT(1 == OB_CTRL(item)->syn_stream.assoc_stream_id);\n  CU_ASSERT(3 == OB_CTRL(item)->syn_stream.pri);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_syn_reply(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \"version\", \"HTTP/1.1\", NULL };\n  my_user_data ud;\n  spdylay_outbound_item *item;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n\n  CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  CU_ASSERT(0 == spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1,\n                                          nv));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\"version\", OB_CTRL(item)->syn_reply.nv[0]));\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == OB_CTRL(item)->syn_reply.hd.flags);\n\n  ud.ctrl_send_cb_called = 0;\n  ud.sent_frame_type = 0;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == ud.ctrl_send_cb_called);\n\n  stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                                       SPDYLAY_STREAM_OPENING, NULL);\n\n  CU_ASSERT(0 == spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1,\n                                          nv));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == ud.ctrl_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type);\n  CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_headers(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \":Version\", \"HTTP/1.1\", NULL };\n  my_user_data ud;\n  spdylay_outbound_item *item;\n  spdylay_stream *stream;\n  accumulator acc;\n  spdylay_frame frame;\n\n  acc.length = 0;\n  ud.acc = &acc;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = accumulator_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  CU_ASSERT(0 == spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(0 == strcmp(\":version\", OB_CTRL(item)->headers.nv[0]));\n  CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == OB_CTRL(item)->headers.hd.flags);\n\n  ud.ctrl_send_cb_called = 0;\n  ud.sent_frame_type = 0;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == ud.ctrl_send_cb_called);\n\n  stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                                       SPDYLAY_STREAM_OPENING, NULL);\n\n  CU_ASSERT(0 == spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == ud.ctrl_send_cb_called);\n  CU_ASSERT(SPDYLAY_HEADERS == ud.sent_frame_type);\n  CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR);\n\n  CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_HEADERS,\n                                            SPDYLAY_PROTO_SPDY2,\n                                            &frame,\n                                            &session->hd_inflater,\n                                            acc.buf, acc.length));\n  CU_ASSERT(0 == strcmp(\"version\", frame.headers.nv[0]));\n  spdylay_frame_headers_free(&frame.headers);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_invalid_nv(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,\n                                            &callbacks, NULL));\n\n  /* spdylay_submit_request */\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_request(session, 3, empty_name_nv, NULL, NULL));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_request(session, 3, null_val_nv, NULL, NULL));\n\n  /* spdylay_submit_response */\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_response(session, 2, empty_name_nv, NULL));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_response(session, 2, null_val_nv, NULL));\n\n  /* spdylay_submit_syn_stream */\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_NONE, 0,\n                                      0, empty_name_nv, NULL));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_NONE, 0,\n                                      0, null_val_nv, NULL));\n\n  /* spdylay_submit_syn_reply */\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_NONE, 2,\n                                     empty_name_nv));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_NONE, 2,\n                                     null_val_nv));\n\n  /* spdylay_submit_headers */\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_NONE, 2,\n                                   empty_name_nv));\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_NONE, 2,\n                                   null_val_nv));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_reply_fail(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  int32_t stream_id = 2;\n  spdylay_data_provider data_prd;\n  my_user_data ud;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = fail_send_callback;\n\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n  ud.data_source_length = 4*1024;\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                            &callbacks, &ud));\n  CU_ASSERT(0 == spdylay_submit_response(session, stream_id, nv, &data_prd));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_headers_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  spdylay_frame frame;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_stream_shutdown(spdylay_session_get_stream(session, 1),\n                          SPDYLAY_SHUT_WR);\n  spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2,\n                             SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(SPDYLAY_STREAM_OPENED ==\n            spdylay_session_get_stream(session, 1)->state);\n\n  frame.headers.hd.flags |= SPDYLAY_CTRL_FLAG_FIN;\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(2 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 1));\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  /* Check to see when SPDYLAY_STREAM_CLOSING, incoming HEADERS is\n     discarded. */\n  spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_CLOSING, NULL);\n  frame.headers.stream_id = 3;\n  frame.headers.hd.flags = SPDYLAY_CTRL_FLAG_NONE;\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(2 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called);\n\n  /* Server initiated stream */\n  spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_OPENING, NULL);\n\n  frame.headers.hd.flags = SPDYLAY_CTRL_FLAG_FIN;\n  frame.headers.stream_id = 2;\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(3 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(SPDYLAY_STREAM_OPENING ==\n            spdylay_session_get_stream(session, 2)->state);\n  CU_ASSERT(spdylay_session_get_stream(session, 2)->shut_flags &\n            SPDYLAY_SHUT_RD);\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called);\n\n  spdylay_frame_headers_free(&frame.headers);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_window_update_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_frame frame;\n  spdylay_stream *stream;\n  spdylay_outbound_item *data_item;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,\n                             &user_data);\n  stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0,\n                                       SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1, 16*1024);\n\n  CU_ASSERT(0 == spdylay_session_on_window_update_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(64*1024+16*1024 == stream->window_size);\n\n  data_item = malloc(sizeof(spdylay_outbound_item));\n  memset(data_item, 0, sizeof(spdylay_outbound_item));\n  data_item->frame_cat = SPDYLAY_DATA;\n  spdylay_stream_defer_data(stream, data_item, SPDYLAY_DEFERRED_FLOW_CONTROL);\n\n  CU_ASSERT(0 == spdylay_session_on_window_update_received(session, &frame));\n  CU_ASSERT(2 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(64*1024+16*1024*2 == stream->window_size);\n  CU_ASSERT(NULL == stream->deferred_data);\n\n  spdylay_frame_window_update_free(&frame.window_update);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_ping_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_frame frame;\n  spdylay_outbound_item *top;\n  uint32_t unique_id;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  unique_id = 2;\n  spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY2, unique_id);\n\n  CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  top = spdylay_session_get_ob_pq_top(session);\n  CU_ASSERT(SPDYLAY_PING == OB_CTRL_TYPE(top));\n  CU_ASSERT(unique_id == OB_CTRL(top)->ping.unique_id);\n\n  session->last_ping_unique_id = 1;\n  frame.ping.unique_id = 1;\n\n  CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame));\n  CU_ASSERT(2 == user_data.ctrl_recv_cb_called);\n\n  spdylay_frame_ping_free(&frame.ping);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_goaway_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_frame frame;\n  int32_t stream_id = 1000000007;\n  user_data.ctrl_recv_cb_called = 0;\n  user_data.invalid_ctrl_recv_cb_called = 0;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;\n  callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY2, stream_id,\n                            SPDYLAY_GOAWAY_OK);\n\n  CU_ASSERT(0 == spdylay_session_on_goaway_received(session, &frame));\n  CU_ASSERT(1 == user_data.ctrl_recv_cb_called);\n  CU_ASSERT(session->goaway_flags == SPDYLAY_GOAWAY_RECV);\n\n  spdylay_frame_goaway_free(&frame.goaway);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_data_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_outbound_item *top;\n  int32_t stream_id = 2;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  stream = spdylay_session_open_stream(session, stream_id,\n                                       SPDYLAY_CTRL_FLAG_NONE,\n                                       3, SPDYLAY_STREAM_OPENING, NULL);\n  CU_ASSERT(0 == spdylay_session_on_data_received(session,\n                                                  SPDYLAY_DATA_FLAG_NONE,\n                                                  4096, stream_id));\n  CU_ASSERT(0 == stream->shut_flags);\n\n  CU_ASSERT(0 == spdylay_session_on_data_received(session,\n                                                  SPDYLAY_DATA_FLAG_FIN,\n                                                  4096, stream_id));\n  CU_ASSERT(SPDYLAY_SHUT_RD == stream->shut_flags);\n\n  /* If SPDYLAY_STREAM_CLOSING state, DATA frame is discarded. */\n  stream_id = 4;\n\n  spdylay_session_open_stream(session, stream_id, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_CLOSING, NULL);\n  CU_ASSERT(0 == spdylay_session_on_data_received(session,\n                                                  SPDYLAY_DATA_FLAG_NONE,\n                                                  4096, stream_id));\n  CU_ASSERT(NULL == spdylay_session_get_ob_pq_top(session));\n\n  /* Check INVALID_STREAM case: DATA frame with stream ID which does\n     not exist. */\n  stream_id = 6;\n\n  CU_ASSERT(0 == spdylay_session_on_data_received(session,\n                                                  SPDYLAY_DATA_FLAG_NONE,\n                                                  4096, stream_id));\n  /* In this case, no RST_STREAM */\n  top = spdylay_session_get_ob_pq_top(session);\n  CU_ASSERT(NULL == top);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_is_my_stream_id(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n\n  CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0));\n  CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 1));\n  CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 2));\n\n  spdylay_session_del(session);\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n\n  CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0));\n  CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 1));\n  CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 2));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_rst_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_stream *stream;\n  spdylay_frame frame;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,\n                                       3, SPDYLAY_STREAM_OPENING, NULL);\n  /* server push */\n  spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_stream_add_pushed_stream(stream, 2);\n  spdylay_session_open_stream(session, 4, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_stream_add_pushed_stream(stream, 4);\n\n  spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY2, 1,\n                                SPDYLAY_CANCEL);\n\n  CU_ASSERT(0 == spdylay_session_on_rst_stream_received(session, &frame));\n\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 1));\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 2));\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 4));\n\n  spdylay_frame_rst_stream_free(&frame.rst_stream);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_send_rst_stream(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_stream *stream;\n  spdylay_frame *frame;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,\n                                       3, SPDYLAY_STREAM_OPENING, NULL);\n  /* server push */\n  spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_stream_add_pushed_stream(stream, 2);\n  spdylay_session_open_stream(session, 4, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_stream_add_pushed_stream(stream, 4);\n\n  frame = malloc(sizeof(spdylay_frame));\n  spdylay_frame_rst_stream_init(&frame->rst_stream, SPDYLAY_PROTO_SPDY2, 1,\n                                SPDYLAY_CANCEL);\n  spdylay_session_add_frame(session, SPDYLAY_CTRL, frame, NULL);\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 1));\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 2));\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 4));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_get_next_ob_item(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n  session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2;\n\n  CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session));\n  spdylay_submit_ping(session);\n  CU_ASSERT(SPDYLAY_PING ==\n            OB_CTRL_TYPE(spdylay_session_get_next_ob_item(session)));\n\n  spdylay_submit_request(session, 0, nv, NULL, NULL);\n  CU_ASSERT(SPDYLAY_PING ==\n            OB_CTRL_TYPE(spdylay_session_get_next_ob_item(session)));\n\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session));\n\n  /* Incoming stream does not affect the number of outgoing max\n     concurrent streams. */\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n\n  spdylay_submit_request(session, 0, nv, NULL, NULL);\n  CU_ASSERT(SPDYLAY_SYN_STREAM ==\n            OB_CTRL_TYPE(spdylay_session_get_next_ob_item(session)));\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  spdylay_submit_request(session, 0, nv, NULL, NULL);\n  CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session));\n\n  session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 3;\n\n  CU_ASSERT(SPDYLAY_SYN_STREAM ==\n            OB_CTRL_TYPE(spdylay_session_get_next_ob_item(session)));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_pop_next_ob_item(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  spdylay_outbound_item *item;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n  session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;\n\n  CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session));\n  spdylay_submit_ping(session);\n  spdylay_submit_request(session, 1, nv, NULL, NULL);\n\n  item = spdylay_session_pop_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_PING == OB_CTRL_TYPE(item));\n  spdylay_outbound_item_free(item);\n  free(item);\n\n  item = spdylay_session_pop_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_SYN_STREAM == OB_CTRL_TYPE(item));\n  spdylay_outbound_item_free(item);\n  free(item);\n\n  CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session));\n\n  /* Incoming stream does not affect the number of outgoing max\n     concurrent streams. */\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n\n  /* In-flight outgoing stream */\n  spdylay_session_open_stream(session, 4, SPDYLAY_CTRL_FLAG_NONE,\n                              3, SPDYLAY_STREAM_OPENING, NULL);\n\n  spdylay_submit_request(session, 0, nv, NULL, NULL);\n  spdylay_submit_response(session, 1, nv, NULL);\n\n  item = spdylay_session_pop_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == OB_CTRL_TYPE(item));\n  spdylay_outbound_item_free(item);\n  free(item);\n\n  CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session));\n\n  session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2;\n\n  item = spdylay_session_pop_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_SYN_STREAM == OB_CTRL_TYPE(item));\n  spdylay_outbound_item_free(item);\n  free(item);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_request_recv_callback(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  const char *nv[] = { NULL };\n  spdylay_frame frame;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_request_recv_callback = on_request_recv_callback;\n  user_data.stream_id = 0;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv));\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(0 == user_data.stream_id);\n\n  frame.syn_stream.stream_id = 3;\n  frame.syn_stream.hd.flags |= SPDYLAY_CTRL_FLAG_FIN;\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(3 == user_data.stream_id);\n\n  user_data.stream_id = 0;\n\n  frame.syn_stream.stream_id = 0;\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n  CU_ASSERT(0 == user_data.stream_id);\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  user_data.stream_id = 0;\n\n  spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2,\n                             SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(0 == user_data.stream_id);\n\n  frame.headers.hd.flags |= SPDYLAY_CTRL_FLAG_FIN;\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(5 == user_data.stream_id);\n\n  user_data.stream_id = 0;\n\n  CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));\n  CU_ASSERT(0 == user_data.stream_id);\n\n  spdylay_frame_headers_free(&frame.headers);\n  spdylay_session_del(session);\n}\n\nstatic void stream_close_callback(spdylay_session *session, int32_t stream_id,\n                                  spdylay_status_code status_code _U_,\n                                  void *user_data)\n{\n  my_user_data* my_data = (my_user_data*)user_data;\n  void *stream_data = spdylay_session_get_stream_user_data(session, stream_id);\n  ++my_data->stream_close_cb_called;\n  CU_ASSERT(stream_data != NULL);\n}\n\nvoid test_spdylay_session_on_stream_close(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_stream *stream;\n  int32_t stream_id = 1;\n  uint8_t pri = 3;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_stream_close_callback = stream_close_callback;\n  user_data.stream_close_cb_called = 0;\n\n  CU_ASSERT(spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                       &callbacks, &user_data) == 0);\n  stream = spdylay_session_open_stream(session, stream_id,\n                                       SPDYLAY_CTRL_FLAG_NONE,\n                                       pri, SPDYLAY_STREAM_OPENED, &user_data);\n  CU_ASSERT(stream != NULL);\n  CU_ASSERT(spdylay_session_close_stream(session, stream_id, SPDYLAY_OK) == 0);\n  CU_ASSERT(user_data.stream_close_cb_called == 1);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_max_concurrent_streams(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  spdylay_frame frame;\n  const char *nv[] = { NULL };\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENED, NULL);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 3, 0, 3, dup_nv(nv));\n  session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n\n  item = spdylay_session_get_ob_pq_top(session);\n  CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));\n  CU_ASSERT(SPDYLAY_REFUSED_STREAM == OB_CTRL(item)->rst_stream.status_code)\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  spdylay_session_del(session);\n}\n\nstatic ssize_t block_count_send_callback(spdylay_session* session _U_,\n                                         const uint8_t *data _U_, size_t len,\n                                         int flags _U_,\n                                         void *user_data)\n{\n  my_user_data *ud = (my_user_data*)user_data;\n  int r;\n  if(ud->block_count == 0) {\n    r = SPDYLAY_ERR_WOULDBLOCK;\n  } else {\n    --ud->block_count;\n    r = (int)len;\n  }\n  return r;\n}\n\nvoid test_spdylay_session_data_backoff_by_high_pri_frame(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = block_count_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n\n  ud.ctrl_send_cb_called = 0;\n  ud.data_source_length = 16*1024;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud);\n  spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n\n  ud.block_count = 2;\n  /* Sends SYN_STREAM + DATA[0] */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(SPDYLAY_SYN_STREAM == ud.sent_frame_type);\n  /* data for DATA[1] is read from data_prd but it is not sent */\n  CU_ASSERT(ud.data_source_length == 8*1024);\n\n  spdylay_submit_ping(session);\n  ud.block_count = 2;\n  /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(SPDYLAY_PING == ud.sent_frame_type);\n  /* data for DATA[2] is read from data_prd but it is not sent */\n  CU_ASSERT(ud.data_source_length == 4*1024);\n\n  ud.block_count = 2;\n  /* Sends DATA[2..3] */\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  stream = spdylay_session_get_stream(session, 1);\n  CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_stop_data_with_rst_stream(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_frame frame;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  callbacks.send_callback = block_count_send_callback;\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n\n  ud.ctrl_send_cb_called = 0;\n  ud.data_source_length = 16*1024;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_submit_response(session, 1, nv, &data_prd);\n\n  ud.block_count = 2;\n  /* Sends SYN_REPLY + DATA[0] */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type);\n  /* data for DATA[1] is read from data_prd but it is not sent */\n  CU_ASSERT(ud.data_source_length == 8*1024);\n\n  spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY2, 1,\n                                SPDYLAY_CANCEL);\n  CU_ASSERT(0 == spdylay_session_on_rst_stream_received(session, &frame));\n  spdylay_frame_rst_stream_free(&frame.rst_stream);\n\n  /* Big enough number to send all DATA frames potentially. */\n  ud.block_count = 100;\n  /* Nothing will be sent in the following call. */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  /* With RST_STREAM, stream is canceled and further DATA on that\n     stream are not sent. */\n  CU_ASSERT(ud.data_source_length == 8*1024);\n\n  CU_ASSERT(NULL == spdylay_session_get_stream(session, 1));\n\n  spdylay_session_del(session);\n}\n\n/*\n * Check that on_stream_close_callback is called when server pushed\n * SYN_STREAM have SPDYLAY_CTRL_FLAG_FIN.\n */\nvoid test_spdylay_session_stream_close_on_syn_stream(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_frame frame;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_stream_close_callback =\n    no_stream_user_data_stream_close_callback;\n  ud.stream_close_cb_called = 0;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_FIN |\n                                SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL,\n                                2, 1, 3, dup_nv(nv));\n\n  CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));\n\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_recv_invalid_frame(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  scripted_data_feed df;\n  my_user_data user_data;\n  const char *nv[] = {\n    \"url\", \"/\", NULL\n  };\n  uint8_t *framedata = NULL, *nvbuf = NULL;\n  size_t framedatalen = 0, nvbuflen = 0;\n  ssize_t framelen;\n  spdylay_frame frame;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.recv_callback = scripted_recv_callback;\n  callbacks.send_callback = null_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n\n  user_data.df = &df;\n  user_data.ctrl_send_cb_called = 0;\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,\n                             &user_data);\n  spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,\n                                SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv));\n  framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,\n                                           &nvbuf, &nvbuflen,\n                                           &frame.syn_stream,\n                                           &session->hd_deflater);\n  scripted_data_feed_init(&df, framedata, framelen);\n\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == user_data.ctrl_send_cb_called);\n\n  /* Receive exactly same bytes of SYN_STREAM causes error */\n  scripted_data_feed_init(&df, framedata, framelen);\n\n  CU_ASSERT(0 == spdylay_session_recv(session));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_send_cb_called);\n  CU_ASSERT(SPDYLAY_GOAWAY == user_data.sent_frame_type);\n\n  free(framedata);\n  free(nvbuf);\n  spdylay_frame_syn_stream_free(&frame.syn_stream);\n\n  spdylay_session_del(session);\n}\n\nstatic ssize_t defer_data_source_read_callback\n(spdylay_session *session _U_, int32_t stream_id _U_,\n uint8_t *buf _U_, size_t len _U_, int *eof _U_,\n spdylay_data_source *source _U_, void *user_data _U_)\n{\n  return SPDYLAY_ERR_DEFERRED;\n}\n\nvoid test_spdylay_session_defer_data(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_outbound_item *item;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  callbacks.send_callback = block_count_send_callback;\n  data_prd.read_callback = defer_data_source_read_callback;\n\n  ud.ctrl_send_cb_called = 0;\n  ud.data_source_length = 16*1024;\n\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud);\n  spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3,\n                              SPDYLAY_STREAM_OPENING, NULL);\n  spdylay_submit_response(session, 1, nv, &data_prd);\n\n  ud.block_count = 1;\n  /* Sends SYN_REPLY */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type);\n  /* No data is read */\n  CU_ASSERT(ud.data_source_length == 16*1024);\n\n  ud.block_count = 1;\n  spdylay_submit_ping(session);\n  /* Sends PING */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(SPDYLAY_PING == ud.sent_frame_type);\n\n  /* Resume deferred DATA */\n  CU_ASSERT(0 == spdylay_session_resume_data(session, 1));\n  item = spdylay_session_get_ob_pq_top(session);\n  OB_DATA(item)->data_prd.read_callback =\n    fixed_length_data_source_read_callback;\n  ud.block_count = 1;\n  /* Reads 2 4KiB blocks */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(ud.data_source_length == 8*1024);\n\n  /* Deferred again */\n  OB_DATA(item)->data_prd.read_callback = defer_data_source_read_callback;\n  /* This is needed since 4KiB block is already read and waiting to be\n     sent. No read_callback invocation. */\n  ud.block_count = 1;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(ud.data_source_length == 8*1024);\n\n  /* Resume deferred DATA */\n\n  CU_ASSERT(0 == spdylay_session_resume_data(session, 1));\n  item = spdylay_session_get_ob_pq_top(session);\n  OB_DATA(item)->data_prd.read_callback =\n    fixed_length_data_source_read_callback;\n  ud.block_count = 1;\n  /* Reads 2 4KiB blocks */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(ud.data_source_length == 0);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_flow_control(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_frame frame;\n  spdylay_stream *stream;\n  int32_t new_initial_window_size;\n  spdylay_settings_entry iv[1];\n  spdylay_frame settings_frame;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = fixed_bytes_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n\n  ud.ctrl_send_cb_called = 0;\n  ud.data_source_length = 128*1024;\n  /* Use smaller emission count so that we can check outbound flow\n     control window calculation is correct. */\n  ud.fixed_sendlen = 2*1024;\n\n  /* Initial window size is 64KiB */\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);\n  spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n\n  /* Sends 64KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(64*1024 == ud.data_source_length);\n\n  /* Back 32KiB */\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1, 32*1024);\n  spdylay_session_on_window_update_received(session, &frame);\n\n  /* Sends another 32KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(32*1024 == ud.data_source_length);\n\n  stream = spdylay_session_get_stream(session, 1);\n  /* Change initial window size to 16KiB. The window_size becomes\n     negative. */\n  new_initial_window_size = 16*1024;\n  stream->window_size = new_initial_window_size-\n    (session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]\n     -stream->window_size);\n  session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] =\n    new_initial_window_size;\n  CU_ASSERT(-48*1024 == stream->window_size);\n\n  /* Back 48KiB */\n  frame.window_update.delta_window_size = 48*1024;\n  spdylay_session_on_window_update_received(session, &frame);\n\n  /* Nothing is sent because window_size is less than 0 */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(32*1024 == ud.data_source_length);\n\n  /* Back 16KiB */\n  frame.window_update.delta_window_size = 16*1024;\n  spdylay_session_on_window_update_received(session, &frame);\n\n  /* Sends another 16KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(16*1024 == ud.data_source_length);\n\n  /* Increase initial window size to 32KiB */\n  iv[0].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n  iv[0].value = 32*1024;\n\n  spdylay_frame_settings_init(&settings_frame.settings, SPDYLAY_PROTO_SPDY3,\n                              SPDYLAY_FLAG_SETTINGS_NONE, dup_iv(iv, 1), 1);\n  spdylay_session_on_settings_received(session, &settings_frame);\n  spdylay_frame_settings_free(&settings_frame.settings);\n\n  /* Sends another 16KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == ud.data_source_length);\n  CU_ASSERT(spdylay_session_get_stream(session, 1)->shut_flags &\n            SPDYLAY_SHUT_WR);\n\n  spdylay_frame_window_update_free(&frame.window_update);\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_connection_flow_control(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_frame frame;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = fixed_bytes_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n\n  ud.ctrl_send_cb_called = 0;\n  ud.data_source_length = 128*1024;\n  /* Use smaller emission count so that we can check outbound flow\n     control window calculation is correct. */\n  ud.fixed_sendlen = 2*1024;\n\n  /* Initial window size is 64KiB for both stream- and\n     connection-level flow control */\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3_1, &callbacks, &ud);\n  spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n\n  /* Sends 64KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(64*1024 == ud.data_source_length);\n\n  /* Back 32KiB to stream-level window size */\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1, 32*1024);\n  spdylay_session_on_window_update_received(session, &frame);\n\n  /* Nothing sent because of connection-level flow control */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(64*1024 == ud.data_source_length);\n\n  /* Back 32KiB to connection-level window size */\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   0, 32*1024);\n  spdylay_session_on_window_update_received(session, &frame);\n\n  /* Sends another 32KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(32*1024 == ud.data_source_length);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_ctrl_not_send(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_stream *stream;\n  const char *nv[] = { NULL };\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback;\n  callbacks.send_callback = null_send_callback;\n  user_data.ctrl_not_send_cb_called = 0;\n  user_data.not_sent_frame_type = 0;\n  user_data.not_sent_error = 0;\n\n  CU_ASSERT(spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2,\n                                       &callbacks, &user_data) == 0);\n  stream = spdylay_session_open_stream(session, 1,\n                                       SPDYLAY_CTRL_FLAG_NONE,\n                                       3, SPDYLAY_STREAM_OPENED, &user_data);\n  /* Check SYN_STREAM */\n  CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 3, 3,\n                                           nv, NULL));\n\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Associated stream closed */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED == user_data.not_sent_error);\n\n  /* Check SYN_REPLY */\n  CU_ASSERT(0 ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));\n  user_data.ctrl_not_send_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error);\n\n  stream->state = SPDYLAY_STREAM_OPENING;\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Send bogus stream ID */\n  CU_ASSERT(0 ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED == user_data.not_sent_error);\n\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Shutdown transmission */\n  stream->shut_flags |= SPDYLAY_SHUT_WR;\n  CU_ASSERT(0 ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_STREAM_SHUT_WR == user_data.not_sent_error);\n\n  stream->shut_flags = SPDYLAY_SHUT_NONE;\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Queue RST_STREAM */\n  CU_ASSERT(0 ==\n            spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));\n  CU_ASSERT(0 == spdylay_submit_rst_stream(session, 1, SPDYLAY_INTERNAL_ERROR));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error);\n\n  stream = spdylay_session_open_stream(session, 3,\n                                       SPDYLAY_CTRL_FLAG_NONE,\n                                       3, SPDYLAY_STREAM_OPENED, &user_data);\n\n  /* Check HEADERS */\n  user_data.ctrl_not_send_cb_called = 0;\n  stream->state = SPDYLAY_STREAM_OPENING;\n  CU_ASSERT(0 ==\n            spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error);\n\n  stream->state = SPDYLAY_STREAM_OPENED;\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Queue RST_STREAM */\n  CU_ASSERT(0 ==\n            spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));\n  CU_ASSERT(0 == spdylay_submit_rst_stream(session, 3, SPDYLAY_INTERNAL_ERROR));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error);\n\n  spdylay_session_del(session);\n\n  /* Check SYN_STREAM */\n  user_data.ctrl_not_send_cb_called = 0;\n  CU_ASSERT(spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,\n                                       &callbacks, &user_data) == 0);\n  /* Maximum Stream ID is reached */\n  session->next_stream_id = (1u << 31)+1;\n  CU_ASSERT(SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE ==\n            spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0, 3, nv,\n                                      NULL));\n\n  session->next_stream_id = 1;\n  user_data.ctrl_not_send_cb_called = 0;\n  /* Send GOAWAY */\n  CU_ASSERT(0 == spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK));\n  CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0,\n                                           3, nv, NULL));\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);\n  CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type);\n  CU_ASSERT(SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED == user_data.not_sent_error);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_on_settings_received(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data user_data;\n  spdylay_stream *stream1, *stream2;\n  spdylay_frame frame;\n  const size_t niv = 5;\n  spdylay_settings_entry iv[255];\n\n  iv[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[0].value = 1000000009;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[1].value = 50;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  iv[2].value = 64*1024;\n  iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  iv[3].settings_id = SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE;\n  iv[3].value = 512;\n  iv[3].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  iv[4].settings_id = 999;\n  iv[4].value = 0;\n  iv[4].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,\n                             &user_data);\n  session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024;\n\n  stream1 = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,\n                                        3, SPDYLAY_STREAM_OPENING, NULL);\n  stream2 = spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE,\n                                        3, SPDYLAY_STREAM_OPENING, NULL);\n  stream1->window_size = 16*1024;\n  stream2->window_size = -48*1024;\n\n  spdylay_frame_settings_init(&frame.settings, SPDYLAY_PROTO_SPDY3,\n                              SPDYLAY_FLAG_SETTINGS_NONE, dup_iv(iv, niv), niv);\n\n  CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame));\n\n  CU_ASSERT(1000000009 ==\n            session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);\n  CU_ASSERT(64*1024 ==\n            session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);\n  CU_ASSERT(64*1024 == stream1->window_size);\n  CU_ASSERT(0 == stream2->window_size);\n\n  frame.settings.iv[2].value = 16*1024;\n\n  CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame));\n\n  CU_ASSERT(16*1024 == stream1->window_size);\n  CU_ASSERT(-48*1024 == stream2->window_size);\n\n  spdylay_frame_settings_free(&frame.settings);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_settings(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data ud;\n  spdylay_outbound_item *item;\n  spdylay_frame *frame;\n  spdylay_settings_entry iv[3];\n\n  iv[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[0].value = 50;\n  iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  iv[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;\n  iv[1].value = 16*1024;\n  iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  /* This is duplicate entry */\n  iv[2].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;\n  iv[2].value = 150;\n  iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_settings(session, SPDYLAY_FLAG_SETTINGS_NONE,\n                                    iv, 3));\n\n  /* Make sure that local settings are not changed */\n  CU_ASSERT(SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS ==\n            session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);\n  CU_ASSERT(SPDYLAY_INITIAL_WINDOW_SIZE ==\n            session->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);\n\n  CU_ASSERT(0 == spdylay_submit_settings(session,\n                                         SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS,\n                                         iv, 2));\n\n  /* Make sure that local settings are changed */\n  CU_ASSERT(50 ==\n            session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);\n  CU_ASSERT(16*1024 ==\n            session->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);\n\n  item = spdylay_session_get_next_ob_item(session);\n\n  CU_ASSERT(SPDYLAY_SETTINGS == OB_CTRL_TYPE(item));\n\n  frame = item->frame;\n  CU_ASSERT(2 == frame->settings.niv);\n  CU_ASSERT(SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS == frame->settings.hd.flags);\n\n  CU_ASSERT(50 == frame->settings.iv[0].value);\n  CU_ASSERT(SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS ==\n            frame->settings.iv[0].settings_id);\n  CU_ASSERT(SPDYLAY_FLAG_SETTINGS_NONE ==\n            frame->settings.iv[0].flags);\n\n  CU_ASSERT(16*1024 == frame->settings.iv[1].value);\n  CU_ASSERT(SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE ==\n            frame->settings.iv[1].settings_id);\n  CU_ASSERT(SPDYLAY_FLAG_SETTINGS_NONE ==\n            frame->settings.iv[1].flags);\n\n  ud.ctrl_send_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(1 == ud.ctrl_send_cb_called);\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_get_outbound_queue_size(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { \"version\", \"HTTP/1.1\", NULL };\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,\n                                            &callbacks, NULL));\n  CU_ASSERT(0 == spdylay_session_get_outbound_queue_size(session));\n\n  CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 7,\n                                           nv, NULL));\n  CU_ASSERT(1 == spdylay_session_get_outbound_queue_size(session));\n\n  CU_ASSERT(0 == spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK));\n  CU_ASSERT(2 == spdylay_session_get_outbound_queue_size(session));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_set_option(void)\n{\n  spdylay_session* session;\n  spdylay_session_callbacks callbacks;\n  int intval;\n  char charval;\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, NULL);\n\n  intval = 1;\n  CU_ASSERT(0 ==\n            spdylay_session_set_option(session,\n                                       SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE,\n                                       &intval, sizeof(intval)));\n  CU_ASSERT(session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE);\n\n  intval = 0;\n  CU_ASSERT(0 ==\n            spdylay_session_set_option(session,\n                                       SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE,\n                                       &intval, sizeof(intval)));\n  CU_ASSERT((session->opt_flags & SPDYLAY_OPTMASK_NO_AUTO_WINDOW_UPDATE) == 0);\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_session_set_option(session, 0, /* 0 is invalid optname */\n                                       &intval, sizeof(intval)));\n\n  charval = 1;\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_session_set_option(session,\n                                       SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE,\n                                       &charval, sizeof(charval)));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_submit_window_update(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  int32_t stream_id = 2;\n  my_user_data ud;\n  spdylay_outbound_item *item;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,\n                             &callbacks, &ud);\n  stream = spdylay_session_open_stream(session, stream_id,\n                                       SPDYLAY_CTRL_FLAG_NONE, 3,\n                                       SPDYLAY_STREAM_OPENED, NULL);\n  stream->recv_window_size = 4096;\n\n  CU_ASSERT(0 == spdylay_submit_window_update(session, stream_id, 1024));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_WINDOW_UPDATE == OB_CTRL_TYPE(item));\n  CU_ASSERT(1024 == OB_CTRL(item)->window_update.delta_window_size);\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(3072 == stream->recv_window_size);\n\n  CU_ASSERT(0 == spdylay_submit_window_update(session, stream_id, 4096));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_WINDOW_UPDATE == OB_CTRL_TYPE(item));\n  CU_ASSERT(4096 == OB_CTRL(item)->window_update.delta_window_size);\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == stream->recv_window_size);\n\n  CU_ASSERT(0 == spdylay_submit_window_update(session, stream_id, 4096));\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(SPDYLAY_WINDOW_UPDATE == OB_CTRL_TYPE(item));\n  CU_ASSERT(4096 == OB_CTRL(item)->window_update.delta_window_size);\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(0 == stream->recv_window_size);\n\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_window_update(session, stream_id, 0));\n  CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT ==\n            spdylay_submit_window_update(session, stream_id, -1));\n  CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED ==\n            spdylay_submit_window_update(session, 4, 4096));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_data_read_temporal_failure(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  const char *nv[] = { NULL };\n  my_user_data ud;\n  spdylay_data_provider data_prd;\n  spdylay_frame frame;\n  spdylay_data *data_frame;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.on_ctrl_send_callback = on_ctrl_send_callback;\n  data_prd.read_callback = fixed_length_data_source_read_callback;\n\n  ud.data_source_length = 128*1024;\n\n  /* Initial window size is 64KiB */\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);\n  spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n\n  /* Sends 64KiB data */\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(64*1024 == ud.data_source_length);\n\n  stream = spdylay_session_get_stream(session, 1);\n  CU_ASSERT(NULL != stream->deferred_data);\n  CU_ASSERT(SPDYLAY_DATA == stream->deferred_data->frame_cat);\n  data_frame = (spdylay_data*)stream->deferred_data->frame;\n  data_frame->data_prd.read_callback =\n    temporal_failure_data_source_read_callback;\n\n  /* Back 64KiB */\n  spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,\n                                   1, 64*1024);\n  spdylay_session_on_window_update_received(session, &frame);\n  spdylay_frame_window_update_free(&frame.window_update);\n\n  /* Sending data will fail */\n  ud.ctrl_send_cb_called = 0;\n  CU_ASSERT(0 == spdylay_session_send(session));\n  CU_ASSERT(64*1024 == ud.data_source_length);\n\n  CU_ASSERT(1 == ud.ctrl_send_cb_called);\n  CU_ASSERT(SPDYLAY_RST_STREAM == ud.sent_frame_type);\n\n  data_prd.read_callback = fail_data_source_read_callback;\n  spdylay_submit_request(session, 3, nv, &data_prd, NULL);\n  /* Sending data will fail */\n  CU_ASSERT(SPDYLAY_ERR_CALLBACK_FAILURE == spdylay_session_send(session));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_recv_eof(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.recv_callback = eof_recv_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3,\n                             &callbacks, NULL);\n\n  CU_ASSERT(SPDYLAY_ERR_EOF == spdylay_session_recv(session));\n\n  spdylay_session_del(session);\n}\n\nvoid test_spdylay_session_recv_data(void)\n{\n  spdylay_session *session;\n  spdylay_session_callbacks callbacks;\n  my_user_data ud;\n  uint8_t data[8092];\n  int rv;\n  spdylay_outbound_item *item;\n  spdylay_stream *stream;\n\n  memset(&callbacks, 0, sizeof(spdylay_session_callbacks));\n  callbacks.send_callback = null_send_callback;\n  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;\n  callbacks.on_data_recv_callback = on_data_recv_callback;\n\n  spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);\n\n  /* Create DATA frame with length 4KiB */\n  memset(data, 0, sizeof(data));\n  spdylay_put_uint32be(data, 1);\n  spdylay_put_uint32be(data+4, 4096);\n\n  /* stream 1 is not opened, and it is ignored. */\n  ud.data_chunk_recv_cb_called = 0;\n  ud.data_recv_cb_called = 0;\n  rv = (int)spdylay_session_mem_recv(session, data, 8+4096);\n  CU_ASSERT(8+4096 == rv);\n\n  CU_ASSERT(0 == ud.data_chunk_recv_cb_called);\n  CU_ASSERT(0 == ud.data_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(NULL == item);\n\n  CU_ASSERT(0 == spdylay_session_send(session));\n\n  /* Create stream 1 with CLOSING state. It is ignored. */\n  stream = spdylay_session_open_stream(session, 1,\n                                       SPDYLAY_CTRL_FLAG_NONE, 3,\n                                       SPDYLAY_STREAM_CLOSING, NULL);\n\n  ud.data_chunk_recv_cb_called = 0;\n  ud.data_recv_cb_called = 0;\n  rv = (int)spdylay_session_mem_recv(session, data, 8+4096);\n  CU_ASSERT(8+4096 == rv);\n\n  CU_ASSERT(0 == ud.data_chunk_recv_cb_called);\n  CU_ASSERT(0 == ud.data_recv_cb_called);\n  item = spdylay_session_get_next_ob_item(session);\n  CU_ASSERT(NULL == item);\n\n  /* This is normal case. DATA is acceptable. */\n  stream->state = SPDYLAY_STREAM_OPENED;\n\n  ud.data_chunk_recv_cb_called = 0;\n  ud.data_recv_cb_called = 0;\n  rv = (int)spdylay_session_mem_recv(session, data, 8+4096);\n  CU_ASSERT(8+4096 == rv);\n\n  CU_ASSERT(1 == ud.data_chunk_recv_cb_called);\n  CU_ASSERT(1 == ud.data_recv_cb_called);\n\n  spdylay_session_del(session);\n}\n"
  },
  {
    "path": "tests/spdylay_session_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_SESSION_TEST_H\n#define SPDYLAY_SESSION_TEST_H\n\nvoid test_spdylay_session_recv(void);\nvoid test_spdylay_session_recv_invalid_stream_id(void);\nvoid test_spdylay_session_add_frame(void);\nvoid test_spdylay_session_on_syn_stream_received(void);\nvoid test_spdylay_session_on_syn_stream_received_with_push(void);\nvoid test_spdylay_session_on_syn_reply_received(void);\nvoid test_spdylay_session_on_window_update_received(void);\nvoid test_spdylay_session_send_syn_stream(void);\nvoid test_spdylay_session_send_syn_reply(void);\nvoid test_spdylay_submit_response(void);\nvoid test_spdylay_submit_response_with_null_data_read_callback(void);\nvoid test_spdylay_submit_request_with_data(void);\nvoid test_spdylay_submit_request_with_null_data_read_callback(void);\nvoid test_spdylay_submit_syn_stream(void);\nvoid test_spdylay_submit_syn_reply(void);\nvoid test_spdylay_submit_headers(void);\nvoid test_spdylay_submit_invalid_nv(void);\nvoid test_spdylay_session_reply_fail(void);\nvoid test_spdylay_session_on_headers_received(void);\nvoid test_spdylay_session_on_ping_received(void);\nvoid test_spdylay_session_on_goaway_received(void);\nvoid test_spdylay_session_on_data_received(void);\nvoid test_spdylay_session_on_rst_received(void);\nvoid test_spdylay_session_is_my_stream_id(void);\nvoid test_spdylay_session_send_rst_stream(void);\nvoid test_spdylay_session_get_next_ob_item(void);\nvoid test_spdylay_session_pop_next_ob_item(void);\nvoid test_spdylay_session_on_request_recv_callback(void);\nvoid test_spdylay_session_on_stream_close(void);\nvoid test_spdylay_session_max_concurrent_streams(void);\nvoid test_spdylay_session_data_backoff_by_high_pri_frame(void);\nvoid test_spdylay_session_stop_data_with_rst_stream(void);\nvoid test_spdylay_session_stream_close_on_syn_stream(void);\nvoid test_spdylay_session_recv_invalid_frame(void);\nvoid test_spdylay_session_defer_data(void);\nvoid test_spdylay_session_flow_control(void);\nvoid test_spdylay_session_connection_flow_control(void);\nvoid test_spdylay_session_on_ctrl_not_send(void);\nvoid test_spdylay_session_on_settings_received(void);\nvoid test_spdylay_submit_settings(void);\nvoid test_spdylay_session_get_outbound_queue_size(void);\nvoid test_spdylay_session_prep_credential(void);\nvoid test_spdylay_submit_syn_stream_with_credential(void);\nvoid test_spdylay_session_set_initial_client_cert_origin(void);\nvoid test_spdylay_session_set_option(void);\nvoid test_spdylay_submit_window_update(void);\nvoid test_spdylay_session_data_read_temporal_failure(void);\nvoid test_spdylay_session_recv_eof(void);\nvoid test_spdylay_session_recv_data(void);\n\n#endif /* SPDYLAY_SESSION_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_stream_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_stream_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_stream.h\"\n\nvoid test_spdylay_stream_add_pushed_stream(void)\n{\n  spdylay_stream stream;\n  int i, n;\n  spdylay_stream_init(&stream, 1, SPDYLAY_CTRL_FLAG_NONE, 3, 65536,\n                      SPDYLAY_STREAM_OPENING, NULL);\n  n = 26;\n  for(i = 2; i < n; i += 2) {\n    CU_ASSERT(0 == spdylay_stream_add_pushed_stream(&stream, i));\n    CU_ASSERT((size_t)i/2 == stream.pushed_streams_length);\n  }\n  for(i = 2; i < n; i += 2) {\n    CU_ASSERT(i == stream.pushed_streams[i/2-1]);\n  }\n  spdylay_stream_free(&stream);\n}\n"
  },
  {
    "path": "tests/spdylay_stream_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_STREAM_TEST_H\n#define SPDYLAY_STREAM_TEST_H\n\nvoid test_spdylay_stream_add_pushed_stream(void);\n\n#endif /* SPDYLAY_STREAM_TEST_H */\n"
  },
  {
    "path": "tests/spdylay_test_helper.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_test_helper.h\"\n\n#include <assert.h>\n\n#include <CUnit/CUnit.h>\n\n#include \"spdylay_session.h\"\n\nssize_t unpack_frame_with_nv_block(spdylay_frame_type type,\n                                   uint16_t version,\n                                   spdylay_frame *frame,\n                                   spdylay_zlib *inflater,\n                                   const uint8_t *in, size_t len)\n{\n  spdylay_buffer buffer;\n  ssize_t rv;\n  ssize_t pnvlen;\n  pnvlen = spdylay_frame_nv_offset(type, version) - SPDYLAY_HEAD_LEN;\n  assert(pnvlen > 0);\n\n  spdylay_buffer_init(&buffer, 4096);\n  rv = spdylay_zlib_inflate_hd(inflater, &buffer,\n                               &in[SPDYLAY_HEAD_LEN + pnvlen],\n                               len - SPDYLAY_HEAD_LEN - pnvlen);\n  if(rv < 0) {\n    spdylay_buffer_free(&buffer);\n    return rv;\n  }\n  switch(type) {\n  case SPDYLAY_SYN_STREAM:\n    rv = spdylay_frame_unpack_syn_stream(&frame->syn_stream,\n                                         &in[0], SPDYLAY_HEAD_LEN,\n                                         &in[SPDYLAY_HEAD_LEN], pnvlen,\n                                         &buffer);\n    break;\n  case SPDYLAY_SYN_REPLY:\n    rv = spdylay_frame_unpack_syn_reply(&frame->syn_reply,\n                                        &in[0], SPDYLAY_HEAD_LEN,\n                                        &in[SPDYLAY_HEAD_LEN], pnvlen,\n                                        &buffer);\n    break;\n  case SPDYLAY_HEADERS:\n    rv = spdylay_frame_unpack_headers(&frame->headers,\n                                      &in[0], SPDYLAY_HEAD_LEN,\n                                      &in[SPDYLAY_HEAD_LEN], pnvlen,\n                                      &buffer);\n    break;\n  default:\n    /* Must not be reachable */\n    assert(0);\n  }\n  spdylay_buffer_free(&buffer);\n  return rv;\n}\n"
  },
  {
    "path": "tests/spdylay_test_helper.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_TEST_HELPER_H\n#define SPDYLAY_TEST_HELPER_H\n\n#ifdef HAVE_CONFIG_H\n#  include <config.h>\n#endif /* HAVE_CONFIG_H */\n\n#include \"spdylay_frame.h\"\n#include \"spdylay_zlib.h\"\n\nssize_t unpack_frame_with_nv_block(spdylay_frame_type type,\n                                   uint16_t version,\n                                   spdylay_frame *frame,\n                                   spdylay_zlib *inflater,\n                                   const uint8_t *in, size_t len);\n\n#endif /* SPDYLAY_TEST_HELPER_H */\n"
  },
  {
    "path": "tests/spdylay_zlib_test.c",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#include \"spdylay_zlib_test.h\"\n\n#include <CUnit/CUnit.h>\n\n#include <stdio.h>\n\n#include \"spdylay_zlib.h\"\n\nstatic void test_spdylay_zlib_with(uint16_t version)\n{\n  spdylay_zlib deflater, inflater;\n  const char msg[] =\n    \"GET /chat HTTP/1.1\\r\\n\"\n    \"Host: server.example.com\\r\\n\"\n    \"Upgrade: websocket\\r\\n\"\n    \"Connection: Upgrade\\r\\n\"\n    \"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\\r\\n\"\n    \"Origin: http://example.com\\r\\n\"\n    \"Sec-WebSocket-Protocol: chat, superchat\\r\\n\"\n    \"Sec-WebSocket-Version: 13\\r\\n\"\n    \"\\r\\n\";\n  uint8_t inflatebuf[sizeof(msg)];\n  spdylay_buffer buf;\n  uint8_t *deflatebuf;\n  size_t deflatebuf_max;\n  ssize_t deflatebuf_len;\n  spdylay_buffer_init(&buf, 4096);\n\n  CU_ASSERT(0 == spdylay_zlib_deflate_hd_init(&deflater, 1,\n                                              version));\n  CU_ASSERT(0 == spdylay_zlib_inflate_hd_init(&inflater, version));\n\n  deflatebuf_max = spdylay_zlib_deflate_hd_bound(&deflater, sizeof(msg));\n  deflatebuf = malloc(deflatebuf_max);\n\n  CU_ASSERT(0 < (deflatebuf_len = spdylay_zlib_deflate_hd\n                 (&deflater, deflatebuf, deflatebuf_max,\n                  (const uint8_t*)msg, sizeof(msg))));\n  CU_ASSERT(sizeof(msg) == spdylay_zlib_inflate_hd\n            (&inflater, &buf, deflatebuf, deflatebuf_len));\n  free(deflatebuf);\n  spdylay_buffer_serialize(&buf, inflatebuf);\n\n  spdylay_zlib_deflate_free(&deflater);\n  spdylay_zlib_inflate_free(&inflater);\n\n  spdylay_buffer_free(&buf);\n}\n\nvoid test_spdylay_zlib_spdy2(void)\n{\n  test_spdylay_zlib_with(SPDYLAY_PROTO_SPDY2);\n}\n\nvoid test_spdylay_zlib_spdy3(void)\n{\n  test_spdylay_zlib_with(SPDYLAY_PROTO_SPDY3);\n}\n"
  },
  {
    "path": "tests/spdylay_zlib_test.h",
    "content": "/*\n * Spdylay - SPDY Library\n *\n * Copyright (c) 2012 Tatsuhiro Tsujikawa\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n#ifndef SPDYLAY_ZLIB_TEST_H\n#define SPDYLAY_ZLIB_TEST_H\n\nvoid test_spdylay_zlib_spdy2(void);\nvoid test_spdylay_zlib_spdy3(void);\n\n#endif /* SPDYLAY_ZLIB_TEST_H */\n"
  },
  {
    "path": "tests/testdata/Makefile.am",
    "content": "# Spdylay - SPDY Library\n\n# Copyright (c) 2012 Tatsuhiro Tsujikawa\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nEXTRA_DIST = cacert.pem  index.html  privkey.pem\n"
  },
  {
    "path": "tests/testdata/cacert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDszCCApugAwIBAgIJAIwdWkdWObcIMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk\neSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA\nbG9jYWxob3N0MB4XDTE2MDkxOTEzMTY1M1oXDTE2MTAxOTEzMTY1M1owcDELMAkG\nA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT\ncGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk\neUBsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD6ghRI\n7EZt5wRSsFr69n5gjlTV14cZD/N4OruuYAFvZH9rPc+q3LnQ/aWN5iHO94k9Uq33\nENGhpDFHbhMJMqWinyZabmjH0pbXRRELLCWqz3E0nJBvQWB7vGT4glxJozHOmFzw\nid0zUMedjLR6XxJzP2TMJv7sZgmJd7wf2F5iTvQCq5BId16/rnuA2lIZtzCCNIgn\np4Hs6BktigFrO7A2xuJHuys1bXS6M2PeiuuN/3B2KV/bzZ3Ylw7u1UYIBBgjpon9\nFwcGFoln8cXVadb3ZIsWNBgP2dhXSk5vDl8t7KVdTbcC/uNysQvoCuglJEwXg/7B\nSf3/rpnEcKF+ChFpAgMBAAGjUDBOMB0GA1UdDgQWBBSPX3h2tbo3axwLyfRPuxnR\nt+IKRTAfBgNVHSMEGDAWgBSPX3h2tbo3axwLyfRPuxnRt+IKRTAMBgNVHRMEBTAD\nAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAQyccGq2GeJzq/mIKdEOlosDSkEpvc/siu\nDsa9l4VMGZYL43RRb5bXpLBhaktmtQoTb/QegRxhw3NAaacCqlQNIR1ivdYC6EGU\n7Yx5tMjUrH2pYEUNBHOWQ2+3tPzYxJ6rRtF0WxgiLiJNHbdjLzP3fl01dX2BctEG\ncUzdXalP9/WFlXUyaLuQsfzHVSlR6BMnBpGQTWFO3jdOL36ZjEY1nhTB6YTDQcI2\nJ1WDUcJ98MPMCKismi5hNR0tgcBk13oucV93N7f5fwmIBM2jSwn3l4CC4jS3+9ev\nuSDbcEIN1is+Ly7anZ/AAgUCYJK+vN+At6Fo5XT90pehncrCfVYd\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/testdata/index.html",
    "content": "<html><body>small</body></html>\n"
  },
  {
    "path": "tests/testdata/privkey.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD6ghRI7EZt5wRS\nsFr69n5gjlTV14cZD/N4OruuYAFvZH9rPc+q3LnQ/aWN5iHO94k9Uq33ENGhpDFH\nbhMJMqWinyZabmjH0pbXRRELLCWqz3E0nJBvQWB7vGT4glxJozHOmFzwid0zUMed\njLR6XxJzP2TMJv7sZgmJd7wf2F5iTvQCq5BId16/rnuA2lIZtzCCNIgnp4Hs6Bkt\nigFrO7A2xuJHuys1bXS6M2PeiuuN/3B2KV/bzZ3Ylw7u1UYIBBgjpon9FwcGFoln\n8cXVadb3ZIsWNBgP2dhXSk5vDl8t7KVdTbcC/uNysQvoCuglJEwXg/7BSf3/rpnE\ncKF+ChFpAgMBAAECggEAQ/hDdL3XJj7GZ4E+TlXPWHeGATenil9PmlGlVtzpxsWn\n5Psye5PEPzZzUxscFUII1MC4Pw8rMgaQ8Gand5lsY8F28JaAi6dSEjrFMxjZZ409\nlB7e+bDtcuVdKCiGzdwfyYSBwypFAIVshA0HcySUpvyOZu41wQK2l7wLKUDV6yoj\nkdNsIX0qYQfbPcTizbTz62ZF0E/8FgrfG6Iba7iX10Oig7nkd01zvS0n32xXvR1W\nyMucxzS4SIcpi2mnpsMEGXliC98MIl5d9tmAYNkmq4rB/CJbNVypHj9cCxXZKDq0\nLxr0TKPO5QfZnZw4IYj6ILottvq9YkBgRMplPSRuuQKBgQD+6uQl0GGx+bNa3/7c\nfQpMh3d1gLGhdltor1PbRs+GeNqQvPeXgDqwkJj4Gvi16fd3lr59X1eJQhF/7as2\njO8T/OzfXxwNM7v9CH5g7zOxv1IC02IA1IWKHv4ZQLsfrlj108TkSCZ531oDGoxB\nBSzd4J9AvfaA8Jl9Wo/xujtmhwKBgQD7kmUPH8HjgZ9jdnFvTDsTmSfgxiV3Vr+F\nhr7fXwQyfz958rOQsjR6lkF2uc6hMOaLdO9IJJ7b4FOZ92o5oMNCkVqRzuJbjdj5\nbIBy1IKvPlQooEdCdFAYRvmuroOPOboIQHDnlPsWCNWKz8WS+SMsFK7OaI5sQht+\n3pEaE2zUjwKBgQCsDEy/WM0XGbh7dQLGPX5d+HAf1iQNHZzEItEuBbS6xYZ+eYuB\nrQHveShFC68g2DA1foBCjb6+3OCCvx33S1dw3hvhBoggQPJ37Oue8NKq/L2VOqXC\nea1Hc3N8B7xDvaA9ChixGiEd0CkoPxq6J4wIlDnnsE5W6AwwFpdgRpkgsQKBgQDA\n0WEB6Y5bSMOYoXSqIJx7Ff1Zq6WVxXh/6FotrI70AVHurxXwRpYwiy/bxuhX5boe\nk16/Rco4rsSFEvxo9Q4T3IK1lQMrtxnhescsvDL6DGTGyqozhiDJqsS+nWWahl1O\n681dwnN8XQMHJN7DGo9ZvvSIodzfnv+iCwbeHJtigwKBgQDTAFYIVGgviVStW4+Y\nSyAENsfsO6w7n2Z8mwo1zfpRofsmh40RJ0DjGooOJJc+qn2tmVw3vXgb3kg+2ZDz\neGfcREykoQHw+zPIPK3S7zqrn56xMhR2RgN0WNXw0GJkzGrzt8P53LPTZ9jQr8g1\nP7xMX3H7hPIIHA3SryD6xb/EiQ==\n-----END PRIVATE KEY-----\n"
  }
]