diff --git a/deps/spdylay/.gitignore b/deps/spdylay/.gitignore new file mode 100644 index 0000000..c95b9e7 --- /dev/null +++ b/deps/spdylay/.gitignore @@ -0,0 +1,29 @@ +*~ +*.o +*.lo +*.la +depcomp +*.m4 +Makefile +Makefile.in +libtool +missing +autom4te.cache/ +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +install-sh +.deps/ +.libs +lib/includes/spdylay/spdylayver.h +lib/libspdylay.pc +ltmain.sh +stamp-h1 +.deps/ +INSTALL +.DS_STORE +tests/main diff --git a/deps/spdylay/AUTHORS b/deps/spdylay/AUTHORS new file mode 100644 index 0000000..95bc954 --- /dev/null +++ b/deps/spdylay/AUTHORS @@ -0,0 +1 @@ +Tatsuhiro Tsujikawa diff --git a/deps/spdylay/COPYING b/deps/spdylay/COPYING new file mode 100644 index 0000000..2d3a4c8 --- /dev/null +++ b/deps/spdylay/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2012 Tatsuhiro Tsujikawa + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/spdylay/ChangeLog b/deps/spdylay/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/deps/spdylay/Makefile.am b/deps/spdylay/Makefile.am new file mode 100644 index 0000000..ed56474 --- /dev/null +++ b/deps/spdylay/Makefile.am @@ -0,0 +1,25 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = lib examples tests doc + +ACLOCAL_AMFLAGS = -I m4 diff --git a/deps/spdylay/NEWS b/deps/spdylay/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/deps/spdylay/README b/deps/spdylay/README new file mode 100644 index 0000000..5ccc0ea --- /dev/null +++ b/deps/spdylay/README @@ -0,0 +1 @@ +See README.rst diff --git a/deps/spdylay/README.rst b/deps/spdylay/README.rst new file mode 100644 index 0000000..f1b3838 --- /dev/null +++ b/deps/spdylay/README.rst @@ -0,0 +1,174 @@ +Spdylay - SPDY C Library +======================== + +This is an experimental implementation of Google's SPDY protocol +version 2 and 3 in C. + +This library provides SPDY framing layer implementation. It does not +perform any I/O operations. When the library needs them, it calls the +callback functions provided by the application. It also does not +include any event polling mechanism, so the application can freely +choose the way of handling events. This library code does not depend +on any particular SSL library (except for example programs which +depend on OpenSSL 1.0.1 or later). + +The current status of development is in a beta stage now. As described +below, we can create SPDY client and server with the current Spdylay +API. + +Build from git +-------------- + +Building from git is easy, but please be sure that at least autoconf 2.68 is +used.:: + + $ autoreconf -i + $ automake + $ autoconf + $ ./configure + $ make + +API +--- + +The public API reference is available on online. Visit +http://spdylay.sourceforge.net/. All public APIs are in +*spdylay/spdylay.h*. All public API functions as well as the callback +function typedefs are documented. + +Examples +-------- + +*examples* directory contains SPDY client and server implementation +using Spdylay. These programs are intended to make sure that Spdylay +API is acutally usable for real implementation and also for debugging +purposes. Please note that OpenSSL with `NPN +`_ support is +required in order to build and run these programs. At the time of +this writing, the Beta 2 of OpenSSL 1.0.1 supports NPN. + +SPDY client is called ``spdycat``. It is a dead simple downloader like +wget/curl. It connects to SPDY server and gets resources given in the +command-line:: + + $ ./spdycat -vn https://www.google.com/ + [ 0.029] NPN select next protocol: the remote server offers: + * spdy/2 + * http/1.1 + [ 0.040] recv SETTINGS frame + (niv=1) + [4(1):100] + [ 0.040] send SYN_STREAM frame + (stream_id=1, assoc_stream_id=0, pri=3) + host: www.google.com:443 + method: GET + scheme: https + url: / + user-agent: spdylay/0.0.0 + version: HTTP/1.1 + [ 0.087] recv SYN_REPLY frame + (stream_id=1) + cache-control: private, max-age=0 + content-type: text/html; charset=ISO-8859-1 + date: Wed, 01 Feb 2012 15:43:00 GMT + expires: -1 + server: gws + status: 200 OK + version: HTTP/1.1 + x-frame-options: SAMEORIGIN + x-xss-protection: 1; mode=block + [ 0.087] recv DATA frame (stream_id=1, flags=0, length=4096) + [ 0.088] recv DATA frame (stream_id=1, flags=0, length=2617) + [ 0.094] recv DATA frame (stream_id=1, flags=0, length=4096) + [ 0.094] recv DATA frame (stream_id=1, flags=1, length=828) + [ 0.094] send GOAWAY frame + (last_good_stream_id=0) + +``spdycat`` can speak SPDY/3. Note that SPDY/3 is still moving target +and thus considered highly experimental. ``-3`` option forces ``spdycat`` +to use SPDY/3 only:: + + $ ./spdycat -nv3 https://localhost:3000/ + [ 0.000] NPN select next protocol: the remote server offers: + * spdy/2 + * spdy/3 + NPN selected the protocol: spdy/3 + [ 0.002] send SYN_STREAM frame + (stream_id=1, assoc_stream_id=0, pri=3) + :host: localhost:3000 + :method: GET + :path: / + :scheme: https + :version: HTTP/1.1 + user-agent: spdylay/0.0.0 + [ 0.003] recv SYN_REPLY frame + (stream_id=1) + :status: 404 Not Found + :version: HTTP/1.1 + cache-control: max-age=3600 + content-length: 144 + date: Sun, 26 Feb 2012 09:16:51 GMT + server: spdyd spdylay/0.1.0 + [ 0.003] recv DATA frame (stream_id=1, flags=0, length=144) + [ 0.003] recv DATA frame (stream_id=1, flags=1, length=0) + [ 0.003] send GOAWAY frame + (last_good_stream_id=0) + +SPDY server is called ``spdyd``. It is a non-blocking server and only +serves static contents. It can speak SPDY/2 and SPDY/3:: + + $ ./spdyd --htdocs=/your/htdocs/ -v 3000 server.key server.crt + The negotiated next protocol: spdy/2 + [id=1] [ 1.633] recv SYN_STREAM frame + (stream_id=1, assoc_stream_id=0, pri=3) + host: localhost:3000 + method: GET + scheme: https + url: / + user-agent: spdylay/0.0.0 + version: HTTP/1.1 + [id=1] [ 1.633] send SYN_REPLY frame + (stream_id=1) + cache-control: max-age=3600 + content-length: 8472 + date: Mon, 16 Jan 2012 12:46:27 GMT + last-modified: Mon, 16 Jan 2012 12:46:27 GMT + server: spdyd spdylay/0.1.0 + status: 200 OK + version: HTTP/1.1 + [id=1] [ 1.633] send DATA frame (stream_id=1, flags=0, length=4104) + [id=1] [ 1.633] send DATA frame (stream_id=1, flags=0, length=4104) + [id=1] [ 1.633] send DATA frame (stream_id=1, flags=0, length=288) + [id=1] [ 1.633] send DATA frame (stream_id=1, flags=1, length=8) + [id=1] [ 1.633] stream_id=1 closed + [id=1] [ 1.634] recv GOAWAY frame + (last_good_stream_id=0) + [id=1] [ 1.634] closed + +Currently, ``spdyd`` needs ``epoll`` or ``kqueue``. + +There is another SPDY server called ``spdynative``, which is +`node.native `_ style simple SPDY +server:: + + #include + + #include "spdy.h" + + int main() + { + spdy server; + if(!server.listen("localhost", 8080, "server.key", "server.crt", + [](request& req, response& res) { + res.set_status(200); + res.set_header("content-type", "text/plain"); + res.end("C++ FTW\n"); + })) + return EXIT_FAILURE; + + std::cout << "Server running at http://localhost:8080/" << std::endl; + return reactor::run(server); + } + +Don't expect much from ``spdynative``. It is just an example and does +not support asynchronous I/O at all. diff --git a/deps/spdylay/configure.ac b/deps/spdylay/configure.ac new file mode 100644 index 0000000..dc07f40 --- /dev/null +++ b/deps/spdylay/configure.ac @@ -0,0 +1,148 @@ +dnl Spdylay - SPDY Library + +dnl Copyright (c) 2012 Tatsuhiro Tsujikawa + +dnl Permission is hereby granted, free of charge, to any person obtaining +dnl a copy of this software and associated documentation files (the +dnl "Software"), to deal in the Software without restriction, including +dnl without limitation the rights to use, copy, modify, merge, publish, +dnl distribute, sublicense, and/or sell copies of the Software, and to +dnl permit persons to whom the Software is furnished to do so, subject to +dnl the following conditions: + +dnl The above copyright notice and this permission notice shall be +dnl included in all copies or substantial portions of the Software. + +dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +dnl MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +dnl LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +dnl OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +dnl WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +AC_PREREQ(2.61) +LT_PREREQ([2.2.6]) +AC_INIT([spdylay], [0.1.0], [t-tujikawa@users.sourceforge.net]) +dnl See versioning rule: +dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +AC_SUBST(LT_CURRENT, 0) +AC_SUBST(LT_REVISION, 0) +AC_SUBST(LT_AGE, 0) + +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE() +AC_CONFIG_HEADERS([config.h]) + +dnl Checks for programs +AC_PROG_CC +AC_PROG_CXX +AM_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +PKG_PROG_PKG_CONFIG([0.20]) + +AC_COMPILE_STDCXX_11 +AM_CONDITIONAL([HAVE_STDCXX_11], + [ test "x$ac_cv_cxx_compile_cxx11_cxx" = "xyes" ]) + +# Checks for libraries. + +# zlib +PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3]) +LIBS="$ZLIB_LIBS $LIBS" +CFLAGS="$CFLAGS $ZLIB_CFLAGS" + +# cunit +PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no]) +# If pkg-config does not find cunit, check it using AC_CHECK_LIB. We +# do this because Debian (Ubuntu) lacks pkg-config file for cunit. +if test "x${have_cunit}" = "xno"; then + AC_MSG_WARN([${CUNIT_PKG_ERRORS}]) + AC_CHECK_LIB([cunit], [CU_initialize_registry], + [have_cunit=yes], [have_cunit=no]) + if test "x${have_cunit}" = "xyes"; then + CUNIT_LIBS="-lcunit" + CUNIT_CFLAGS="" + AC_SUBST([CUNIT_LIBS]) + AC_SUBST([CUNIT_CFLAGS]) + fi +fi +if test "x${have_cunit}" = "xyes"; then + # cunit in Mac OS X requires ncurses. Note that in Mac OS X, test + # program can be built without -lncurses, but it emits runtime + # error. + case "${build}" in + *-apple-darwin*) + CUNIT_LIBS="$CUNIT_LIBS -lncurses" + AC_SUBST([CUNIT_LIBS]) + ;; + esac +fi + +AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) + +# openssl (for examples) +PKG_CHECK_MODULES([OPENSSL], [openssl >= 1.0.1]) + +# Checks for header files. +AC_CHECK_HEADERS([ \ + arpa/inet.h \ + stddef.h \ + stdint.h \ + stdlib.h \ + string.h \ + time.h \ + unistd.h \ +]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_CHECK_TYPES([ptrdiff_t]) +AC_C_BIGENDIAN + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([ \ + memmove \ + memset \ +]) + +AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) +if test "x${have_epoll}" = "xyes"; then + AC_DEFINE([HAVE_EPOLL], [1], [Define to 1 if you have the `epoll`.]) +fi +AM_CONDITIONAL([HAVE_EPOLL], [ test "x${have_epoll}" = "xyes" ]) + +AC_CHECK_FUNCS([kqueue], [have_kqueue=yes]) +AM_CONDITIONAL([HAVE_KQUEUE], [test "x${have_kqueue}" = "xyes"]) + +AC_CONFIG_FILES([ + Makefile + lib/Makefile + lib/libspdylay.pc + lib/includes/Makefile + lib/includes/spdylay/spdylayver.h + tests/Makefile + examples/Makefile + doc/Makefile + doc/conf.py +]) +AC_OUTPUT + +AC_MSG_NOTICE([summary of build options: + + version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE + Host type: ${host} + Install prefix: ${prefix} + C compiler: ${CC} + CFlags: ${CFLAGS} + Library types: Shared=${enable_shared}, Static=${enable_static} + CUnit: ${have_cunit} +]) diff --git a/deps/spdylay/doc/.gitignore b/deps/spdylay/doc/.gitignore new file mode 100644 index 0000000..65f5f83 --- /dev/null +++ b/deps/spdylay/doc/.gitignore @@ -0,0 +1,3 @@ +apiref.rst +conf.py +manual diff --git a/deps/spdylay/doc/Makefile.am b/deps/spdylay/doc/Makefile.am new file mode 100644 index 0000000..6397ed7 --- /dev/null +++ b/deps/spdylay/doc/Makefile.am @@ -0,0 +1,158 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = manual + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +apiref.rst: $(top_builddir)/lib/includes/spdylay/spdylayver.h \ + $(top_builddir)/lib/includes/spdylay/spdylay.h + $(builddir)/mkapiref.py --header apiref-header.rst $^ > $@ + +clean: + -rm apiref.rst + -rm -rf $(BUILDDIR)/* + +html: apiref.rst + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Spdylay.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Spdylay.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Spdylay" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Spdylay" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/deps/spdylay/doc/README.rst b/deps/spdylay/doc/README.rst new file mode 100644 index 0000000..b583190 --- /dev/null +++ b/deps/spdylay/doc/README.rst @@ -0,0 +1,160 @@ +Spdylay Documentation +===================== + +The documentation of Spdylay is generated using Sphinx. This +directory contains the source files to be processed by Sphinx. The +source file for API reference is generated using a script called +``mkapiref.py`` from the Spdylay C source code. + +Generating API reference +------------------------ + +As described earlier, we use ``mkapiref.py`` to generate rst formatted +text of API reference from C source code. The ``mkapiref.py`` is not +so flexible and it requires that C source code is formatted in rather +strict rules. + +To generate API reference, just run ``make html``. It runs +``mkapiref.py`` and then run Sphinx to build the entire document. + +The ``mkapiref.py`` reads C source code and searches the comment block +starts with ``/**``. In other words, it only processes the comment +block starting ``/**``. The comment block must end with ``*/``. The +``mkapiref.py`` requires that which type of the object this comment +block refers to. To specify the type of the object, the next line +must contain the so-caled action keyword. Currently, the following +action keywords are supported: ``@function``, ``@functypedef``, +``@enum``, ``@struct`` and ``@union``. The following sections +describes each action keyword. + +@function +######### + +``@function`` is used to refer to the function. The comment block is +used for the document for the function. After the script sees the end +of the comment block, it consumes the lines as the function +declaration until the line which ends with ``;`` is encountered. + +In Sphinx doc, usually the function argument is formatted like +``*this*``. But in C, ``*`` is used for dereferencing a pointer and +we must escape ``*`` with a back slash. To avoid this, we format the +argument like ``|this|``. The ``mkapiref.py`` translates it with +``*this*``, as escaping ``*`` inside ``|`` and ``|`` as necessary. +Note that this shadows the substitution feature of Sphinx. + +The example follows:: + + /** + * @function + * + * Submits PING frame to the |session|. + */ + int spdylay_submit_ping(spdylay_session *session); + + +@functypedef +############ + +``@functypedef`` is used to refer to the typedef of the function +pointer. The formatting rule is pretty much the same with +``@function``, but this outputs ``type`` domain, rather than +``function`` domain. + +The example follows:: + + /** + * @functypedef + * + * Callback function invoked when |session| wants to send data to + * remote peer. + */ + typedef ssize_t (*spdylay_send_callback) + (spdylay_session *session, + const uint8_t *data, size_t length, int flags, void *user_data); + +@enum +##### + +``@enum`` is used to refer to the enum. Currently, only enum typedefs +are supported. The comment block is used for the document for the +enum type itself. To document each values, put comment block starting +with the line ``/**`` and ending with the ``*/`` just before the enum +value. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of enum. + +At the time of this writing, Sphinx does not support enum type. So we +use ``type`` domain for enum it self and ``macro`` domain for each +value. To refer to the enum value, use ``:enum:`` pseudo role. The +``mkapiref.py`` replaces it with ``:macro:``. By doing this, when +Sphinx will support enum officially, we can replace ``:enum:`` with +the official role easily. + +The example follows:: + + /** + * @enum + * Error codes used in the Spdylay library. + */ + typedef enum { + /** + * Invalid argument passed. + */ + SPDYLAY_ERR_INVALID_ARGUMENT = -501, + /** + * Zlib error. + */ + SPDYLAY_ERR_ZLIB = -502, + } spdylay_error; + +@struct +####### + +``@struct`` is used to refer to the struct. Currently, only struct +typedefs are supported. The comment block is used for the document for +the struct type itself.To document each member, put comment block +starting with the line ``/**`` and ending with the ``*/`` just before +the member. When the line starts with ``}`` is encountered, the +``mkapiref.py`` extracts strings next to ``}`` as the name of struct. +The block-less typedef is also supported. In this case, typedef +declaration must be all in one line and the ``mkapiref.py`` uses last +word as the name of struct. + +Some examples follow:: + + /** + * @struct + * The control frame header. + */ + typedef struct { + /** + * SPDY protocol version. + */ + uint16_t version; + /** + * The type of this control frame. + */ + uint16_t type; + /** + * The control frame flags. + */ + uint8_t flags; + /** + * The length field of this control frame. + */ + int32_t length; + } spdylay_ctrl_hd; + + /** + * @struct + * + * The primary structure to hold the resources needed for a SPDY + * session. The details of this structure is hidden from the public + * API. + */ + typedef struct spdylay_session spdylay_session; + +@union +###### + +``@union`` is used to refer to the union. Currently, ``@union`` is an +alias of ``@struct``. diff --git a/deps/spdylay/doc/apiref-header.rst b/deps/spdylay/doc/apiref-header.rst new file mode 100644 index 0000000..cdd836f --- /dev/null +++ b/deps/spdylay/doc/apiref-header.rst @@ -0,0 +1,9 @@ +API Reference +============= + +Includes +-------- + +To use the public APIs, include ``spdylay/spdylay.h``:: + + #include diff --git a/deps/spdylay/doc/conf.py.in b/deps/spdylay/doc/conf.py.in new file mode 100644 index 0000000..81d83a1 --- /dev/null +++ b/deps/spdylay/doc/conf.py.in @@ -0,0 +1,243 @@ +# -*- coding: utf-8 -*- +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# +# Spdylay documentation build configuration file, created by +# sphinx-quickstart on Sun Mar 11 22:57:49 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Spdylay' +copyright = u'2012, Tatsuhiro Tsujikawa' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PACKAGE_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@PACKAGE_VERSION@' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['manual', 'README.rst', '*-header.rst'] + +# The reST default role (used for this markup: `text`) to use for all documents. +default_role = 'c:func' +primary_domain = 'c' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The default language to highlight source code in. The default is 'python'. +highlight_language = 'c' + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Spdylaydoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Spdylay.tex', u'Spdylay Documentation', + u'Tatsuhiro Tsujikawa', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'spdylay', u'Spdylay Documentation', + [u'Tatsuhiro Tsujikawa'], 1) +] diff --git a/deps/spdylay/doc/index.rst b/deps/spdylay/doc/index.rst new file mode 100644 index 0000000..5ccfaf8 --- /dev/null +++ b/deps/spdylay/doc/index.rst @@ -0,0 +1,29 @@ +.. Spdylay documentation master file, created by + sphinx-quickstart on Sun Mar 11 22:57:49 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Spdylay's documentation! +=================================== + +This is an experimental implementation of Google's SPDY protocol +version 2 and 3 in C. + +This library provides SPDY framing layer implementation. It does not +perform any I/O operations. When the library needs them, it calls the +callback functions provided by the application. It also does not +include any event polling mechanism, so the application can freely +choose the way of handling events. This library code does not depend +on any particular SSL library (except for example programs which +depend on OpenSSL 1.0.1 or later). + +The project is hosted at https://sourceforge.net/projects/spdylay/. + +The source code is hosted at https://github.com/tatsuhiro-t/spdylay. + +Contents: + +.. toctree:: + :maxdepth: 2 + + apiref diff --git a/deps/spdylay/doc/make.bat b/deps/spdylay/doc/make.bat new file mode 100644 index 0000000..6f693b2 --- /dev/null +++ b/deps/spdylay/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Spdylay.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Spdylay.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/deps/spdylay/doc/mkapiref.py b/deps/spdylay/doc/mkapiref.py new file mode 100755 index 0000000..a0c9553 --- /dev/null +++ b/deps/spdylay/doc/mkapiref.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Generates API reference from C source code. +import re, sys, argparse + +class FunctionDoc: + def __init__(self, name, content, domain): + self.name = name + self.content = content + self.domain = domain + + def write(self, out): + print '''.. {}:: {}'''.format(self.domain, self.name) + print '' + for line in self.content: + print ' {}'.format(line) + +class StructDoc: + def __init__(self, name, content, members, member_domain): + self.name = name + self.content = content + self.members = members + self.member_domain = member_domain + + def write(self, out): + if self.name: + print '''.. type:: {}'''.format(self.name) + print '' + for line in self.content: + print ' {}'.format(line) + print '' + for name, content in self.members: + print ''' .. {}:: {}'''.format(self.member_domain, name) + print '' + for line in content: + print ''' {}'''.format(line) + print '' + +class MacroDoc: + def __init__(self, name, content): + self.name = name + self.content = content + + def write(self, out): + print '''.. macro:: {}'''.format(self.name) + print '' + for line in self.content: + print ' {}'.format(line) + +def make_api_ref(infiles): + macros = [] + enums = [] + types = [] + functions = [] + for infile in infiles: + while True: + line = infile.readline() + if not line: + break + elif line == '/**\n': + line = infile.readline() + doctype = line.split()[1] + if doctype == '@function': + functions.append(process_function('function', infile)) + elif doctype == '@functypedef': + types.append(process_function('type', infile)) + elif doctype == '@struct' or doctype == '@union': + types.append(process_struct(infile)) + elif doctype == '@enum': + enums.append(process_enum(infile)) + elif doctype == '@macro': + macros.append(process_macro(infile)) + alldocs = [('Macros', macros), + ('Enums', enums), + ('Types (structs, unions and typedefs)', types), + ('Functions', functions)] + for title, docs in alldocs: + if not docs: + continue + print title + print '-'*len(title) + for doc in docs: + doc.write(sys.stdout) + print '' + print '' + +def process_macro(infile): + content = read_content(infile) + line = infile.readline() + macro_name = line.split()[1] + return MacroDoc(macro_name, content) + +def process_enum(infile): + members = [] + enum_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + member_name = line.split()[0] + members.append((member_name, member_content)) + elif line.startswith('}'): + enum_name = line.rstrip().split()[1] + enum_name = re.sub(r';$', '', enum_name) + break + return StructDoc(enum_name, content, members, 'macro') + +def process_struct(infile): + members = [] + struct_name = None + content = read_content(infile) + while True: + line = infile.readline() + if not line: + break + elif re.match(r'\s*/\*\*\n', line): + member_content = read_content(infile) + line = infile.readline() + member_name = line.rstrip().rstrip(';') + members.append((member_name, member_content)) + elif line.startswith('}') or\ + (line.startswith('typedef ') and line.endswith(';\n')): + if line.startswith('}'): + index = 1 + else: + index = 3 + struct_name = line.rstrip().split()[index] + struct_name = re.sub(r';$', '', struct_name) + break + return StructDoc(struct_name, content, members, 'member') + +def process_function(domain, infile): + content = read_content(infile) + func_proto = [] + while True: + line = infile.readline() + if not line: + break + elif line == '\n': + break + else: + func_proto.append(line) + func_proto = ''.join(func_proto) + func_proto = re.sub(r';\n$', '', func_proto) + func_proto = re.sub(r'\s+', ' ', func_proto) + return FunctionDoc(func_proto, content, domain) + +def read_content(infile): + content = [] + while True: + line = infile.readline() + if not line: + break + if re.match(r'\s*\*/\n', line): + break + else: + content.append(transform_content(line.rstrip())) + return content + +def arg_repl(matchobj): + return '*{}*'.format(matchobj.group(1).replace('*', '\\*')) + +def transform_content(content): + content = re.sub(r'^\s+\* ?', '', content) + content = re.sub(r'\|([^\s|]+)\|', arg_repl, content) + content = re.sub(r':enum:', ':macro:', content) + return content + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Generate API reference") + parser.add_argument('--header', type=argparse.FileType('rb', 0), + help='header inserted at the top of the page') + parser.add_argument('files', nargs='+', type=argparse.FileType('rb', 0), + help='source file') + args = parser.parse_args() + if args.header: + print args.header.read() + for infile in args.files: + make_api_ref(args.files) diff --git a/deps/spdylay/examples/.gitignore b/deps/spdylay/examples/.gitignore new file mode 100644 index 0000000..5c50552 --- /dev/null +++ b/deps/spdylay/examples/.gitignore @@ -0,0 +1,3 @@ +spdycat +spdyd +spdynative diff --git a/deps/spdylay/examples/EventPoll.h b/deps/spdylay/examples/EventPoll.h new file mode 100644 index 0000000..7f1e5cb --- /dev/null +++ b/deps/spdylay/examples/EventPoll.h @@ -0,0 +1,38 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef EVENT_POLL_H +#define EVENT_POLL_H + +#ifdef HAVE_CONFIG_H +# include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_EPOLL +# include "EventPoll_epoll.h" +#elif HAVE_KQUEUE +# include "EventPoll_kqueue.h" +#endif // HAVE_KQUEUE + +#endif // EVENT_POLL_H diff --git a/deps/spdylay/examples/EventPollEvent.h b/deps/spdylay/examples/EventPollEvent.h new file mode 100644 index 0000000..9c2ec4e --- /dev/null +++ b/deps/spdylay/examples/EventPollEvent.h @@ -0,0 +1,44 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef EVENT_POLL_EVENT_H +#define EVENT_POLL_EVENT_H + +namespace spdylay { + +enum EventPollEvent { + EP_POLLIN = 1, + EP_POLLOUT = 1 << 1, + EP_POLLHUP = 1 << 2, + EP_POLLERR = 1 << 3 +}; + +enum EventPollOp { + EP_ADD, + EP_MOD +}; + +} // namespace spdylay + +#endif // EVENT_POLL_EVENT_H diff --git a/deps/spdylay/examples/EventPoll_epoll.cc b/deps/spdylay/examples/EventPoll_epoll.cc new file mode 100644 index 0000000..2b8f55b --- /dev/null +++ b/deps/spdylay/examples/EventPoll_epoll.cc @@ -0,0 +1,116 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "EventPoll_epoll.h" + +#include + +#include + +namespace spdylay { + +EventPoll::EventPoll(size_t max_events) + : max_events_(max_events), num_events_(0) +{ + epfd_ = epoll_create(1); + assert(epfd_ != -1); + evlist_ = new epoll_event[max_events_]; +} + +EventPoll::~EventPoll() +{ + if(epfd_ != -1) { + close(epfd_); + } + delete [] evlist_; +} + +int EventPoll::poll(int timeout) +{ + num_events_ = 0; + int n = epoll_wait(epfd_, evlist_, max_events_, timeout); + if(n > 0) { + num_events_ = n; + } + return n; +} + +int EventPoll::get_num_events() +{ + return num_events_; +} + +void* EventPoll::get_user_data(size_t p) +{ + return evlist_[p].data.ptr; +} + +int EventPoll::get_events(size_t p) +{ + int events = 0; + int revents = evlist_[p].events; + if(revents & EPOLLIN) { + events |= EP_POLLIN; + } + if(revents & EPOLLOUT) { + events |= EP_POLLOUT; + } + if(revents & EPOLLHUP) { + events |= EP_POLLHUP; + } + if(revents & EPOLLERR) { + events |= EP_POLLERR; + } + return events; +} + +namespace { +int update_event(int epfd, int op, int fd, int events, void *user_data) +{ + epoll_event ev; + ev.events = 0; + if(events & EP_POLLIN) { + ev.events |= EPOLLIN; + } + if(events & EP_POLLOUT) { + ev.events |= EPOLLOUT; + } + ev.data.ptr = user_data; + return epoll_ctl(epfd, op, fd, &ev); +} +} // namespace + +int EventPoll::ctl_event(int op, int fd, int events, void *user_data) +{ + if(op == EP_ADD) { + op = EPOLL_CTL_ADD; + } else if(op == EP_MOD) { + op = EPOLL_CTL_MOD; + } else { + return -1; + } + return update_event(epfd_, op, fd, events, user_data); +} + +} // namespace spdylay diff --git a/deps/spdylay/examples/EventPoll_epoll.h b/deps/spdylay/examples/EventPoll_epoll.h new file mode 100644 index 0000000..b17ec90 --- /dev/null +++ b/deps/spdylay/examples/EventPoll_epoll.h @@ -0,0 +1,60 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef EVENT_POLL_EPOLL_H +#define EVENT_POLL_EPOLL_H + +#include + +#include + +#include "EventPollEvent.h" + +namespace spdylay { + +class EventPoll { +public: + EventPoll(size_t max_events); + ~EventPoll(); + // Returns 0 if this function succeeds, or -1. + // On success + int poll(int timeout); + // Returns number of events detected in previous call of poll(). + int get_num_events(); + // Returns events of p-eth event. + int get_events(size_t p); + // Returns user data of p-th event. + void* get_user_data(size_t p); + // Adds/Modifies event to watch. + int ctl_event(int op, int fd, int events, void *user_data); +private: + int epfd_; + size_t max_events_; + epoll_event *evlist_; + size_t num_events_; +}; + +} // namespace spdylay + +#endif // EVENT_POLL_EPOLL_H diff --git a/deps/spdylay/examples/EventPoll_kqueue.cc b/deps/spdylay/examples/EventPoll_kqueue.cc new file mode 100644 index 0000000..e95d73f --- /dev/null +++ b/deps/spdylay/examples/EventPoll_kqueue.cc @@ -0,0 +1,113 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "EventPoll_kqueue.h" + +#include + +#include +#include + +namespace spdylay { + +EventPoll::EventPoll(size_t max_events) + : max_events_(max_events), num_events_(0) +{ + kq_ = kqueue(); + assert(kq_ != -1); + evlist_ = new struct kevent[max_events_]; +} + +EventPoll::~EventPoll() +{ + if(kq_ != -1) { + close(kq_); + } + delete [] evlist_; +} + +int EventPoll::poll(int timeout) +{ + timespec ts, *ts_ptr; + if(timeout == -1) { + ts_ptr = 0; + } else { + ts.tv_sec = timeout/1000; + ts.tv_nsec = (timeout%1000)*1000000; + ts_ptr = &ts; + } + num_events_ = 0; + int n; + while((n = kevent(kq_, evlist_, 0, evlist_, max_events_, ts_ptr)) == -1 && + errno == EINTR); + if(n > 0) { + num_events_ = n; + } + return n; +} + +int EventPoll::get_num_events() +{ + return num_events_; +} + +void* EventPoll::get_user_data(size_t p) +{ + return evlist_[p].udata; +} + +int EventPoll::get_events(size_t p) +{ + int events = 0; + short filter = evlist_[p].filter; + if(filter == EVFILT_READ) { + events |= EP_POLLIN; + } + if(filter == EVFILT_WRITE) { + events |= EP_POLLOUT; + } + return events; +} + +namespace { +int update_event(int kq, int fd, int events, void *user_data) +{ + struct kevent changelist[2]; + EV_SET(&changelist[0], fd, EVFILT_READ, + EV_ADD | ((events & EP_POLLIN) ? EV_ENABLE : EV_DISABLE), + 0, 0, user_data); + EV_SET(&changelist[1], fd, EVFILT_WRITE, + EV_ADD | ((events & EP_POLLOUT) ? EV_ENABLE : EV_DISABLE), + 0, 0, user_data); + timespec ts = { 0, 0 }; + return kevent(kq, changelist, 2, changelist, 0, &ts); +} +} // namespace + +int EventPoll::ctl_event(int op, int fd, int events, void *user_data) +{ + return update_event(kq_, fd, events, user_data); +} + +} // namespace spdylay diff --git a/deps/spdylay/examples/EventPoll_kqueue.h b/deps/spdylay/examples/EventPoll_kqueue.h new file mode 100644 index 0000000..15138b8 --- /dev/null +++ b/deps/spdylay/examples/EventPoll_kqueue.h @@ -0,0 +1,62 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef EVENT_POLL_KQUEUE_H +#define EVENT_POLL_KQUEUE_H + +#include + +#include +#include +#include + +#include "EventPollEvent.h" + +namespace spdylay { + +class EventPoll { +public: + EventPoll(size_t max_events); + ~EventPoll(); + // Returns 0 if this function succeeds, or -1. + // On success + int poll(int timeout); + // Returns number of events detected in previous call of poll(). + int get_num_events(); + // Returns events of p-eth event. + int get_events(size_t p); + // Returns user data of p-th event. + void* get_user_data(size_t p); + // Adds/Modifies event to watch. + int ctl_event(int op, int fd, int events, void *user_data); +private: + int kq_; + size_t max_events_; + struct kevent *evlist_; + size_t num_events_; +}; + +} // namespace spdylay + +#endif // EVENT_POLL_KQUEUE_H diff --git a/deps/spdylay/examples/Makefile.am b/deps/spdylay/examples/Makefile.am new file mode 100644 index 0000000..f064548 --- /dev/null +++ b/deps/spdylay/examples/Makefile.am @@ -0,0 +1,65 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AM_CFLAGS = -Wall +AM_CPPFLAGS = -Wall -I$(srcdir)/../lib/includes -I$(builddir)/../lib/includes \ + @OPENSSL_CFLAGS@ +AM_LDFLAGS = @OPENSSL_LIBS@ +LDADD = $(top_builddir)/lib/libspdylay.la + +bin_PROGRAMS = spdycat spdyd + +HELPER_OBJECTS = uri.cc util.cc spdylay_ssl.cc +HELPER_HFILES = uri.h util.h spdylay_ssl.h + +EVENT_OBJECTS = +EVENT_HFILES = EventPoll.h EventPollEvent.h + +if HAVE_EPOLL +EVENT_OBJECTS += EventPoll_epoll.cc +EVENT_HFILES += EventPoll_epoll.h +endif # HAVE_EPOLL + +if HAVE_KQUEUE +EVENT_OBJECTS += EventPoll_kqueue.cc +EVENT_HFILES += EventPoll_kqueue.h +endif # HAVE_KQUEUE + +SPDY_SERVER_OBJECTS = SpdyServer.cc +SPDY_SERVER_HFILES = SpdyServer.h + +spdycat_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} spdycat.cc + +spdyd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \ + ${EVENT_OBJECTS} ${EVENT_HFILES} \ + ${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \ + spdyd.cc + +if HAVE_STDCXX_11 +bin_PROGRAMS += spdynative +spdynative_CXXFLAGS = -std=c++0x +spdynative_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \ + ${EVENT_OBJECTS} ${EVENT_HFILES} \ + ${SPDY_SERVER_OBJECTS} ${SPDY_SERVER_HFILES} \ + spdy.h spdynative.cc +endif # HAVE_STDCXX_11 diff --git a/deps/spdylay/examples/SpdyServer.cc b/deps/spdylay/examples/SpdyServer.cc new file mode 100644 index 0000000..92d3c10 --- /dev/null +++ b/deps/spdylay/examples/SpdyServer.cc @@ -0,0 +1,978 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "SpdyServer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "spdylay_ssl.h" +#include "uri.h" +#include "util.h" +#include "EventPoll.h" + +namespace spdylay { + +namespace { +Config config; +const std::string STATUS_200 = "200 OK"; +const std::string STATUS_304 = "304 Not Modified"; +const std::string STATUS_400 = "400 Bad Request"; +const std::string STATUS_404 = "404 Not Found"; +const std::string DEFAULT_HTML = "index.html"; +const std::string SPDYD_SERVER = "spdyd spdylay/"SPDYLAY_VERSION; +} // namespace + +Config::Config(): verbose(false), daemon(false), port(0), data_ptr(0), + spdy3_only(false) +{} + +Request::Request(int32_t stream_id) + : stream_id(stream_id), + file(-1) +{} + +Request::~Request() +{ + if(file != -1) { + close(file); + } +} + +EventHandler::EventHandler(const Config *config) + : config_(config), + mark_del_(false) +{} + +namespace { +void on_close(Sessions &sessions, EventHandler *hd); +} // namespace + +class Sessions { +public: + Sessions(int max_events, SSL_CTX *ssl_ctx) + : eventPoll_(max_events), + ssl_ctx_(ssl_ctx) + {} + ~Sessions() + { + for(std::set::iterator i = handlers_.begin(), + eoi = handlers_.end(); i != eoi; ++i) { + on_close(*this, *i); + delete *i; + } + SSL_CTX_free(ssl_ctx_); + } + void add_handler(EventHandler *handler) + { + handlers_.insert(handler); + } + void remove_handler(EventHandler *handler) + { + handlers_.erase(handler); + } + SSL* ssl_session_new(int fd) + { + SSL *ssl = SSL_new(ssl_ctx_); + if(SSL_set_fd(ssl, fd) == 0) { + SSL_free(ssl); + return 0; + } + return ssl; + } + int add_poll(EventHandler *handler) + { + return update_poll_internal(handler, EP_ADD); + } + int mod_poll(EventHandler *handler) + { + return update_poll_internal(handler, EP_MOD); + } + int poll(int timeout) + { + return eventPoll_.poll(timeout); + } + void* get_user_data(int p) + { + return eventPoll_.get_user_data(p); + } + int get_events(int p) + { + return eventPoll_.get_events(p); + } +private: + int update_poll_internal(EventHandler *handler, int op) + { + int events = 0; + if(handler->want_read()) { + events |= EP_POLLIN; + } + if(handler->want_write()) { + events |= EP_POLLOUT; + } + return eventPoll_.ctl_event(op, handler->fd(), events, handler); + } + + std::set handlers_; + EventPoll eventPoll_; + SSL_CTX *ssl_ctx_; +}; + +namespace { +void print_session_id(int64_t id) +{ + std::cout << "[id=" << id << "] "; +} +} // namespace + +namespace { +void on_session_closed(EventHandler *hd, int64_t session_id) +{ + if(hd->config()->verbose) { + print_session_id(session_id); + print_timer(); + std::cout << " closed" << std::endl; + } +} +} // namespace + +SpdyEventHandler::SpdyEventHandler(const Config* config, + int fd, SSL *ssl, + uint16_t version, + const spdylay_session_callbacks *callbacks, + int64_t session_id) + : EventHandler(config), + fd_(fd), ssl_(ssl), version_(version), session_id_(session_id), + want_write_(false) +{ + int r; + r = spdylay_session_server_new(&session_, version, callbacks, this); + assert(r == 0); + spdylay_settings_entry entry; + entry.settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + entry.value = SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS; + entry.flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + r = spdylay_submit_settings(session_, SPDYLAY_FLAG_SETTINGS_NONE, + &entry, 1); + assert(r == 0); +} + +SpdyEventHandler::~SpdyEventHandler() +{ + on_session_closed(this, session_id_); + spdylay_session_del(session_); + for(std::map::iterator i = id2req_.begin(), + eoi = id2req_.end(); i != eoi; ++i) { + delete (*i).second; + } + SSL_shutdown(ssl_); + SSL_free(ssl_); + shutdown(fd_, SHUT_WR); + close(fd_); +} + +uint16_t SpdyEventHandler::version() const +{ + return version_; +} + +int SpdyEventHandler::execute(Sessions *sessions) +{ + int r; + r = spdylay_session_recv(session_); + if(r == 0) { + r = spdylay_session_send(session_); + } + return r; +} + +bool SpdyEventHandler::want_read() +{ + return spdylay_session_want_read(session_); +} + +bool SpdyEventHandler::want_write() +{ + return spdylay_session_want_write(session_) || want_write_; +} + +int SpdyEventHandler::fd() const +{ + return fd_; +} + +bool SpdyEventHandler::finish() +{ + return !want_read() && !want_write(); +} + +ssize_t SpdyEventHandler::send_data(const uint8_t *data, size_t len, int flags) +{ + ssize_t r; + r = SSL_write(ssl_, data, len); + return r; +} + +ssize_t SpdyEventHandler::recv_data(uint8_t *data, size_t len, int flags) +{ + ssize_t r; + want_write_ = false; + r = SSL_read(ssl_, data, len); + if(r < 0) { + if(SSL_get_error(ssl_, r) == SSL_ERROR_WANT_WRITE) { + want_write_ = true; + } + } + return r; +} + +bool SpdyEventHandler::would_block(int r) +{ + int e = SSL_get_error(ssl_, r); + return e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_WANT_READ; +} + +int SpdyEventHandler::submit_file_response(const std::string& status, + int32_t stream_id, + time_t last_modified, + off_t file_length, + spdylay_data_provider *data_prd) +{ + std::string date_str = util::http_date(time(0)); + std::string content_length = util::to_str(file_length); + const char *nv[] = { + ":status", status.c_str(), + ":version", "HTTP/1.1", + "server", SPDYD_SERVER.c_str(), + "content-length", content_length.c_str(), + "cache-control", "max-age=3600", + "date", date_str.c_str(), + 0, 0, + 0 + }; + if(last_modified != 0) { + nv[12] = "last-modified"; + nv[13] = util::http_date(last_modified).c_str(); + } + return spdylay_submit_response(session_, stream_id, nv, data_prd); +} + +int SpdyEventHandler::submit_response +(const std::string& status, + int32_t stream_id, + const std::vector >& headers, + spdylay_data_provider *data_prd) +{ + std::string date_str = util::http_date(time(0)); + const char **nv = new const char*[8+headers.size()*2+1]; + nv[0] = ":status"; + nv[1] = status.c_str(); + nv[2] = ":version"; + nv[3] = "HTTP/1.1"; + nv[4] = "server"; + nv[5] = SPDYD_SERVER.c_str(); + nv[6] = "date"; + nv[7] = date_str.c_str(); + for(int i = 0; i < (int)headers.size(); ++i) { + nv[8+i*2] = headers[i].first.c_str(); + nv[8+i*2+1] = headers[i].second.c_str(); + } + nv[8+headers.size()*2] = 0; + int r = spdylay_submit_response(session_, stream_id, nv, data_prd); + delete [] nv; + return r; +} + +int SpdyEventHandler::submit_response(const std::string& status, + int32_t stream_id, + spdylay_data_provider *data_prd) +{ + const char *nv[] = { + ":status", status.c_str(), + ":version", "HTTP/1.1", + "server", SPDYD_SERVER.c_str(), + 0 + }; + return spdylay_submit_response(session_, stream_id, nv, data_prd); +} + +void SpdyEventHandler::add_stream(int32_t stream_id, Request *req) +{ + id2req_[stream_id] = req; +} + +void SpdyEventHandler::remove_stream(int32_t stream_id) +{ + Request *req = id2req_[stream_id]; + id2req_.erase(stream_id); + delete req; +} + +Request* SpdyEventHandler::get_stream(int32_t stream_id) +{ + return id2req_[stream_id]; +} + +int64_t SpdyEventHandler::session_id() const +{ + return session_id_; +} + +namespace { +ssize_t hd_send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + ssize_t r = hd->send_data(data, len, flags); + if(r < 0) { + if(hd->would_block(r)) { + r = SPDYLAY_ERR_WOULDBLOCK; + } else { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + return r; +} +} // namespace + +namespace { +ssize_t hd_recv_callback(spdylay_session *session, + uint8_t *data, size_t len, int flags, void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + ssize_t r = hd->recv_data(data, len, flags); + if(r < 0) { + if(hd->would_block(r)) { + r = SPDYLAY_ERR_WOULDBLOCK; + } else { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } else if(r == 0) { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + return r; +} +} // namespace + +ssize_t file_read_callback +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) +{ + int fd = source->fd; + ssize_t r; + while((r = read(fd, buf, length)) == -1 && errno == EINTR); + if(r == -1) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } else { + if(r == 0) { + *eof = 1; + } + return r; + } +} + +namespace { +bool check_url(const std::string& url) +{ + // We don't like '\' in url. + return !url.empty() && url[0] == '/' && + url.find('\\') == std::string::npos && + url.find("/../") == std::string::npos && + url.find("/./") == std::string::npos && + !util::endsWith(url, "/..") && !util::endsWith(url, "/."); +} +} // namespace + +namespace { +void prepare_status_response(Request *req, SpdyEventHandler *hd, + const std::string& status) +{ + int pipefd[2]; + if(pipe(pipefd) == -1) { + hd->submit_response(status, req->stream_id, 0); + } else { + std::stringstream ss; + ss << "" << status << "" + << "

" << status << "

" + << "
" + << "
" << SPDYD_SERVER << " at port " << hd->config()->port + << "
" + << ""; + std::string body = ss.str(); + write(pipefd[1], body.c_str(), body.size()); + close(pipefd[1]); + + req->file = pipefd[0]; + spdylay_data_provider data_prd; + data_prd.source.fd = pipefd[0]; + data_prd.read_callback = file_read_callback; + hd->submit_file_response(status, req->stream_id, 0, body.size(), &data_prd); + } +} +} // namespace + +namespace { +void prepare_response(Request *req, SpdyEventHandler *hd) +{ + std::string url; + bool url_found = false; + bool method_found = false; + bool scheme_found = false; + bool version_found = false; + bool host_found = false; + time_t last_mod = 0; + bool last_mod_found = false; + for(int i = 0; i < (int)req->headers.size(); ++i) { + const std::string &field = req->headers[i].first; + const std::string &value = req->headers[i].second; + if(!url_found && field == ":path") { + url_found = true; + url = value; + } else if(field == ":method") { + method_found = true; + } else if(field == ":scheme") { + scheme_found = true; + } else if(field == ":version") { + version_found = true; + } else if(field == ":host") { + host_found = true; + } else if(!last_mod_found && field == "if-modified-since") { + last_mod_found = true; + last_mod = util::parse_http_date(value); + } + } + if(!url_found || !method_found || !scheme_found || !version_found || + !host_found) { + prepare_status_response(req, hd, STATUS_400); + return; + } + std::string::size_type query_pos = url.find("?"); + if(query_pos != std::string::npos) { + url = url.substr(0, query_pos); + } + url = util::percentDecode(url.begin(), url.end()); + if(!check_url(url)) { + prepare_status_response(req, hd, STATUS_404); + return; + } + std::string path = hd->config()->htdocs+url; + if(path[path.size()-1] == '/') { + path += DEFAULT_HTML; + } + int file = open(path.c_str(), O_RDONLY); + if(file == -1) { + prepare_status_response(req, hd, STATUS_404); + } else { + struct stat buf; + if(fstat(file, &buf) == -1) { + close(file); + prepare_status_response(req, hd, STATUS_404); + } else { + req->file = file; + spdylay_data_provider data_prd; + data_prd.source.fd = file; + data_prd.read_callback = file_read_callback; + if(last_mod_found && buf.st_mtime <= last_mod) { + prepare_status_response(req, hd, STATUS_304); + } else { + hd->submit_file_response(STATUS_200, req->stream_id, buf.st_mtime, + buf.st_size, &data_prd); + } + } + } +} +} // namespace + +namespace { +void append_nv(Request *req, char **nv) +{ + for(int i = 0; nv[i]; i += 2) { + req->headers.push_back(std::make_pair(nv[i], nv[i+1])); + } +} +} // namespace + +namespace { +void hd_on_ctrl_recv_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + if(hd->config()->verbose) { + print_session_id(hd->session_id()); + on_ctrl_recv_callback(session, type, frame, user_data); + } + switch(type) { + case SPDYLAY_SYN_STREAM: { + int32_t stream_id = frame->syn_stream.stream_id; + Request *req = new Request(stream_id); + append_nv(req, frame->syn_stream.nv); + hd->add_stream(stream_id, req); + break; + } + case SPDYLAY_HEADERS: { + int32_t stream_id = frame->headers.stream_id; + Request *req = hd->get_stream(stream_id); + append_nv(req, frame->headers.nv); + break; + } + default: + break; + } +} +} // namespace + +void htdocs_on_request_recv_callback +(spdylay_session *session, int32_t stream_id, void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + prepare_response(hd->get_stream(stream_id), hd); +} + +namespace { +void hd_on_ctrl_send_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + if(hd->config()->verbose) { + print_session_id(hd->session_id()); + on_ctrl_send_callback(session, type, frame, user_data); + } +} +} // namespace + +namespace { +void on_data_chunk_recv_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t len, void *user_data) +{ + // TODO Handle POST +} +} // namespace + +namespace { +void hd_on_data_recv_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data) +{ + // TODO Handle POST + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + if(hd->config()->verbose) { + print_session_id(hd->session_id()); + on_data_recv_callback(session, flags, stream_id, length, user_data); + } +} +} // namespace + +namespace { +void hd_on_data_send_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + if(hd->config()->verbose) { + print_session_id(hd->session_id()); + on_data_send_callback(session, flags, stream_id, length, user_data); + } +} +} // namespace + +namespace { +void on_stream_close_callback +(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, + void *user_data) +{ + SpdyEventHandler *hd = (SpdyEventHandler*)user_data; + hd->remove_stream(stream_id); + if(hd->config()->verbose) { + print_session_id(hd->session_id()); + print_timer(); + printf(" stream_id=%d closed\n", stream_id); + fflush(stdout); + } +} +} // namespace + +class SSLAcceptEventHandler : public EventHandler { +public: + SSLAcceptEventHandler(const Config *config, + int fd, SSL *ssl, int64_t session_id) + : EventHandler(config), + fd_(fd), ssl_(ssl), version_(0), fail_(false), finish_(false), + want_read_(true), want_write_(true), + session_id_(session_id) + {} + virtual ~SSLAcceptEventHandler() + { + if(fail_) { + on_session_closed(this, session_id_); + SSL_shutdown(ssl_); + SSL_free(ssl_); + shutdown(fd_, SHUT_WR); + close(fd_); + } + } + virtual int execute(Sessions *sessions) + { + want_read_ = want_write_ = false; + int r = SSL_accept(ssl_); + if(r == 1) { + finish_ = true; + const unsigned char *next_proto = 0; + unsigned int next_proto_len; + SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len); + if(next_proto) { + std::string proto(next_proto, next_proto+next_proto_len); + if(config()->verbose) { + std::cout << "The negotiated next protocol: " << proto << std::endl; + } + version_ = spdylay_npn_get_version(next_proto, next_proto_len); + if(version_) { + add_next_handler(sessions); + } else { + fail_ = true; + } + } else { + fail_ = true; + } + } else if(r == 0) { + int e = SSL_get_error(ssl_, r); + if(e == SSL_ERROR_SSL) { + if(config()->verbose) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + } + } + finish_ = true; + fail_ = true; + } else { + int d = SSL_get_error(ssl_, r); + if(d == SSL_ERROR_WANT_READ) { + want_read_ = true; + } else if(d == SSL_ERROR_WANT_WRITE) { + want_write_ = true; + } else { + finish_ = true; + fail_ = true; + } + } + return 0; + } + virtual bool want_read() + { + return want_read_; + } + virtual bool want_write() + { + return want_write_; + } + virtual int fd() const + { + return fd_; + } + virtual bool finish() + { + return finish_; + } +private: + void add_next_handler(Sessions *sessions) + { + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = hd_send_callback; + callbacks.recv_callback = hd_recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + callbacks.on_ctrl_recv_callback = hd_on_ctrl_recv_callback; + callbacks.on_ctrl_send_callback = hd_on_ctrl_send_callback; + callbacks.on_data_recv_callback = hd_on_data_recv_callback; + callbacks.on_data_send_callback = hd_on_data_send_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_request_recv_callback = config()->on_request_recv_callback; + SpdyEventHandler *hd = new SpdyEventHandler(config(), + fd_, ssl_, version_, &callbacks, + session_id_); + if(sessions->mod_poll(hd) == -1) { + // fd_, ssl_ are freed by ~SpdyEventHandler() + delete hd; + } else { + sessions->add_handler(hd); + } + } + + int fd_; + SSL *ssl_; + uint16_t version_; + bool fail_, finish_; + bool want_read_, want_write_; + int64_t session_id_; +}; + +class ListenEventHandler : public EventHandler { +public: + ListenEventHandler(const Config* config, + int fd, int64_t *session_id_seed_ptr) + : EventHandler(config), + fd_(fd), session_id_seed_ptr_(session_id_seed_ptr) {} + virtual ~ListenEventHandler() + {} + virtual int execute(Sessions *sessions) + { + int cfd; + while((cfd = accept(fd_, 0, 0)) == -1 && errno == EINTR); + if(cfd != -1) { + if(make_non_block(cfd) == -1 || + set_tcp_nodelay(cfd) == -1) { + close(cfd); + } else { + add_next_handler(sessions, cfd); + } + } + return 0; + } + virtual bool want_read() + { + return true; + } + virtual bool want_write() + { + return false; + } + virtual int fd() const + { + return fd_; + } + virtual bool finish() + { + return false; + } +private: + void add_next_handler(Sessions *sessions, int cfd) + { + SSL *ssl = sessions->ssl_session_new(cfd); + if(ssl == 0) { + close(cfd); + return; + } + SSLAcceptEventHandler *hd = new SSLAcceptEventHandler + (config(), cfd, ssl, ++(*session_id_seed_ptr_)); + if(sessions->add_poll(hd) == -1) { + delete hd; + SSL_free(ssl); + close(cfd); + } else { + sessions->add_handler(hd); + } + } + + int fd_; + int64_t *session_id_seed_ptr_; +}; + +namespace { +void on_close(Sessions &sessions, EventHandler *hd) +{ + sessions.remove_handler(hd); + delete hd; +} +} // namespace + +SpdyServer::SpdyServer(const Config *config) + : config_(config) +{ + memset(sfd_, -1, sizeof(sfd_)); +} + +SpdyServer::~SpdyServer() +{ + for(int i = 0; i < 2; ++i) { + if(sfd_[i] != -1) { + close(sfd_[i]); + } + } +} + +int SpdyServer::listen() +{ + int families[] = { AF_INET, AF_INET6 }; + bool bind_ok = false; + for(int i = 0; i < 2; ++i) { + const char* ipv = (families[i] == AF_INET ? "IPv4" : "IPv6"); + int sfd = make_listen_socket(config_->host, config_->port, families[i]); + if(sfd == -1) { + std::cerr << ipv << ": Could not listen on port " << config_->port + << std::endl; + continue; + } + make_non_block(sfd); + sfd_[i] = sfd; + if(config_->verbose) { + std::cout << ipv << ": listen on port " << config_->port << std::endl; + } + bind_ok = true; + } + if(!bind_ok) { + return -1; + } + return 0; +} + +namespace { +int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, + void *arg) +{ + std::pair *next_proto = + reinterpret_cast* >(arg); + *data = next_proto->first; + *len = next_proto->second; + return SSL_TLSEXT_ERR_OK; +} +} // namespace + +int SpdyServer::run() +{ + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if(!ssl_ctx) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + if(SSL_CTX_use_PrivateKey_file(ssl_ctx, + config_->private_key_file.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl; + return -1; + } + if(SSL_CTX_use_certificate_file(ssl_ctx, config_->cert_file.c_str(), + SSL_FILETYPE_PEM) != 1) { + std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl; + return -1; + } + if(SSL_CTX_check_private_key(ssl_ctx) != 1) { + std::cerr << "SSL_CTX_check_private_key failed." << std::endl; + return -1; + } + + // We speaks "spdy/2" and "spdy/3". + std::pair next_proto; + unsigned char proto_list[14]; + + if(config_->spdy3_only) { + proto_list[0] = 6; + memcpy(&proto_list[1], "spdy/3", 6); + next_proto.first = proto_list; + next_proto.second = 7; + } else { + proto_list[0] = 6; + memcpy(&proto_list[1], "spdy/2", 6); + proto_list[7] = 6; + memcpy(&proto_list[8], "spdy/3", 6); + next_proto.first = proto_list; + next_proto.second = sizeof(proto_list); + } + + SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto); + + const size_t MAX_EVENTS = 256; + Sessions sessions(MAX_EVENTS, ssl_ctx); + + int64_t session_id_seed = 0; + int families[] = { AF_INET, AF_INET6 }; + bool bind_ok = false; + for(int i = 0; i < 2; ++i) { + const char* ipv = (families[i] == AF_INET ? "IPv4" : "IPv6"); + ListenEventHandler *listen_hd = new ListenEventHandler(config_, + sfd_[i], + &session_id_seed); + if(sessions.add_poll(listen_hd) == -1) { + std::cerr << ipv << ": Adding listening socket to poll failed." + << std::endl; + delete listen_hd; + } + sessions.add_handler(listen_hd); + bind_ok = true; + } + if(!bind_ok) { + return -1; + } + + std::vector del_list; + while(1) { + int n = sessions.poll(-1); + if(n == -1) { + perror("EventPoll"); + } else { + for(int i = 0; i < n; ++i) { + EventHandler *hd = reinterpret_cast + (sessions.get_user_data(i)); + int events = sessions.get_events(i); + int r = 0; + if(hd->mark_del()) { + continue; + } + if((events & EP_POLLIN) || (events & EP_POLLOUT)) { + r = hd->execute(&sessions); + } else if(events & (EP_POLLERR | EP_POLLHUP)) { + hd->mark_del(true); + } + if(r != 0) { + hd->mark_del(true); + } else { + if(hd->finish()) { + hd->mark_del(true); + } else { + sessions.mod_poll(hd); + } + } + if(hd->mark_del()) { + del_list.push_back(hd); + } + } + for(std::vector::iterator i = del_list.begin(), + eoi = del_list.end(); i != eoi; ++i) { + on_close(sessions, *i); + sessions.remove_handler(*i); + } + del_list.clear(); + } + } + return 0; +} + +} // namespace spdylay diff --git a/deps/spdylay/examples/SpdyServer.h b/deps/spdylay/examples/SpdyServer.h new file mode 100644 index 0000000..4aefde4 --- /dev/null +++ b/deps/spdylay/examples/SpdyServer.h @@ -0,0 +1,166 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDY_SERVER_H +#define SPDY_SERVER_H + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +namespace spdylay { + +struct Config { + std::string htdocs; + bool verbose; + bool daemon; + std::string host; + uint16_t port; + std::string private_key_file; + std::string cert_file; + spdylay_on_request_recv_callback on_request_recv_callback; + void *data_ptr; + bool spdy3_only; + Config(); +}; + +class Sessions; + +class EventHandler { +public: + EventHandler(const Config *config); + virtual ~EventHandler() {} + virtual int execute(Sessions *sessions) = 0; + virtual bool want_read() = 0; + virtual bool want_write() = 0; + virtual int fd() const = 0; + virtual bool finish() = 0; + const Config* config() const + { + return config_; + } + bool mark_del() + { + return mark_del_; + } + void mark_del(bool d) + { + mark_del_ = d; + } +private: + const Config *config_; + bool mark_del_; +}; + +struct Request { + int32_t stream_id; + std::vector > headers; + int file; + std::pair response_body; + Request(int32_t stream_id); + ~Request(); +}; + +class SpdyEventHandler : public EventHandler { +public: + SpdyEventHandler(const Config* config, + int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks, + int64_t session_id); + virtual ~SpdyEventHandler(); + virtual int execute(Sessions *sessions); + virtual bool want_read(); + virtual bool want_write(); + virtual int fd() const; + virtual bool finish(); + + uint16_t version() const; + + ssize_t send_data(const uint8_t *data, size_t len, int flags); + + ssize_t recv_data(uint8_t *data, size_t len, int flags); + + bool would_block(int r); + + int submit_file_response(const std::string& status, + int32_t stream_id, + time_t last_modified, + off_t file_length, + spdylay_data_provider *data_prd); + + int submit_response(const std::string& status, + int32_t stream_id, + spdylay_data_provider *data_prd); + + int submit_response + (const std::string& status, + int32_t stream_id, + const std::vector >& headers, + spdylay_data_provider *data_prd); + + void add_stream(int32_t stream_id, Request *req); + void remove_stream(int32_t stream_id); + Request* get_stream(int32_t stream_id); + int64_t session_id() const; +private: + spdylay_session *session_; + int fd_; + SSL* ssl_; + uint16_t version_; + int64_t session_id_; + bool want_write_; + std::map id2req_; +}; + +class SpdyServer { +public: + SpdyServer(const Config* config); + ~SpdyServer(); + int listen(); + int run(); +private: + const Config *config_; + int sfd_[2]; +}; + +void htdocs_on_request_recv_callback +(spdylay_session *session, int32_t stream_id, void *user_data); + +ssize_t file_read_callback +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data); + +} // namespace spdylay + +#endif // SPDY_SERVER_H diff --git a/deps/spdylay/examples/spdy.h b/deps/spdylay/examples/spdy.h new file mode 100644 index 0000000..ba3782c --- /dev/null +++ b/deps/spdylay/examples/spdy.h @@ -0,0 +1,233 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include +#include + +#include "spdylay_ssl.h" +#include "uri.h" +#include "util.h" +#include "SpdyServer.h" + +using namespace spdylay; + +namespace spdylay { + +class request { +public: + request(const std::vector>& headers) + : headers_(headers) + {} + + const std::vector>& headers() + { + return headers_; + } +private: + std::vector> headers_; +}; + +class response { +public: + response() + : status_code_(200) + {} + + void set_status(int status_code) + { + status_code_ = status_code; + } + + const char* get_status_string() const + { + switch(status_code_) { + case 100: return "100 Continue"; + case 101: return "101 Switching Protocols"; + case 200: return "200 OK"; + case 201: return "201 Created"; + case 202: return "202 Accepted"; + case 203: return "203 Non-Authoritative Information"; + case 204: return "204 No Content"; + case 205: return "205 Reset Content"; + case 206: return "206 Partial Content"; + case 300: return "300 Multiple Choices"; + case 301: return "301 Moved Permanently"; + case 302: return "302 Found"; + case 303: return "303 See Other"; + case 304: return "304 Not Modified"; + case 305: return "305 Use Proxy"; + // case 306: return "306 (Unused)"; + case 307: return "307 Temporary Redirect"; + case 400: return "400 Bad Request"; + case 401: return "401 Unauthorized"; + case 402: return "402 Payment Required"; + case 403: return "403 Forbidden"; + case 404: return "404 Not Found"; + case 405: return "405 Method Not Allowed"; + case 406: return "406 Not Acceptable"; + case 407: return "407 Proxy Authentication Required"; + case 408: return "408 Request Timeout"; + case 409: return "409 Conflict"; + case 410: return "410 Gone"; + case 411: return "411 Length Required"; + case 412: return "412 Precondition Failed"; + case 413: return "413 Request Entity Too Large"; + case 414: return "414 Request-URI Too Long"; + case 415: return "415 Unsupported Media Type"; + case 416: return "416 Requested Range Not Satisfiable"; + case 417: return "417 Expectation Failed"; + case 500: return "500 Internal Server Error"; + case 501: return "501 Not Implemented"; + case 502: return "502 Bad Gateway"; + case 503: return "503 Service Unavailable"; + case 504: return "504 Gateway Timeout"; + case 505: return "505 HTTP Version Not Supported"; + default: return ""; + } + } + + void set_header(const std::string& key, const std::string& value) + { + headers_.push_back(std::make_pair(key, value)); + } + + const std::vector>& get_headers() + { + return headers_; + } + + void end(const std::string& body) + { + body_ = body; + } + + const std::string& get_body() const + { + return body_; + } +private: + int status_code_; + std::string body_; + std::vector> headers_; +}; + +ssize_t string_read_callback +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data) +{ + std::pair& body_pair = + *reinterpret_cast*>(source->ptr); + const std::string& body = body_pair.first; + size_t off = body_pair.second; + ssize_t readlen = std::min(body.size()-off, length); + memcpy(buf, body.c_str()+off, readlen); + off += readlen; + if(off == body.size()) { + *eof = 1; + } + return readlen; +} + +void on_request_recv_callback +(spdylay_session *session, int32_t stream_id, void *user_data) +{ + SpdyEventHandler *hd = reinterpret_cast(user_data); + Request *req = hd->get_stream(stream_id); + request request_obj(req->headers); + response response_obj; + (*reinterpret_cast*> + (hd->config()->data_ptr))(request_obj, response_obj); + size_t body_length = response_obj.get_body().size(); + response_obj.set_header("content-length", util::to_str(body_length)); + req->response_body = std::make_pair(response_obj.get_body(), 0); + + spdylay_data_provider data_prd; + data_prd.source.ptr = &req->response_body; + data_prd.read_callback = string_read_callback; + hd->submit_response(response_obj.get_status_string(), stream_id, + response_obj.get_headers(), &data_prd); +} + +class spdy { +public: + spdy() : server_(0) {} + ~spdy() + { + delete server_; + } + bool listen(const std::string& host, uint16_t port, + const std::string& private_key_file, const std::string& cert_file, + std::function callback, + bool verbose = false) + { + delete server_; + callback_ = callback; + config_.verbose = verbose; + config_.host = host; + config_.port = port; + config_.private_key_file = private_key_file; + config_.cert_file = cert_file; + config_.on_request_recv_callback = on_request_recv_callback; + config_.data_ptr = &callback_; + server_ = new SpdyServer(&config_); + return server_->listen() == 0; + } + + int run() + { + return server_->run(); + } +private: + Config config_; + std::function callback_; + SpdyServer *server_; +}; + +namespace reactor { + +template +int run(Server& server) +{ + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + reset_timer(); + int r = server.run(); + if(r == 0) { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} + +} // namespace reactor + +} // namespace spdylay diff --git a/deps/spdylay/examples/spdycat.cc b/deps/spdylay/examples/spdycat.cc new file mode 100644 index 0000000..c0d29d2 --- /dev/null +++ b/deps/spdylay/examples/spdycat.cc @@ -0,0 +1,351 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "spdylay_ssl.h" +#include "uri.h" + +namespace spdylay { + +struct Config { + bool null_out; + bool remote_name; + bool verbose; + bool spdy3_only; + Config():null_out(false), remote_name(false), verbose(false), + spdy3_only(false) {} +}; + +struct Request { + uri::UriStruct us; + Request(const uri::UriStruct& us):us(us) {} +}; + +std::map stream2req; +size_t numreq, complete; + +Config config; +extern bool ssl_debug; + +void on_data_chunk_recv_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t len, void *user_data) +{ + std::map::iterator itr = stream2req.find(stream_id); + if(itr != stream2req.end()) { + std::cout.write(reinterpret_cast(data), len); + } +} + +void check_stream_id(spdylay_session *session, + spdylay_frame_type type, spdylay_frame *frame) +{ + int32_t stream_id = frame->syn_stream.stream_id; + Request *req = (Request*)spdylay_session_get_stream_user_data(session, + stream_id); + stream2req[stream_id] = req; +} + +void on_ctrl_send_callback2 +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + if(type == SPDYLAY_SYN_STREAM) { + check_stream_id(session, type, frame); + } +} + +void on_ctrl_send_callback3 +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + if(type == SPDYLAY_SYN_STREAM) { + check_stream_id(session, type, frame); + } + on_ctrl_send_callback(session, type, frame, user_data); +} + +void on_stream_close_callback +(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, + void *user_data) +{ + std::map::iterator itr = stream2req.find(stream_id); + if(itr != stream2req.end()) { + ++complete; + if(complete == numreq) { + spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); + } + } +} + +int communicate(const std::string& host, uint16_t port, + std::vector& reqvec, + const spdylay_session_callbacks *callbacks) +{ + numreq = reqvec.size(); + complete = 0; + stream2req.clear(); + int fd = connect_to(host, port); + if(fd == -1) { + std::cerr << "Could not connect to the host" << std::endl; + return -1; + } + SSL_CTX *ssl_ctx; + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if(!ssl_ctx) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + std::string next_proto; + if(config.spdy3_only) { + next_proto = "spdy/3"; + } + setup_ssl_ctx(ssl_ctx, &next_proto); + SSL *ssl = SSL_new(ssl_ctx); + if(!ssl) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + if(ssl_handshake(ssl, fd) == -1) { + return -1; + } + make_non_block(fd); + set_tcp_nodelay(fd); + int spdy_version = spdylay_npn_get_version( + reinterpret_cast(next_proto.c_str()), + next_proto.size()); + if (spdy_version <= 0) { + return -1; + } + Spdylay sc(fd, ssl, spdy_version, callbacks); + + nfds_t npollfds = 1; + pollfd pollfds[1]; + + std::stringstream ss; + if(reqvec[0].us.ipv6LiteralAddress) { + ss << "["; + } + ss << host; + if(reqvec[0].us.ipv6LiteralAddress) { + ss << "]"; + } + if(port != 443) { + ss << ":" << port; + } + std::string hostport = ss.str(); + + for(int i = 0, n = reqvec.size(); i < n; ++i) { + uri::UriStruct& us = reqvec[i].us; + std::string path = us.dir+us.file+us.query; + int r = sc.submit_request(hostport, path, 3, &reqvec[i]); + assert(r == 0); + } + pollfds[0].fd = fd; + ctl_poll(pollfds, &sc); + + bool ok = true; + while(sc.want_read() || sc.want_write()) { + int nfds = poll(pollfds, npollfds, -1); + if(nfds == -1) { + perror("poll"); + return -1; + } + if(pollfds[0].revents & (POLLIN | POLLOUT)) { + if(sc.recv() != 0 || sc.send() != 0) { + ok = false; + std::cout << "Fatal" << std::endl; + break; + } + } + if((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { + std::cout << "HUP" << std::endl; + ok = false; + break; + } + if(!ok) { + break; + } + ctl_poll(pollfds, &sc); + } + + SSL_shutdown(ssl); + SSL_free(ssl); + SSL_CTX_free(ssl_ctx); + shutdown(fd, SHUT_WR); + close(fd); + return ok ? 0 : -1; +} + +int run(char **uris, int n) +{ + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = send_callback; + callbacks.recv_callback = recv_callback; + callbacks.on_stream_close_callback = on_stream_close_callback; + if(config.verbose) { + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_data_recv_callback = on_data_recv_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback3; + } else { + callbacks.on_ctrl_send_callback = on_ctrl_send_callback2; + } + if(!config.null_out) { + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + } + ssl_debug = config.verbose; + std::vector reqvec; + std::string prev_host; + uint16_t prev_port = 0; + int failures = 0; + for(int i = 0; i < n; ++i) { + uri::UriStruct us; + if(uri::parse(us, uris[i])) { + if(prev_host != us.host || prev_port != us.port) { + if(!reqvec.empty()) { + if (communicate(prev_host, prev_port, reqvec, &callbacks) != 0) { + ++failures; + } + reqvec.clear(); + } + prev_host = us.host; + prev_port = us.port; + } + reqvec.push_back(Request(us)); + } + } + if(!reqvec.empty()) { + if (communicate(prev_host, prev_port, reqvec, &callbacks) != 0) { + ++failures; + } + } + return failures; +} + +void print_usage(std::ostream& out) +{ + out << "Usage: spdycat [-Onv] [URI...]" << std::endl; +} + +void print_help(std::ostream& out) +{ + print_usage(out); + out << "\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" + << " -3, --spdy3 Only use SPDY/3.\n" + << "\n" + << std::endl; +} + +int main(int argc, char **argv) +{ + while(1) { + static option long_options[] = { + {"verbose", no_argument, 0, 'v' }, + {"null-out", no_argument, 0, 'n' }, + {"remote-name", no_argument, 0, 'O' }, + {"spdy3", no_argument, 0, '3' }, + {"help", no_argument, 0, 'h' }, + {0, 0, 0, 0 } + }; + int option_index = 0; + int c = getopt_long(argc, argv, "Onhv3", long_options, &option_index); + if(c == -1) { + break; + } + switch(c) { + case 'O': + config.remote_name = true; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'n': + config.null_out = true; + break; + case 'v': + config.verbose = true; + break; + case '3': + config.spdy3_only = true; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + SSL_load_error_strings(); + SSL_library_init(); + reset_timer(); + return run(argv+optind, argc-optind); +} + +} // namespace spdylay + +int main(int argc, char **argv) +{ + return spdylay::main(argc, argv); +} diff --git a/deps/spdylay/examples/spdyd.cc b/deps/spdylay/examples/spdyd.cc new file mode 100644 index 0000000..f66a4a3 --- /dev/null +++ b/deps/spdylay/examples/spdyd.cc @@ -0,0 +1,163 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "spdylay_ssl.h" +#include "SpdyServer.h" + +namespace spdylay { + +extern bool ssl_debug; + +namespace { +void print_usage(std::ostream& out) +{ + out << "Usage: spdyd [-Ddhv] PORT PRIVATE_KEY CERT" << std::endl; +} +} // namespace + +namespace { +void print_help(std::ostream& out) +{ + print_usage(out); + out << "\n" + << "OPTIONS:\n" + << " -D, --daemon Run in a background. If -D is used, the\n" + << " current working directory is changed to '/'.\n" + << " Therefore if this option is used, -d option\n" + << " must be specified.\n" + << "\n" + << " -d, --htdocs=PATH Specify document root. If this option is not\n" + << " specified, the document root is the current\n" + << " working directory.\n" + << "\n" + << " -v, --verbose Print debug information such as reception/\n" + << " transmission of frames and name/value pairs.\n" + << "\n" + << " -3, --spdy3 Only use SPDY/3.\n" + << "\n" + << " -h, --help Print this help.\n" + << std::endl; +} +} // namespace + +int main(int argc, char **argv) +{ + Config config; + while(1) { + static option long_options[] = { + {"daemon", no_argument, 0, 'D' }, + {"htdocs", required_argument, 0, 'd' }, + {"help", no_argument, 0, 'h' }, + {"verbose", no_argument, 0, 'v' }, + {"spdy3", no_argument, 0, '3' }, + {0, 0, 0, 0 } + }; + int option_index = 0; + int c = getopt_long(argc, argv, "Dd:hv3", long_options, &option_index); + if(c == -1) { + break; + } + switch(c) { + case 'D': + config.daemon = true; + break; + case 'd': + config.htdocs = optarg; + break; + case 'h': + print_help(std::cout); + exit(EXIT_SUCCESS); + case 'v': + config.verbose = true; + break; + case '3': + config.spdy3_only = true; + break; + case '?': + exit(EXIT_FAILURE); + default: + break; + } + } + if(argc-optind < 3) { + print_usage(std::cerr); + std::cerr << "Too few arguments" << std::endl; + exit(EXIT_FAILURE); + } + if(config.daemon) { + if(config.htdocs.empty()) { + print_usage(std::cerr); + std::cerr << "-d option must be specified when -D is used." << std::endl; + exit(EXIT_FAILURE); + } + if(daemon(0, 0) == -1) { + perror("daemon"); + exit(EXIT_FAILURE); + } + } + if(config.htdocs.empty()) { + config.htdocs = "./"; + } + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, 0); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + SSL_library_init(); + reset_timer(); + config.port = strtol(argv[optind++], 0, 10); + config.private_key_file = argv[optind++]; + config.cert_file = argv[optind++]; + config.on_request_recv_callback = htdocs_on_request_recv_callback; + ssl_debug = config.verbose; + + SpdyServer server(&config); + if(server.listen() == 0) { + server.run(); + } + return 0; +} + +} // namespace spdylay + +int main(int argc, char **argv) +{ + return spdylay::main(argc, argv); +} diff --git a/deps/spdylay/examples/spdylay_ssl.cc b/deps/spdylay/examples/spdylay_ssl.cc new file mode 100644 index 0000000..64f50c1 --- /dev/null +++ b/deps/spdylay/examples/spdylay_ssl.cc @@ -0,0 +1,527 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spdylay_ssl.h" + +namespace spdylay { + +bool ssl_debug = false; + +Spdylay::Spdylay(int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks) + : fd_(fd), ssl_(ssl), version_(version), want_write_(false) +{ + int r = spdylay_session_client_new(&session_, version_, callbacks, this); + assert(r == 0); +} + +Spdylay::~Spdylay() +{ + spdylay_session_del(session_); +} + +int Spdylay::recv() +{ + return spdylay_session_recv(session_); +} + +int Spdylay::send() +{ + return spdylay_session_send(session_); +} + +ssize_t Spdylay::send_data(const uint8_t *data, size_t len, int flags) +{ + ssize_t r; + r = SSL_write(ssl_, data, len); + return r; +} + +ssize_t Spdylay::recv_data(uint8_t *data, size_t len, int flags) +{ + ssize_t r; + want_write_ = false; + r = SSL_read(ssl_, data, len); + if(r < 0) { + if(SSL_get_error(ssl_, r) == SSL_ERROR_WANT_WRITE) { + want_write_ = true; + } + } + return r; +} + +bool Spdylay::want_read() +{ + return spdylay_session_want_read(session_); +} + +bool Spdylay::want_write() +{ + return spdylay_session_want_write(session_) || want_write_; +} + +int Spdylay::fd() const +{ + return fd_; +} + +int Spdylay::submit_request(const std::string& hostport, + const std::string& path, uint8_t pri, + void *stream_user_data) +{ + const char *nv[] = { + ":method", "GET", + ":path", path.c_str(), + ":version", "HTTP/1.1", + ":scheme", "https", + ":host", hostport.c_str(), + "accept", "*/*", + "user-agent", "spdylay/"SPDYLAY_VERSION, + NULL + }; + return spdylay_submit_request(session_, pri, nv, NULL, stream_user_data); +} + +bool Spdylay::would_block(int r) +{ + int e = SSL_get_error(ssl_, r); + return e == SSL_ERROR_WANT_WRITE || e == SSL_ERROR_WANT_READ; +} + +int connect_to(const std::string& host, uint16_t port) +{ + struct addrinfo hints; + int fd = -1; + int r; + char service[10]; + snprintf(service, sizeof(service), "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + struct addrinfo *res; + r = getaddrinfo(host.c_str(), service, &hints, &res); + if(r != 0) { + std::cerr << "getaddrinfo: " << gai_strerror(r) << std::endl; + return -1; + } + for(struct addrinfo *rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(fd == -1) { + continue; + } + while((r = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && + errno == EINTR); + if(r == 0) { + break; + } + close(fd); + fd = -1; + } + freeaddrinfo(res); + return fd; +} + +int make_listen_socket(const std::string& host, uint16_t port, int family) +{ + addrinfo hints; + int fd = -1; + int r; + char service[10]; + snprintf(service, sizeof(service), "%u", port); + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res, *rp; + const char* host_ptr; + if(host.empty()) { + host_ptr = 0; + } else { + host_ptr = host.c_str(); + } + r = getaddrinfo(host_ptr, service, &hints, &res); + if(r != 0) { + std::cerr << "getaddrinfo: " << gai_strerror(r) << std::endl; + return -1; + } + for(rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(fd == -1) { + continue; + } + int val = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } +#ifdef IPV6_V6ONLY + if(family == AF_INET6) { + if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, + static_cast(sizeof(val))) == -1) { + close(fd); + continue; + } + } +#endif // IPV6_V6ONLY + + if(bind(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + break; + } + close(fd); + } + freeaddrinfo(res); + if(rp == 0) { + return -1; + } else { + if(listen(fd, 16) == -1) { + close(fd); + return -1; + } else { + return fd; + } + } +} + +int make_non_block(int fd) +{ + int flags, r; + while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); + if(flags == -1) { + return -1; + } + while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); + if(r == -1) { + return -1; + } + return 0; +} + +int set_tcp_nodelay(int fd) +{ + int val = 1; + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); +} + +ssize_t send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data) +{ + Spdylay *sc = (Spdylay*)user_data; + ssize_t r = sc->send_data(data, len, flags); + if(r < 0) { + if(sc->would_block(r)) { + r = SPDYLAY_ERR_WOULDBLOCK; + } else { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + return r; +} + +ssize_t recv_callback(spdylay_session *session, + uint8_t *data, size_t len, int flags, void *user_data) +{ + Spdylay *sc = (Spdylay*)user_data; + ssize_t r = sc->recv_data(data, len, flags); + if(r < 0) { + if(sc->would_block(r)) { + r = SPDYLAY_ERR_WOULDBLOCK; + } else { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } else if(r == 0) { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + return r; +} + +namespace { +const char *ctrl_names[] = { + "SYN_STREAM", + "SYN_REPLY", + "RST_STREAM", + "SETTINGS", + "NOOP", + "PING", + "GOAWAY", + "HEADERS", + "WINDOW_UPDATE" +}; +} // namespace + +namespace { +void print_frame_attr_indent() +{ + printf(" "); +} +} // namespace + +void print_nv(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + print_frame_attr_indent(); + printf("%s: %s\n", nv[i], nv[i+1]); + } +} + +void print_timer() +{ + timeval tv; + get_timer(&tv); + printf("[%3ld.%03ld]", tv.tv_sec, tv.tv_usec/1000); +} + +namespace { +void print_ctrl_hd(const spdylay_ctrl_hd& hd) +{ + printf("\n", + hd.version, hd.flags, hd.length); +} +} // namespace + +namespace { +void print_frame(spdylay_frame_type type, spdylay_frame *frame) +{ + printf("%s frame ", ctrl_names[type-1]); + print_ctrl_hd(frame->syn_stream.hd); + switch(type) { + case SPDYLAY_SYN_STREAM: + print_frame_attr_indent(); + printf("(stream_id=%d, assoc_stream_id=%d, pri=%u)\n", + frame->syn_stream.stream_id, frame->syn_stream.assoc_stream_id, + frame->syn_stream.pri); + print_nv(frame->syn_stream.nv); + break; + case SPDYLAY_SYN_REPLY: + print_frame_attr_indent(); + printf("(stream_id=%d)\n", frame->syn_reply.stream_id); + print_nv(frame->syn_reply.nv); + break; + case SPDYLAY_RST_STREAM: + print_frame_attr_indent(); + printf("(stream_id=%d, status_code=%u)\n", + frame->rst_stream.stream_id, frame->rst_stream.status_code); + break; + case SPDYLAY_SETTINGS: + print_frame_attr_indent(); + printf("(niv=%lu)\n", static_cast(frame->settings.niv)); + for(size_t i = 0; i < frame->settings.niv; ++i) { + print_frame_attr_indent(); + printf("[%d(%u):%u]\n", + frame->settings.iv[i].settings_id, + frame->settings.iv[i].flags, frame->settings.iv[i].value); + } + break; + case SPDYLAY_PING: + print_frame_attr_indent(); + printf("(unique_id=%d)\n", frame->ping.unique_id); + break; + case SPDYLAY_GOAWAY: + print_frame_attr_indent(); + printf("(last_good_stream_id=%d)\n", frame->goaway.last_good_stream_id); + break; + case SPDYLAY_HEADERS: + print_frame_attr_indent(); + printf("(stream_id=%d)\n", frame->headers.stream_id); + print_nv(frame->headers.nv); + break; + case SPDYLAY_WINDOW_UPDATE: + print_frame_attr_indent(); + printf("(stream_id=%d, delta_window_size=%d)\n", + frame->window_update.stream_id, + frame->window_update.delta_window_size); + break; + default: + printf("\n"); + break; + } +} +} // namespace + +void on_ctrl_recv_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + print_timer(); + printf(" recv "); + print_frame(type, frame); + fflush(stdout); +} + +void on_ctrl_send_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + print_timer(); + printf(" send "); + print_frame(type, frame); + fflush(stdout); +} + +namespace { +void print_data_frame(uint8_t flags, int32_t stream_id, int32_t length) +{ + printf("DATA frame (stream_id=%d, flags=%d, length=%d)\n", + stream_id, flags, length); +} +} // namespace + +void on_data_recv_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data) +{ + print_timer(); + printf(" recv "); + print_data_frame(flags, stream_id, length); + fflush(stdout); +} + +void on_data_send_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data) +{ + print_timer(); + printf(" send "); + print_data_frame(flags, stream_id, length); + fflush(stdout); +} + +void ctl_poll(pollfd *pollfd, Spdylay *sc) +{ + pollfd->events = 0; + if(sc->want_read()) { + pollfd->events |= POLLIN; + } + if(sc->want_write()) { + pollfd->events |= POLLOUT; + } +} + +int select_next_proto_cb(SSL* ssl, + unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) +{ + if(ssl_debug) { + print_timer(); + std::cout << " NPN select next protocol: the remote server offers:" + << std::endl; + } + for(unsigned int i = 0; i < inlen; i += in[i]+1) { + if(ssl_debug) { + std::cout << " * "; + std::cout.write(reinterpret_cast(&in[i+1]), in[i]); + std::cout << std::endl; + } + } + std::string& next_proto = *(std::string*)arg; + if(next_proto.empty()) { + if(spdylay_select_next_protocol(out, outlen, in, inlen) <= 0) { + std::cerr << "Server did not advertise spdy/2 or spdy/3 protocol." + << std::endl; + abort(); + } else { + next_proto.assign(&(*out)[0], &(*out)[*outlen]); + } + } else { + *out = (unsigned char*)(next_proto.c_str()); + *outlen = next_proto.size(); + } + if(ssl_debug) { + std::cout << " NPN selected the protocol: " + << std::string((const char*)*out, (size_t)*outlen) << std::endl; + } + return SSL_TLSEXT_ERR_OK; +} + +void setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg) +{ + /* Disable SSLv2 and enable all workarounds for buggy servers */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, + next_proto_select_cb_arg); +} + +int ssl_handshake(SSL *ssl, int fd) +{ + if(SSL_set_fd(ssl, fd) == 0) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + int r = SSL_connect(ssl); + if(r <= 0) { + std::cerr << ERR_error_string(ERR_get_error(), 0) << std::endl; + return -1; + } + return 0; +} + +namespace { +timeval base_tv; +} // namespace + +void reset_timer() +{ + gettimeofday(&base_tv, 0); +} + +void get_timer(timeval* tv) +{ + gettimeofday(tv, 0); + tv->tv_usec -= base_tv.tv_usec; + tv->tv_sec -= base_tv.tv_sec; + if(tv->tv_usec < 0) { + tv->tv_usec += 1000000; + --tv->tv_sec; + } +} + +} // namespace spdylay diff --git a/deps/spdylay/examples/spdylay_ssl.h b/deps/spdylay/examples/spdylay_ssl.h new file mode 100644 index 0000000..84afdd0 --- /dev/null +++ b/deps/spdylay/examples/spdylay_ssl.h @@ -0,0 +1,117 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_SSL_H +#define SPDYLAY_SSL_H + +#include +#include +#include +#include + +#include +#include +#include + +namespace spdylay { + +extern bool ssl_debug; + +class Spdylay { +public: + Spdylay(int fd, SSL *ssl, uint16_t version, + const spdylay_session_callbacks *callbacks); + ~Spdylay(); + int recv(); + int send(); + ssize_t send_data(const uint8_t *data, size_t len, int flags); + ssize_t recv_data(uint8_t *data, size_t len, int flags); + bool want_read(); + bool want_write(); + int fd() const; + int submit_request(const std::string& hostport, const std::string& path, + uint8_t pri, void *stream_user_data); + bool would_block(int r); +private: + int fd_; + SSL *ssl_; + uint16_t version_; + spdylay_session *session_; + bool want_write_; + bool debug_; +}; + +int connect_to(const std::string& host, uint16_t port); + +int make_listen_socket(const std::string& host, uint16_t port, int family); + +int make_non_block(int fd); + +int set_tcp_nodelay(int fd); + +ssize_t send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data); + +ssize_t recv_callback(spdylay_session *session, + uint8_t *data, size_t len, int flags, void *user_data); + +void print_nv(char **nv); + +void on_ctrl_recv_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +void on_ctrl_send_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +void on_data_recv_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data); + +void on_data_send_callback +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data); + +void ctl_poll(pollfd *pollfd, Spdylay *sc); + +int select_next_proto_cb(SSL* ssl, + unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg); + +void setup_ssl_ctx(SSL_CTX *ssl_ctx, void *next_proto_select_cb_arg); + +int ssl_handshake(SSL *ssl, int fd); + +void reset_timer(); + +void get_timer(timeval *tv); + +void print_timer(); + +} // namespace spdylay + +#endif // SPDYLAY_SSL_H diff --git a/deps/spdylay/examples/spdynative.cc b/deps/spdylay/examples/spdynative.cc new file mode 100644 index 0000000..3e64bd1 --- /dev/null +++ b/deps/spdylay/examples/spdynative.cc @@ -0,0 +1,42 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include "spdy.h" + +int main() +{ + spdy server; + if(!server.listen("localhost", 8080, "server.key", "server.crt", + [](request& req, response& res) { + res.set_status(200); + res.set_header("content-type", "text/plain"); + res.end("C++ FTW\n"); + })) + return EXIT_FAILURE; + + std::cout << "Server running at http://localhost:8080/" << std::endl; + return reactor::run(server); +} diff --git a/deps/spdylay/examples/uri.cc b/deps/spdylay/examples/uri.cc new file mode 100644 index 0000000..f1e98e6 --- /dev/null +++ b/deps/spdylay/examples/uri.cc @@ -0,0 +1,331 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "uri.h" + +#include +#include +#include + +#include "util.h" + +namespace spdylay { + +namespace uri { + +UriStruct::UriStruct() + : port(0), hasPassword(false), ipv6LiteralAddress(false) +{} + +UriStruct::UriStruct(const UriStruct& c) + : protocol(c.protocol), + host(c.host), + port(c.port), + dir(c.dir), + file(c.file), + query(c.query), + username(c.username), + password(c.password), + hasPassword(c.hasPassword), + ipv6LiteralAddress(c.ipv6LiteralAddress) +{} + +UriStruct::~UriStruct() {} + +UriStruct& UriStruct::operator=(const UriStruct& c) +{ + if(this != &c) { + protocol = c.protocol; + host = c.host; + port = c.port; + dir = c.dir; + file = c.file; + query = c.query; + username = c.username; + password = c.password; + hasPassword = c.hasPassword; + ipv6LiteralAddress = c.ipv6LiteralAddress; + } + return *this; +} + +void UriStruct::swap(UriStruct& other) +{ + using std::swap; + if(this != &other) { + swap(protocol, other.protocol); + swap(host, other.host); + swap(port, other.port); + swap(dir, other.dir); + swap(file, other.file); + swap(query, other.query); + swap(username, other.username); + swap(password, other.password); + swap(hasPassword, other.hasPassword); + swap(ipv6LiteralAddress, other.ipv6LiteralAddress); + } +} + +void swap(UriStruct& lhs, UriStruct& rhs) +{ + lhs.swap(rhs); +} + +bool parse(UriStruct& result, const std::string& uri) +{ + // http://user:password@aria2.sourceforge.net:80/dir/file?query#fragment + // | || || | | | | + // | || hostLast| | | | | + // | || portFirst| | | | + // authorityFirst || authorityLast | | | + // || | | | | + // userInfoLast | | | | + // | | | | | + // hostPortFirst | | | | + // | | | | + // dirFirst dirLast| | + // | | + // queryFirst fragmentFirst + + // find fragment part + std::string::const_iterator fragmentFirst = uri.begin(); + for(; fragmentFirst != uri.end(); ++fragmentFirst) { + if(*fragmentFirst == '#') break; + } + // find query part + std::string::const_iterator queryFirst = uri.begin(); + for(; queryFirst != fragmentFirst; ++queryFirst) { + if(*queryFirst == '?') break; + } + result.query.assign(queryFirst, fragmentFirst); + // find protocol + std::string::size_type protocolOffset = uri.find("://"); + if(protocolOffset == std::string::npos) return false; + result.protocol.assign(uri.begin(), uri.begin()+protocolOffset); + uint16_t defPort; + if(result.protocol == "http") { + defPort = 80; + } else if(result.protocol == "https") { + defPort = 443; + } else { + return false; + } + // find authority + std::string::const_iterator authorityFirst = uri.begin()+protocolOffset+3; + std::string::const_iterator authorityLast = authorityFirst; + for(; authorityLast != queryFirst; ++authorityLast) { + if(*authorityLast == '/') break; + } + if(authorityFirst == authorityLast) { + // No authority found + return false; + } + // find userinfo(username and password) in authority if they exist + result.username = ""; + result.password = ""; + result.hasPassword = false; + std::string::const_iterator userInfoLast = authorityLast; + std::string::const_iterator hostPortFirst = authorityFirst; + for(; userInfoLast != authorityFirst-1; --userInfoLast) { + if(*userInfoLast == '@') { + hostPortFirst = userInfoLast; + ++hostPortFirst; + std::string::const_iterator userLast = authorityFirst; + for(; userLast != userInfoLast; ++userLast) { + if(*userLast == ':') { + result.password = + util::percentDecode(userLast+1,userInfoLast); + result.hasPassword = true; + break; + } + } + result.username = + util::percentDecode(authorityFirst, userLast); + break; + } + } + std::string::const_iterator hostLast = hostPortFirst; + std::string::const_iterator portFirst = authorityLast; + result.ipv6LiteralAddress = false; + if(*hostPortFirst == '[') { + // Detected IPv6 literal address in square brackets + for(; hostLast != authorityLast; ++hostLast) { + if(*hostLast == ']') { + ++hostLast; + if(hostLast == authorityLast) { + result.ipv6LiteralAddress = true; + } else { + if(*hostLast == ':') { + portFirst = hostLast; + ++portFirst; + result.ipv6LiteralAddress = true; + } + } + break; + } + } + if(!result.ipv6LiteralAddress) { + return false; + } + } else { + for(; hostLast != authorityLast; ++hostLast) { + if(*hostLast == ':') { + portFirst = hostLast; + ++portFirst; + break; + } + } + } + if(hostPortFirst == hostLast) { + // No host + return false; + } + if(portFirst == authorityLast) { + // If port is not specified, then we set it to default port of + // its protocol.. + result.port = defPort; + } else { + errno = 0; + uint32_t tempPort = strtol(std::string(portFirst, authorityLast).c_str(), + 0, 10); + if(errno != 0) { + return false; + } else if(65535 < tempPort) { + return false; + } + result.port = tempPort; + } + if(result.ipv6LiteralAddress) { + result.host.assign(hostPortFirst+1, hostLast-1); + } else { + result.host.assign(hostPortFirst, hostLast); + } + // find directory and file part + std::string::const_iterator dirLast = authorityLast; + for(std::string::const_iterator i = authorityLast; + i != queryFirst; ++i) { + if(*i == '/') { + dirLast = i+1; + } + } + if(dirLast == queryFirst) { + result.file = ""; + } else { + result.file.assign(dirLast, queryFirst); + } + // dirFirst == authorityLast + if(authorityLast == dirLast) { + result.dir = "/"; + } else { + result.dir.assign(authorityLast, dirLast); + } + return true; +} + +std::string construct(const UriStruct& us) +{ + std::string res; + res += us.protocol; + res += "://"; + if(!us.username.empty()) { + res += util::percentEncode(us.username); + if(us.hasPassword) { + res += ":"; + res += util::percentEncode(us.password); + } + res += "@"; + } + if(us.ipv6LiteralAddress) { + res += "["; + res += us.host; + res += "]"; + } else { + res += us.host; + } + uint16_t defPort; + if(us.protocol == "http") { + defPort = 80; + } else if(us.protocol == "https") { + defPort = 443; + } else { + defPort = 0; + } + if(us.port != 0 && defPort != us.port) { + char temp[10]; + snprintf(temp, sizeof(temp), ":%u", us.port); + res += temp; + } + res += us.dir; + if(us.dir.empty() || us.dir[us.dir.size()-1] != '/') { + res += "/"; + } + res += us.file; + res += us.query; + return res; +} + +std::string joinUri(const std::string& baseUri, const std::string& uri) +{ + UriStruct us; + if(parse(us, uri)) { + return uri; + } else { + UriStruct bus; + if(!parse(bus, baseUri)) { + return uri; + } + std::vector parts; + if(uri.empty() || uri[0] != '/') { + util::split(bus.dir.begin(), bus.dir.end(), std::back_inserter(parts), + '/'); + } + std::string::const_iterator qend; + for(qend = uri.begin(); qend != uri.end(); ++qend) { + if(*qend == '#') { + break; + } + } + std::string::const_iterator end; + for(end = uri.begin(); end != qend; ++end) { + if(*end == '?') { + break; + } + } + util::split(uri.begin(), end, std::back_inserter(parts), '/'); + bus.dir.clear(); + bus.file.clear(); + bus.query.clear(); + std::string res = construct(bus); + res += util::joinPath(parts.begin(), parts.end()); + if((uri.begin() == end || *(end-1) == '/') && *(res.end()-1) != '/') { + res += "/"; + } + res.append(end, qend); + return res; + } +} + +} // namespace uri + +} // namespace spdylay diff --git a/deps/spdylay/examples/uri.h b/deps/spdylay/examples/uri.h new file mode 100644 index 0000000..fc0baf1 --- /dev/null +++ b/deps/spdylay/examples/uri.h @@ -0,0 +1,71 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef URI_H +#define URI_H + +#include + +#include + +namespace spdylay { + +namespace uri { + +struct UriStruct { + std::string protocol; + std::string host; + uint16_t port; + std::string dir; + std::string file; + std::string query; + std::string username; + std::string password; + bool hasPassword; + bool ipv6LiteralAddress; + + UriStruct(); + UriStruct(const UriStruct& c); + ~UriStruct(); + + UriStruct& operator=(const UriStruct& c); + void swap(UriStruct& other); +}; + +void swap(UriStruct& lhs, UriStruct& rhs); + +// Splits URI uri into components and stores them into result. On +// success returns true. Otherwise returns false and result is +// undefined. +bool parse(UriStruct& result, const std::string& uri); + +std::string construct(const UriStruct& us); + +std::string joinUri(const std::string& baseUri, const std::string& uri); + +} // namespace uri + +} // namespace spdylay + +#endif // URI_H diff --git a/deps/spdylay/examples/util.cc b/deps/spdylay/examples/util.cc new file mode 100644 index 0000000..a2cda70 --- /dev/null +++ b/deps/spdylay/examples/util.cc @@ -0,0 +1,129 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "util.h" + +#include + +#include +#include + +namespace spdylay { + +namespace util { + +const std::string DEFAULT_STRIP_CHARSET("\r\n\t "); + +bool isAlpha(const char c) +{ + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +bool isDigit(const char c) +{ + return '0' <= c && c <= '9'; +} + +bool isHexDigit(const char c) +{ + return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); +} + +bool inRFC3986UnreservedChars(const char c) +{ + static const char unreserved[] = { '-', '.', '_', '~' }; + return isAlpha(c) || isDigit(c) || + std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4]; +} + +std::string percentEncode(const unsigned char* target, size_t len) +{ + std::string dest; + for(size_t i = 0; i < len; ++i) { + if(inRFC3986UnreservedChars(target[i])) { + dest += target[i]; + } else { + char temp[4]; + snprintf(temp, sizeof(temp), "%%%02X", target[i]); + dest.append(temp); + //dest.append(fmt("%%%02X", target[i])); + } + } + return dest; +} + +std::string percentEncode(const std::string& target) +{ + return percentEncode(reinterpret_cast(target.c_str()), + target.size()); +} + +std::string percentDecode +(std::string::const_iterator first, std::string::const_iterator last) +{ + std::string result; + for(; first != last; ++first) { + if(*first == '%') { + if(first+1 != last && first+2 != last && + isHexDigit(*(first+1)) && isHexDigit(*(first+2))) { + std::string numstr(first+1, first+3); + result += strtol(numstr.c_str(), 0, 16); + first += 2; + } else { + result += *first; + } + } else { + result += *first; + } + } + return result; +} + +std::string http_date(time_t t) +{ + char buf[32]; + tm* tms = gmtime(&t); // returned struct is statically allocated. + size_t r = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tms); + return std::string(&buf[0], &buf[r]); +} + +time_t parse_http_date(const std::string& s) +{ + tm tm; + memset(&tm, 0, sizeof(tm)); + char* r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm); + if(r == 0) { + return 0; + } + return timegm(&tm); +} + +bool endsWith(const std::string& a, const std::string& b) +{ + return endsWith(a.begin(), a.end(), b.begin(), b.end()); +} + +} // namespace util + +} // namespace spdylay diff --git a/deps/spdylay/examples/util.h b/deps/spdylay/examples/util.h new file mode 100644 index 0000000..f2f549a --- /dev/null +++ b/deps/spdylay/examples/util.h @@ -0,0 +1,196 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include +#include + +namespace spdylay { + +namespace util { + +extern const std::string DEFAULT_STRIP_CHARSET; + +template +std::pair stripIter +(InputIterator first, InputIterator last, + const std::string& chars = DEFAULT_STRIP_CHARSET) +{ + for(; first != last && + std::find(chars.begin(), chars.end(), *first) != chars.end(); ++first); + if(first == last) { + return std::make_pair(first, last); + } + InputIterator left = last-1; + for(; left != first && + std::find(chars.begin(), chars.end(), *left) != chars.end(); --left); + return std::make_pair(first, left+1); +} + +template +OutputIterator splitIter +(InputIterator first, + InputIterator last, + OutputIterator out, + char delim, + bool doStrip = false, + bool allowEmpty = false) +{ + for(InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if(doStrip) { + p = stripIter(i, j); + } + if(allowEmpty || p.first != p.second) { + *out++ = p; + } + i = j; + if(j != last) { + ++i; + } + } + if(allowEmpty && + (first == last || *(last-1) == delim)) { + *out++ = std::make_pair(last, last); + } + return out; +} + +template +OutputIterator split +(InputIterator first, + InputIterator last, + OutputIterator out, + char delim, + bool doStrip = false, + bool allowEmpty = false) +{ + for(InputIterator i = first; i != last;) { + InputIterator j = std::find(i, last, delim); + std::pair p(i, j); + if(doStrip) { + p = stripIter(i, j); + } + if(allowEmpty || p.first != p.second) { + *out++ = std::string(p.first, p.second); + } + i = j; + if(j != last) { + ++i; + } + } + if(allowEmpty && + (first == last || *(last-1) == delim)) { + *out++ = std::string(last, last); + } + return out; +} + +template +std::string strjoin(InputIterator first, InputIterator last, + const DelimiterType& delim) +{ + std::string result; + if(first == last) { + return result; + } + InputIterator beforeLast = last-1; + for(; first != beforeLast; ++first) { + result += *first; + result += delim; + } + result += *beforeLast; + return result; +} + +template +std::string joinPath(InputIterator first, InputIterator last) +{ + std::vector elements; + for(;first != last; ++first) { + if(*first == "..") { + if(!elements.empty()) { + elements.pop_back(); + } + } else if(*first == ".") { + // do nothing + } else { + elements.push_back(*first); + } + } + return strjoin(elements.begin(), elements.end(), "/"); +} + +bool isAlpha(const char c); + +bool isDigit(const char c); + +bool isHexDigit(const char c); + +bool inRFC3986UnreservedChars(const char c); + +std::string percentEncode(const unsigned char* target, size_t len); + +std::string percentEncode(const std::string& target); + +std::string percentDecode +(std::string::const_iterator first, std::string::const_iterator last); + +std::string http_date(time_t t); + +time_t parse_http_date(const std::string& s); + +template +std::string to_str(T value) +{ + std::stringstream ss; + ss << value; + return ss.str(); +} + +template +bool endsWith +(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2) +{ + if(last1-first1 < last2-first2) { + return false; + } + return std::equal(first2, last2, last1-(last2-first2)); +} + +bool endsWith(const std::string& a, const std::string& b); + +} // namespace util + +} // namespace spdylay + +#endif // UTIL_H diff --git a/deps/spdylay/lib/Makefile.am b/deps/spdylay/lib/Makefile.am new file mode 100644 index 0000000..7c756f8 --- /dev/null +++ b/deps/spdylay/lib/Makefile.am @@ -0,0 +1,47 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +SUBDIRS = includes + +AM_CFLAGS = -Wall +AM_CPPFLAGS = -I$(srcdir)/includes -I$(builddir)/includes + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libspdylay.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libspdylay.la + +OBJECTS = spdylay_pq.c spdylay_map.c spdylay_queue.c \ + spdylay_buffer.c spdylay_frame.c spdylay_zlib.c \ + spdylay_session.c spdylay_helper.c spdylay_stream.c spdylay_npn.c \ + spdylay_submit.c spdylay_outbound_item.c + +HFILES = spdylay_pq.h spdylay_int.h spdylay_map.h spdylay_queue.h \ + spdylay_buffer.h spdylay_frame.h spdylay_zlib.h \ + spdylay_session.h spdylay_helper.h spdylay_stream.h spdylay_int.h \ + spdylay_npn.h \ + spdylay_submit.h spdylay_outbound_item.h + +libspdylay_la_SOURCES = $(HFILES) $(OBJECTS) +libspdylay_la_LDFLAGS = -no-undefined \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) diff --git a/deps/spdylay/lib/includes/Makefile.am b/deps/spdylay/lib/includes/Makefile.am new file mode 100644 index 0000000..3d466fe --- /dev/null +++ b/deps/spdylay/lib/includes/Makefile.am @@ -0,0 +1,23 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +nobase_include_HEADERS = spdylay/spdylay.h spdylay/spdylayver.h diff --git a/deps/spdylay/lib/includes/spdylay/spdylay.h b/deps/spdylay/lib/includes/spdylay/spdylay.h new file mode 100644 index 0000000..0e8c0d6 --- /dev/null +++ b/deps/spdylay/lib/includes/spdylay/spdylay.h @@ -0,0 +1,1616 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_H +#define SPDYLAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +struct spdylay_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a SPDY + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct spdylay_session spdylay_session; + +/** + * @macro + * + * SPDY protocol version 2 + */ +#define SPDYLAY_PROTO_SPDY2 2 +/** + * @macro + * + * SPDY protocol version 3 + */ +#define SPDYLAY_PROTO_SPDY3 3 + +/** + * @enum + * + * Error codes used in the Spdylay library. The following values are + * defined: + */ +typedef enum { + /** + * Invalid argument passed. + */ + SPDYLAY_ERR_INVALID_ARGUMENT = -501, + /** + * Zlib error. + */ + SPDYLAY_ERR_ZLIB = -502, + /** + * The specified protocol version is not supported. + */ + SPDYLAY_ERR_UNSUPPORTED_VERSION = -503, + /** + * Used as a return value from :type:`spdylay_send_callback` and + * :type:`spdylay_recv_callback` to indicate that the operation + * would block. + */ + SPDYLAY_ERR_WOULDBLOCK = -504, + /** + * General protocol error + */ + SPDYLAY_ERR_PROTO = -505, + /** + * The frame is invalid. + */ + SPDYLAY_ERR_INVALID_FRAME = -506, + /** + * The peer performed a shutdown on the connection. + */ + SPDYLAY_ERR_EOF = -507, + /** + * Used as a return value from + * :func:`spdylay_data_source_read_callback` to indicate that data + * transfer is postponed. See + * :func:`spdylay_data_source_read_callback` for details. + */ + SPDYLAY_ERR_DEFERRED = -508, + /** + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ + SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE = -509, + /** + * The stream is already closed; or the stream ID is invalid. + */ + SPDYLAY_ERR_STREAM_CLOSED = -510, + /** + * RST_STREAM has been added to the outbound queue. The stream is in + * closing state. + */ + SPDYLAY_ERR_STREAM_CLOSING = -511, + /** + * The transmission is not allowed for this stream (e.g., a frame + * with FLAG_FIN flag set has already sent). + */ + SPDYLAY_ERR_STREAM_SHUT_WR = -512, + /** + * The stream ID is invalid. + */ + SPDYLAY_ERR_INVALID_STREAM_ID = -513, + /** + * The state of the stream is not valid (e.g., SYN_REPLY cannot be + * sent to the stream if SYN_REPLY has already been sent). + */ + SPDYLAY_ERR_INVALID_STREAM_STATE = -514, + /** + * Another DATA frame has already been deferred. + */ + SPDYLAY_ERR_DEFERRED_DATA_EXIST = -515, + /** + * SYN_STREAM is not allowed. (e.g., GOAWAY has been sent and/or + * received. + */ + SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED = -516, + /** + * GOAWAY has already been sent. + */ + SPDYLAY_ERR_GOAWAY_ALREADY_SENT = -517, + /** + * The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is + * under unexpected condition and cannot process any further data + * reliably (e.g., out of memory). + */ + SPDYLAY_ERR_FATAL = -900, + /** + * Out of memory. This is a fatal error. + */ + SPDYLAY_ERR_NOMEM = -901, + /** + * The user callback function failed. This is a fatal error. + */ + SPDYLAY_ERR_CALLBACK_FAILURE = -902, +} spdylay_error; + +typedef enum { + SPDYLAY_MSG_MORE +} spdylay_io_flag; + +/** + * @enum + * The frame types in SPDY protocol. + */ +typedef enum { + /** + * The SYN_STREAM control frame. + */ + SPDYLAY_SYN_STREAM = 1, + /** + * The SYN_REPLY control frame. + */ + SPDYLAY_SYN_REPLY = 2, + /** + * The RST_STREAM control frame. + */ + SPDYLAY_RST_STREAM = 3, + /** + * The SETTINGS control frame. + */ + SPDYLAY_SETTINGS = 4, + /** + * The NOOP control frame. This was deprecated in SPDY/3. + */ + SPDYLAY_NOOP = 5, + /** + * The PING control frame. + */ + SPDYLAY_PING = 6, + /** + * The GOAWAY control frame. + */ + SPDYLAY_GOAWAY = 7, + /** + * The HEADERS control frame. + */ + SPDYLAY_HEADERS = 8, + /** + * The WINDOW_UPDATE control frame. This first appeared in SPDY/3. + */ + SPDYLAY_WINDOW_UPDATE = 9, + /** + * The DATA frame. + */ + SPDYLAY_DATA = 100, +} spdylay_frame_type; + +/** + * @enum + * + * The flags for a control frame. + */ +typedef enum { + /** + * No flag set. + */ + SPDYLAY_CTRL_FLAG_NONE = 0, + /** + * FLAG_FIN flag. + */ + SPDYLAY_CTRL_FLAG_FIN = 0x1, + /** + * FLAG_UNIDIRECTIONAL flag. + */ + SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL = 0x2 +} spdylay_ctrl_flag; + +/** + * @enum + * The flags for a DATA frame. + */ +typedef enum { + /** + * No flag set. + */ + SPDYLAY_DATA_FLAG_NONE = 0, + /** + * FLAG_FIN flag. + */ + SPDYLAY_DATA_FLAG_FIN = 0x1 +} spdylay_data_flag; + +/** + * @enum + * The flags for the SETTINGS control frame. + */ +typedef enum { + /** + * No flag set. + */ + SPDYLAY_FLAG_SETTINGS_NONE = 0, + /** + * SETTINGS_CLEAR_SETTINGS flag. + */ + SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS = 1 +} spdylay_settings_flag; + +/** + * @enum + * The flags for SETTINGS ID/value pair. + */ +typedef enum { + /** + * No flag set. + */ + SPDYLAY_ID_FLAG_SETTINGS_NONE = 0, + /** + * FLAG_SETTINGS_PERSIST_VALUE flag. + */ + SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE = 1, + /** + * FLAG_SETTINGS_PERSISTED flag. + */ + SPDYLAY_ID_FLAG_SETTINGS_PERSISTED = 2 +} spdylay_settings_id_flag; + +/** + * @enum + * The SETTINGS ID. + */ +typedef enum { + /** + * SETTINGS_UPLOAD_BANDWIDTH + */ + SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH = 1, + /** + * SETTINGS_DOWNLOAD_BANDWIDTH + */ + SPDYLAY_SETTINGS_DOWNLOAD_BANDWIDTH = 2, + /** + * SETTINGS_ROUND_TRIP_TIME + */ + SPDYLAY_SETTINGS_ROUND_TRIP_TIME = 3, + /** + * SETTINGS_MAX_CONCURRENT_STREAMS + */ + SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS = 4, + /** + * SETTINGS_CURRENT_CWND + */ + SPDYLAY_SETTINGS_CURRENT_CWND = 5, + /** + * SETTINGS_DOWNLOAD_RETRANS_RATE + */ + SPDYLAY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6, + /** + * SETTINGS_INITIAL_WINDOW_SIZE + */ + SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE = 7, + /** + * SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE. This first appeared in + * SPDY/3. + */ + SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8 +} spdylay_settings_id; + +/** + * @macro + * Maximum ID of :type:`spdylay_settings_id`. + */ +#define SPDYLAY_SETTINGS_MAX 8 + +/** + * @macro + * Default maximum concurrent streams. + */ +#define SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS 100 + +/** + * @enum + * The status codes for the RST_STREAM control frame. + */ +typedef enum { + /** + * SPDYLAY_OK is not valid status code for RST_STREAM. It is defined + * just for spdylay library use. + */ + SPDYLAY_OK = 0, + /** + * PROTOCOL_ERROR + */ + SPDYLAY_PROTOCOL_ERROR = 1, + /** + * INVALID_STREAM + */ + SPDYLAY_INVALID_STREAM = 2, + /** + * REFUSED_STREAM + */ + SPDYLAY_REFUSED_STREAM = 3, + /** + * UNSUPPORTED_VERSION + */ + SPDYLAY_UNSUPPORTED_VERSION = 4, + /** + * CANCEL + */ + SPDYLAY_CANCEL = 5, + /** + * INTERNAL_ERROR + */ + SPDYLAY_INTERNAL_ERROR = 6, + /** + * FLOW_CONTROL_ERROR + */ + SPDYLAY_FLOW_CONTROL_ERROR = 7, + /* Following status codes were introduced in SPDY/3 */ + /** + * STREAM_IN_USE + */ + SPDYLAY_STREAM_IN_USE = 8, + /** + * STREAM_ALREADY_CLOSED + */ + SPDYLAY_STREAM_ALREADY_CLOSED = 9, + /** + * SPDYLAY_INVALID_CREDENTIALS + */ + SPDYLAY_INVALID_CREDENTIALS = 10, + /** + * FRAME_TOO_LARGE + */ + FRAME_TOO_LARGE = 11 +} spdylay_status_code; + +/** + * @enum + * The status codes for GOAWAY, introduced in SPDY/3. + */ +typedef enum { + /** + * OK. This indicates a normal session teardown. + */ + SPDYLAY_GOAWAY_OK = 0, + /** + * PROTOCOL_ERROR + */ + SPDYLAY_GOAWAY_PROTOCOL_ERROR = 1, + /** + * INTERNAL_ERROR + */ + SPDYLAY_GOAWAY_INTERNAL_ERROR = 11 +} spdylay_goaway_status_code; + +/** + * @macro + * Lowest priority value in SPDY/2, which is 3. + */ +#define SPDYLAY_SPDY2_PRI_LOWEST 3 +/** + * @macro + * Lowest priority value in SPDY/3, which is 7. + */ +#define SPDYLAY_SPDY3_PRI_LOWEST 7 + +/** + * @struct + * The control frame header. + */ +typedef struct { + /** + * SPDY protocol version. + */ + uint16_t version; + /** + * The type of this control frame. + */ + uint16_t type; + /** + * The control frame flags. + */ + uint8_t flags; + /** + * The length field of this control frame. + */ + int32_t length; +} spdylay_ctrl_hd; + +/** + * @struct + * The SYN_STREAM control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The associated-to-stream ID. 0 if this frame has no + * associated-to-stream. + */ + int32_t assoc_stream_id; + /** + * The priority of this frame. 0 (Highest) to + * :macro:`SPDYLAY_SPDY2_PRI_LOWEST` or + * :macro:`SPDYLAY_SPDY3_PRI_LOWEST` (lowest), depending on the + * protocol version. + */ + uint8_t pri; + /** + * The index in server's CREDENTIAL vector of the client certificate. + * This was introduced in SPDY/3. + */ + uint8_t slot; + /** + * The name/value pairs. For i > 0, ``nv[2*i]`` contains a pointer + * to the name string and ``nv[2*i+1]`` contains a pointer to the + * value string. The one beyond last value must be ``NULL``. That + * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be + * ``NULL``. + */ + char **nv; +} spdylay_syn_stream; + +/** + * @struct + * The SYN_REPLY control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The name/value pairs. For i > 0, ``nv[2*i]`` contains a pointer + * to the name string and ``nv[2*i+1]`` contains a pointer to the + * value string. The one beyond last value must be ``NULL``. That + * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be + * ``NULL``. + */ + char **nv; +} spdylay_syn_reply; + +/** + * @struct + * The HEADERS control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The name/value pairs. For i > 0, ``nv[2*i]`` contains a pointer + * to the name string and ``nv[2*i+1]`` contains a pointer to the + * value string. The one beyond last value must be ``NULL``. That + * is, if the |nv| contains N name/value pairs, ``nv[2*N]`` must be + * ``NULL``. + */ + char **nv; +} spdylay_headers; + +/** + * @struct + * The RST_STREAM control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The status code. See :type:`spdylay_status_code`. + */ + uint32_t status_code; +} spdylay_rst_stream; + +/** + * @struct + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`spdylay_settings_id`. + */ + int32_t settings_id; + /** + * The flags. See :type:`spdylay_settings_id_flag`. + */ + uint8_t flags; + /** + * The value of this entry. + */ + uint32_t value; +} spdylay_settings_entry; + +/** + * @struct + * The SETTINGS control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + spdylay_settings_entry *iv; +} spdylay_settings; + +/** + * @struct + * The PING control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The unique ID. + */ + uint32_t unique_id; +} spdylay_ping; + +/** + * @struct + * The GOAWAY control frame. It has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The last-good-stream ID. + */ + int32_t last_good_stream_id; + /** + * The status code. This first appeared in SPDY/3. See + * :type:`spdylay_goaway_status_code`. + */ + uint32_t status_code; +} spdylay_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE control frame. This first appeared in SPDY/3. It + * has the following members: + */ +typedef struct { + /** + * The control frame header. + */ + spdylay_ctrl_hd hd; + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The delta-window-size. + */ + int32_t delta_window_size; +} spdylay_window_update; + +/** + * @union + * + * This union represents the some kind of data source passed to + * :type:`spdylay_data_source_read_callback`. + */ +typedef union { + /** + * The integer field, suitable for a file descriptor. + */ + int fd; + /** + * The pointer to an arbitrary object. + */ + void *ptr; +} spdylay_data_source; + +/** + * @functypedef + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. The + * implementation of this function must read at most |length| bytes of + * data from |source| (or possibly other places) and store them in + * |buf| and return number of data stored in |buf|. If EOF is reached, + * set |*eof| to 1. If the application wants to postpone DATA frames, + * (e.g., asynchronous I/O, or reading data blocks for long time), it + * is achieved by returning :enum:`SPDYLAY_ERR_DEFERRED` without + * reading any data in this invocation. The library removes DATA + * frame from the outgoing queue temporarily. To move back deferred + * DATA frame to outgoing queue, call `spdylay_session_resume_data()`. + * In case of error, return :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`, + * which leads to session failure. + */ +typedef ssize_t (*spdylay_data_source_read_callback) +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t length, int *eof, + spdylay_data_source *source, void *user_data); + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { + /** + * The data source. + */ + spdylay_data_source source; + /** + * The callback function to read a chunk of data from the |source|. + */ + spdylay_data_source_read_callback read_callback; +} spdylay_data_provider; + +/** + * @struct + * The DATA frame. It has the following members: + */ +typedef struct { + /** + * The stream ID. + */ + int32_t stream_id; + /** + * The DATA frame flags. See :type:`spdylay_data_flag`. + */ + uint8_t flags; + /** + * The flag to indicate whether EOF was reached or not. Initially + * |eof| is 0. It becomes 1 after all data were read. + */ + uint8_t eof; + /** + * The data to be sent for this DATA frame. + */ + spdylay_data_provider data_prd; +} spdylay_data; + +/** + * @union + * + * This union includes all control frames and DATA frame to pass them + * to various function calls as spdylay_frame type. + */ +typedef union { + /** + * The SYN_STREAM control frame. + */ + spdylay_syn_stream syn_stream; + /** + * The SYN_REPLY control frame. + */ + spdylay_syn_reply syn_reply; + /** + * The RST_STREAM control frame. + */ + spdylay_rst_stream rst_stream; + /** + * The SETTINGS control frame. + */ + spdylay_settings settings; + /** + * The PING control frame. + */ + spdylay_ping ping; + /** + * The GOAWAY control frame. + */ + spdylay_goaway goaway; + /** + * The HEADERS control frame. + */ + spdylay_headers headers; + /** + * The WINDOW_UPDATE control frame. + */ + spdylay_window_update window_update; + /** + * The DATA frame. + */ + spdylay_data data; +} spdylay_frame; + +/** + * @functypedef + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. It must return the number + * of bytes sent if it succeeds. If it cannot send any single byte + * without blocking, it must return + * :enum:`SPDYLAY_ERR_WOULDBLOCK`. For other errors, it must return + * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`. + */ +typedef ssize_t (*spdylay_send_callback) +(spdylay_session *session, + const uint8_t *data, size_t length, int flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. It must return + * the number of bytes written in |buf| if it succeeds. If it cannot + * read any single byte without blocking, it must return + * :enum:`SPDYLAY_ERR_WOULDBLOCK`. If it gets EOF before it reads any + * single byte, it must return :enum:`SPDYLAY_ERR_EOF`. For other + * errors, it must return :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`. + */ +typedef ssize_t (*spdylay_recv_callback) +(spdylay_session *session, + uint8_t *buf, size_t length, int flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `spdylay_session_recv()` when a + * control frame is received. + */ +typedef void (*spdylay_on_ctrl_recv_callback) +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `spdylay_session_recv()` when an + * invalid control frame is received. When this callback function is + * invoked, either RST_STREAM or GOAWAY will be sent. + */ +typedef void (*spdylay_on_invalid_ctrl_recv_callback) +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk is + * contained. ``(flags & SPDYLAY_DATA_FLAG_FIN) != 0`` does not + * necessarily mean this chunk of data is the last one in the + * stream. You should use :type:`spdylay_on_data_recv_callback` to + * know all data frames are received. + */ +typedef void (*spdylay_on_data_chunk_recv_callback) +(spdylay_session *session, uint8_t flags, int32_t stream_id, + const uint8_t *data, size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when DATA frame is received. The actual + * data it contains are received by + * :type:`spdylay_on_data_chunk_recv_callback`. + */ +typedef void (*spdylay_on_data_recv_callback) +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked before the control frame |frame| of type + * |type| is sent. This may be useful, for example, to know the stream + * ID of SYN_STREAM frame (see also + * `spdylay_session_get_stream_user_data()`), which is not assigned + * when it was queued. + */ +typedef void (*spdylay_before_ctrl_send_callback) +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the control frame |frame| of type + * |type| is sent. + */ +typedef void (*spdylay_on_ctrl_send_callback) +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the control frame |frame| of type + * |type| is not sent because of the error. The error is indicated by + * the |error|, which is one of the values defined in + * :type:`spdylay_error`. + */ +typedef void (*spdylay_on_ctrl_not_send_callback) +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + int error, void *user_data); + +/** + * @functypedef + * + * Callback function invoked after DATA frame is sent. + */ +typedef void (*spdylay_on_data_send_callback) +(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the stream |stream_id| is + * closed. The reason of closure is indicated by the + * |status_code|. The stream_user_data, which was specified in + * `spdylay_submit_request()` or `spdylay_submit_syn_stream()`, is + * still available in this function. + */ +typedef void (*spdylay_on_stream_close_callback) +(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the request from the remote peer is + * received. In other words, the frame with FIN flag set is received. + * In HTTP, this means HTTP request, including request body, is fully + * received. + */ +typedef void (*spdylay_on_request_recv_callback) +(spdylay_session *session, int32_t stream_id, void *user_data); + +/** + * @struct + * + * Callback functions. + */ +typedef struct { + /** + * Callback function invoked when the |session| wants to send data + * to the remote peer. + */ + spdylay_send_callback send_callback; + /** + * Callback function invoked when the |session| wants to receive + * data from the remote peer. + */ + spdylay_recv_callback recv_callback; + /** + * Callback function invoked by `spdylay_session_recv()` when a + * control frame is received. + */ + spdylay_on_ctrl_recv_callback on_ctrl_recv_callback; + /** + * Callback function invoked by `spdylay_session_recv()` when an + * invalid control frame is received. + */ + spdylay_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback; + /** + * Callback function invoked when a chunk of data in DATA frame is + * received. + */ + spdylay_on_data_chunk_recv_callback on_data_chunk_recv_callback; + /** + * Callback function invoked when DATA frame is received. + */ + spdylay_on_data_recv_callback on_data_recv_callback; + /** + * Callback function invoked before the control frame is sent. + */ + spdylay_before_ctrl_send_callback before_ctrl_send_callback; + /** + * Callback function invoked after the control frame is sent. + */ + spdylay_on_ctrl_send_callback on_ctrl_send_callback; + /** + * The callback function invoked when a control frame is not sent + * because of an error. + */ + spdylay_on_ctrl_not_send_callback on_ctrl_not_send_callback; + /** + * Callback function invoked after DATA frame is sent. + */ + spdylay_on_data_send_callback on_data_send_callback; + /** + * Callback function invoked when the stream is closed. + */ + spdylay_on_stream_close_callback on_stream_close_callback; + /** + * Callback function invoked when request from the remote peer is + * received. + */ + spdylay_on_request_recv_callback on_request_recv_callback; +} spdylay_session_callbacks; + +/** + * @function + * + * Initializes |*session_ptr| for client use, using the protocol + * version |version|. The all members of |callbacks| are copied to + * |*session_ptr|. Therefore |*session_ptr| does not store + * |callbacks|. |user_data| is an arbitrary user supplied data, which + * will be passed to the callback functions. + * + * Some of the members of |callbacks| can be ``NULL``, but + * :member:`spdylay_session_callbacks.send_callback` and + * :member:`spdylay_session_callbacks.recv_callback` must be + * specified. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + * :enum:`SPDYLAY_ERR_ZLIB` + * The z_stream initialization failed. + * :enum:`SPDYLAY_ERR_UNSUPPORTED_VERSION` + * The version is not supported. + */ +int spdylay_session_client_new(spdylay_session **session_ptr, + uint16_t version, + const spdylay_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use, using the protocol + * version |version|. The all members of |callbacks| are copied to + * |*session_ptr|. Therefore |*session_ptr| does not store + * |callbacks|. |user_data| is an arbitrary user supplied data, which + * will be passed to the callback functions. + * + * Some of the members of |callbacks| can be ``NULL``, but + * :member:`spdylay_session_callbacks.send_callback` and + * :member:`spdylay_session_callbacks.recv_callback` must be + * specified. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + * :enum:`SPDYLAY_ERR_ZLIB` + * The z_stream initialization failed. + * :enum:`SPDYLAY_ERR_UNSUPPORTED_VERSION` + * The version is not supported. + */ +int spdylay_session_server_new(spdylay_session **session_ptr, + uint16_t version, + const spdylay_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +void spdylay_session_del(spdylay_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many as possible until the user callback + * :member:`spdylay_session_callbacks.send_callback` returns + * :enum:`SPDYLAY_ERR_WOULDBLOCK` or the outbound queue becomes empty. + * This function calls several callback functions which are passed + * when initializing the |session|. Here is the simple time chart + * which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * 2. Prepare transmission of the frame. + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., SYN_STREAM cannot be sent after GOAWAY), + * :member:`spdylay_session_callbacks.on_ctrl_not_send_callback` is + * invoked. Abort the following steps. + * 4. If the frame is SYN_STREAM, the stream is opened here. + * 5. :member:`spdylay_session_callbacks.before_ctrl_send_callback` is + * invoked. + * 6. :member:`spdylay_session_callbacks.send_callback` is invoked one + * or more times to send the frame. + * 7. If the frame is a control frame, + * :member:`spdylay_session_callbacks.on_ctrl_send_callback` is + * invoked. + * 8. If the frame is a DATA frame, + * :member:`spdylay_session_callbacks.on_data_send_callback` is + * invoked. + * 9. If the transmission of the frame triggers closure of the stream, + * the stream is closed and + * :member:`spdylay_session_callbacks.on_stream_close_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +int spdylay_session_send(spdylay_session *session); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :member:`spdylay_session_callbacks.recv_callback` returns + * :enum:`SPDYLAY_ERR_WOULDBLOCK`. This function calls several + * callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :member:`spdylay_session_callbacks.recv_callback` is invoked one + * or more times to receive frame header. + * 2. If the frame is DATA frame: + * + * 2.1. :member:`spdylay_session_callbacks.recv_callback` is invoked + * to receive DATA payload. For each chunk of data, + * :member:`spdylay_session_callbacks.on_data_chunk_recv_callback` + * is invoked. + * 2.2. If one DATA frame is completely received, + * :member:`spdylay_session_callbacks.on_data_recv_callback` is + * invoked. If the frame is the final frame of the request, + * :member:`spdylay_session_callbacks.on_request_recv_callback` + * is invoked. If the reception of the frame triggers the + * closure of the stream, + * :member:`spdylay_session_callbacks.on_stream_close_callback` + * is invoked. + * + * 3. If the frame is the control frame: + * + * 3.1. :member:`spdylay_session_callbacks.recv_callback` is invoked + * one or more times to receive whole frame. + * 3.2. If the received frame is valid, + * :member:`spdylay_session_callbacks.on_ctrl_recv_callback` is + * invoked. If the frame is the final frame of the request, + * :member:`spdylay_session_callbacks.on_request_recv_callback` + * is invoked. If the reception of the frame triggers the + * closure of the stream, + * :member:`spdylay_session_callbacks.on_stream_close_callback` + * is invoked. + * 3.3. If the received frame is unpacked but is interpreted as + * invalid, + * :member:`spdylay_session_callbacks.on_invalid_ctrl_recv_callback` + * is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + * :enum:`SPDYLAY_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +int spdylay_session_recv(spdylay_session *session); + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes in the |in|. + * + * This function behaves like `spdylay_session_recv()` except that it + * does not use :member:`spdylay_session_callbacks.recv_callback` to + * receive data; the |in| is the only data for the invocation of this + * function. If all bytes are processed, this function returns. The + * other callbacks are called in the same way as they are in + * `spdylay_session_recv()`. + * + * In the current implementation, this function always tries to + * processes all input data unless an error occurs. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +ssize_t spdylay_session_mem_recv(spdylay_session *session, + const uint8_t *in, size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT` + * The stream does not exist or no deferred data exist. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_session_resume_data(spdylay_session *session, int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `spdylay_session_want_read()` and + * `spdylay_session_want_write()` return 0, the application should + * drop the connection. + */ +int spdylay_session_want_read(spdylay_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `spdylay_session_want_read()` and + * `spdylay_session_want_write()` return 0, the application should + * drop the connection. + */ +int spdylay_session_want_write(spdylay_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `spdylay_submit_request()` or + * `spdylay_submit_syn_stream()`. If the stream is initiated by the + * remote endpoint, stream_user_data is always ``NULL``. If the stream + * is initiated by the local endpoint and ``NULL`` is given in + * `spdylay_submit_request()` or `spdylay_submit_syn_stream()`, then + * this function returns ``NULL``. If the stream does not exist, this + * function returns ``NULL``. + */ +void* spdylay_session_get_stream_user_data(spdylay_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +size_t spdylay_session_get_outbound_queue_size(spdylay_session *session); + +/** + * @function + * + * Submits SYN_STREAM frame and optionally one or more DATA + * frames. + * + * The |pri| is priority of this request. 0 is the highest priority + * value. If the |session| is initialized with the version + * :macro:`SPDYLAY_PROTO_SPDY2`, the lowest priority value is + * :macro:`SPDYLAY_SPDY2_PRI_LOWEST`. If the |session| is initialized + * with the version :macro:`SPDYLAY_PROTO_SPDY3`, the lowest priority + * value is :macro:`SPDYLAY_SPDY3_PRI_LOWEST`. + * + * The |nv| contains the name/value pairs. For i > 0, ``nv[2*i]`` + * contains a pointer to the name string and ``nv[2*i+1]`` contains a + * pointer to the value string. The one beyond last value must be + * ``NULL``. That is, if the |nv| contains N name/value pairs, + * ``nv[2*N]`` must be ``NULL``. + * + * The |nv| must include following name/value pairs: + * + * ":method" + * HTTP method (e.g., "GET", "POST", "HEAD", etc) + * ":scheme" + * URI scheme (e.g., "https") + * ":path" + * Absolute path and parameters of this request (e.g., "/foo", + * "/foo;bar;haz?h=j&y=123") + * ":version" + * HTTP version (e.g., "HTTP/1.1") + * ":host" + * The hostport portion of the URI for this request (e.g., + * "example.org:443"). This is the same as the HTTP "Host" header + * field. + * + * If the |session| is initialized with the version + * :macro:`SPDYLAY_PROTO_SPDY2`, the above names are translated to + * "method", "scheme", "url", "version" and "host" respectively. + * + * This function creates copies of all name/value pairs in |nv|. It + * also lower-cases all names in |nv|. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must + * be specified with "method" key in |nv| (e.g. POST). If |data_prd| + * is ``NULL``, SYN_STREAM have FLAG_FIN set. The |stream_user_data| + * is data associated to the stream opened by this request and can be + * an arbitrary pointer, which can be retrieved later by + * `spdylay_session_get_stream_user_data()`. + * + * Since the library reorders the frames and tries to send the highest + * prioritized one first and the SPDY specification requires the + * stream ID must be strictly increasing, the stream ID of this + * request cannot be known until it is about to sent. To know the + * stream ID of the request, the application can use + * :member:`spdylay_session_callbacks.before_ctrl_send_callback`. This + * callback is called just before the frame is sent. For SYN_STREAM + * frame, the argument frame has the stream ID assigned. Also since + * the stream is already opened, + * `spdylay_session_get_stream_user_data()` can be used to get + * |stream_user_data| to identify which SYN_STREAM we are processing. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT` + * The |pri| is invalid; or the Associated-To-Stream-ID is + * invalid. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_request(spdylay_session *session, uint8_t pri, + const char **nv, + const spdylay_data_provider *data_prd, + void *stream_user_data); + +/** + * @function + * + * Submits SYN_REPLY frame and optionally one or more DATA frames + * against the stream |stream_id|. + * + * The |nv| contains the name/value pairs. For i > 0, ``nv[2*i]`` + * contains a pointer to the name string and ``nv[2*i+1]`` contains a + * pointer to the value string. The one beyond last value must be + * ``NULL``. That is, if the |nv| contains N name/value pairs, + * ``nv[2*N]`` must be ``NULL``. + * + * The |nv| must include following name/value pairs: + * + * ":status" + * HTTP status code (e.g., "200" or "200 OK") + * ":version" + * HTTP response version (e.g., "HTTP/1.1") + * + * If the |session| is initialized with the version + * :macro:`SPDYLAY_PROTO_SPDY2`, the above names are translated to + * "status" and "version" respectively. + * + * This function creates copies of all name/value pairs in |nv|. It + * also lower-cases all names in |nv|. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. If |data_prd| is ``NULL``, SYN_REPLY + * will have FLAG_FIN set. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_response(spdylay_session *session, + int32_t stream_id, const char **nv, + const spdylay_data_provider *data_prd); + +/** + * @function + * + * Submits SYN_STREAM frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`SPDYLAY_CTRL_FLAG_FIN` + * * :enum:`SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL` + * + * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has + * FLAG_FIN flag set. + * + * The |assoc_stream_id| is used for server-push. If |session| is + * initialized for client use, |assoc_stream_id| is ignored. + + * The |pri| is priority of this request. 0 is the highest priority + * value. If the |session| is initialized with the version + * :macro:`SPDYLAY_PROTO_SPDY2`, the lowest priority value is + * :macro:`SPDYLAY_SPDY2_PRI_LOWEST`. If the |session| is initialized + * with the version :macro:`SPDYLAY_PROTO_SPDY3`, the lowest priority + * value is :macro:`SPDYLAY_SPDY3_PRI_LOWEST`. + * + * The |nv| contains the name/value pairs. For i > 0, ``nv[2*i]`` + * contains a pointer to the name string and ``nv[2*i+1]`` contains a + * pointer to the value string. The one beyond last value must be + * ``NULL``. That is, if the |nv| contains N name/value pairs, + * ``nv[2*N]`` must be ``NULL``. + * + * The |stream_user_data| is a pointer to an arbitrary + * data which is associated to the stream this frame will open. + * + * This function is low-level in a sense that the application code can + * specify flags and the Associated-To-Stream-ID directly. For usual + * HTTP request, `spdylay_submit_request()` is useful. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT` + * The |pri| is invalid; or the Associated-To-Stream-ID is + * invalid. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags, + int32_t assoc_stream_id, uint8_t pri, + const char **nv, void *stream_user_data); + +/** + * @function + * + * Submits SYN_REPLY frame. The |flags| is bitwise OR of the following + * values: + * + * * :enum:`SPDYLAY_CTRL_FLAG_FIN` + * + * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has + * FLAG_FIN flag set. + * + * The stream which this frame belongs to is given in the + * |stream_id|. The |nv| is the name/value pairs in this frame. + * + * The |nv| contains the name/value pairs. For i > 0, ``nv[2*i]`` + * contains a pointer to the name string and ``nv[2*i+1]`` contains a + * pointer to the value string. The one beyond last value must be + * ``NULL``. That is, if the |nv| contains N name/value pairs, + * ``nv[2*N]`` must be ``NULL``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags, + int32_t stream_id, const char **nv); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the following + * values: + * + * * :enum:`SPDYLAY_CTRL_FLAG_FIN` + * + * If |flags| includes :enum:`SPDYLAY_CTRL_FLAG_FIN`, this frame has + * FLAG_FIN flag set. + * + * The stream which this frame belongs to is given in the + * |stream_id|. The |nv| is the name/value pairs in this frame. + * + * The |nv| contains the name/value pairs. For i > 0, ``nv[2*i]`` + * contains a pointer to the name string and ``nv[2*i+1]`` contains a + * pointer to the value string. The one beyond last value must be + * ``NULL``. That is, if the |nv| contains N name/value pairs, + * ``nv[2*N]`` must be ``NULL``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_headers(spdylay_session *session, uint8_t flags, + int32_t stream_id, const char **nv); + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`SPDYLAY_DATA_FLAG_FIN`, the last DATA frame has FLAG_FIN + * set. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_data(spdylay_session *session, int32_t stream_id, + uint8_t flags, const spdylay_data_provider *data_prd); + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the status code |status_code|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id, + uint32_t status_code); + +/** + * @function + * + * Submits PING frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_ping(spdylay_session *session); + +/** + * @function + * + * Submits GOAWAY frame. The status code |status_code| is ignored if + * the protocol version is :macro:`SPDYLAY_PROTO_SPDY2`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_goaway(spdylay_session *session, uint32_t status_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`spdylay_settings_entry`. The |niv| + * indicates the number of :type:`spdylay_settings_entry`. The |flags| + * is bitwise-OR of one or more values from + * :type:`spdylay_settings_flag`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. + */ +int spdylay_submit_settings(spdylay_session *session, uint8_t flags, + const spdylay_settings_entry *iv, size_t niv); + +/** + * @function + * + * A helper function for dealing with NPN in client side. The |in| + * contains server's protocol in preferable order. The format of |in| + * is length-prefixed and not null-terminated. For example, "spdy/2" + * are "http/1.1" stored in |in| like this:: + * + * in[0] = 6 + * in[1..6] = "spdy/2" + * in[7] = 8 + * in[8..15] = "http/1.1" + * inlen = 16 + * + * The selection algorithm is as follows: + * + * 1. If server's list contains SPDY versions the spdylay library + * supports, this function selects one of them and returns its SPDY + * protocol version which can be used directly with + * `spdylay_session_client_new()` and + * `spdylay_session_server_new()` . The following steps are not + * taken. + * + * 2. If server's list contains "http/1.1", this function selects + * "http/1.1" and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1. (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * When spdylay supports updated version of SPDY in the future, this + * function may select updated protocol and application code which + * relies on spdylay for SPDY stuff needs not be modified. + * + * Selecting "spdy/2" means that "spdy/2" is written into |*out| and + * length of "spdy/2" (which is 6) is assigned to |*outlen|. + * + * See http://technotes.googlecode.com/git/nextprotoneg.html for more + * details about NPN. + * + * To use this method you should do something like:: + * + * static int select_next_proto_cb(SSL* ssl, + * unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int version; + * version = spdylay_select_next_protocol(out, outlen, in, inlen); + * if(version > 0) { + * ((MyType*)arg)->spdy_version = version; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + */ +int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen); + +/** + * @function + * + * Returns spdy version which spdylay library supports from the given + * protocol name. The |proto| is the pointer to the protocol name and + * |protolen| is its length. Currently, "spdy/2" and "spdy/3" are + * supported. + * + * This function returns nonzero spdy version if it succeeds, or 0. + */ +uint16_t spdylay_npn_get_version(const unsigned char *proto, size_t protolen); + +#ifdef __cplusplus +} +#endif + +#endif /* SPDYLAY_H */ diff --git a/deps/spdylay/lib/includes/spdylay/spdylayver.h.in b/deps/spdylay/lib/includes/spdylay/spdylayver.h.in new file mode 100644 index 0000000..fdd029e --- /dev/null +++ b/deps/spdylay/lib/includes/spdylay/spdylayver.h.in @@ -0,0 +1,34 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAYVER_H +#define SPDYLAYVER_H + +/** + * @macro + * Version number of the Spdylay library release + */ +#define SPDYLAY_VERSION "@PACKAGE_VERSION@" + +#endif /* SPDYLAYVER_H */ diff --git a/deps/spdylay/lib/libspdylay.pc.in b/deps/spdylay/lib/libspdylay.pc.in new file mode 100644 index 0000000..5c61321 --- /dev/null +++ b/deps/spdylay/lib/libspdylay.pc.in @@ -0,0 +1,33 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Spdylay +Description: SPDY library +URL: http://spdylay.sourceforge.net/ +Version: @VERSION@ +Libs: -L${libdir} -lspdylay +Cflags: -I${includedir} diff --git a/deps/spdylay/lib/spdylay_buffer.c b/deps/spdylay/lib/spdylay_buffer.c new file mode 100644 index 0000000..eb5dbf6 --- /dev/null +++ b/deps/spdylay/lib/spdylay_buffer.c @@ -0,0 +1,132 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_buffer.h" + +#include +#include + +void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity) +{ + buffer->root.data = NULL; + buffer->root.next = NULL; + buffer->current = &buffer->root; + buffer->capacity = chunk_capacity; + buffer->len = 0; + /* + * Set last_offset to maximum so that first append adds new buffer + * buffer. + */ + buffer->last_offset = buffer->capacity; +} + +void spdylay_buffer_free(spdylay_buffer *buffer) +{ + spdylay_buffer_chunk *p = buffer->root.next; + while(p) { + spdylay_buffer_chunk *next = p->next; + free(p->data); + free(p); + p = next; + } +} + +int spdylay_buffer_alloc(spdylay_buffer *buffer) +{ + if(buffer->current->next == NULL) { + spdylay_buffer_chunk *chunk; + uint8_t *buf; + chunk = malloc(sizeof(spdylay_buffer_chunk)); + if(chunk == NULL) { + return SPDYLAY_ERR_NOMEM; + } + buf = malloc(buffer->capacity); + if(buf == NULL) { + free(chunk); + return SPDYLAY_ERR_NOMEM; + } + chunk->data = buf; + chunk->next = NULL; + buffer->current->next = chunk; + buffer->current = chunk; + } else { + buffer->current = buffer->current->next; + } + buffer->len += buffer->capacity-buffer->last_offset; + buffer->last_offset = 0; + return 0; +} + +uint8_t* spdylay_buffer_get(spdylay_buffer *buffer) +{ + if(buffer->current->data == NULL) { + return NULL; + } else { + return buffer->current->data+buffer->last_offset; + } +} + +size_t spdylay_buffer_avail(spdylay_buffer *buffer) +{ + return buffer->capacity-buffer->last_offset; +} + +void spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount) +{ + buffer->last_offset += amount; + buffer->len += amount; + assert(buffer->last_offset <= buffer->capacity); +} + +size_t spdylay_buffer_length(spdylay_buffer *buffer) +{ + return buffer->len; +} + +size_t spdylay_buffer_capacity(spdylay_buffer *buffer) +{ + return buffer->capacity; +} + +void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf) +{ + spdylay_buffer_chunk *p = buffer->root.next; + for(; p; p = p->next) { + size_t len; + if(p == buffer->current) { + len = buffer->last_offset; + } else { + len = buffer->capacity; + } + memcpy(buf, p->data, len); + buf += len; + } +} + +void spdylay_buffer_reset(spdylay_buffer *buffer) +{ + buffer->current = &buffer->root; + buffer->len = 0; + buffer->last_offset = buffer->capacity; +} diff --git a/deps/spdylay/lib/spdylay_buffer.h b/deps/spdylay/lib/spdylay_buffer.h new file mode 100644 index 0000000..980e1d5 --- /dev/null +++ b/deps/spdylay/lib/spdylay_buffer.h @@ -0,0 +1,97 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_BUFFER_H +#define SPDYLAY_BUFFER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct spdylay_buffer_chunk { + uint8_t *data; + struct spdylay_buffer_chunk *next; +} spdylay_buffer_chunk; + +/* + * List of fixed sized chunks + */ +typedef struct { + /* Capacity of each chunk buffer */ + size_t capacity; + /* Root of list of chunk buffers. The root is dummy and its data + member is always NULL. */ + spdylay_buffer_chunk root; + /* Points to the current chunk to write */ + spdylay_buffer_chunk *current; + /* Total length of this buffer */ + size_t len; + /* Offset of last chunk buffer */ + size_t last_offset; +} spdylay_buffer; + +/* + * Initializes buffer with fixed chunk size chunk_capacity. + */ +void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity); +/* Releases allocated memory for buffer */ +void spdylay_buffer_free(spdylay_buffer *buffer); +/* Returns buffer pointer */ +uint8_t* spdylay_buffer_get(spdylay_buffer *buffer); +/* Returns available buffer length */ +size_t spdylay_buffer_avail(spdylay_buffer *buffer); +/* Advances buffer pointer by amount. This reduces available buffer + length. */ +void spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount); +/* + * Allocate new chunk buffer. This will increase total length of + * buffer (returned by spdylay_buffer_length) by capacity-last_offset. + * It means untouched buffer is assumued to be written. + * + * This function returns 0 if it succeeds, or one of the following + * negative eror codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_buffer_alloc(spdylay_buffer *buffer); + +/* Returns total length of buffer */ +size_t spdylay_buffer_length(spdylay_buffer *buffer); + +/* Returns capacity of each fixed chunk buffer */ +size_t spdylay_buffer_capacity(spdylay_buffer *buffer); + +/* Stores the contents of buffer into |buf|. |buf| must be at least + spdylay_buffer_length(buffer) bytes long. */ +void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf); + +/* Reset |buffer| for reuse. Set the total length of buffer to 0. + Next spdylay_buffer_avail() returns 0. This function does not free + allocated memory space; they are reused. */ +void spdylay_buffer_reset(spdylay_buffer *buffer); + +#endif /* SPDYLAY_BUFFER_H */ diff --git a/deps/spdylay/lib/spdylay_frame.c b/deps/spdylay/lib/spdylay_frame.c new file mode 100644 index 0000000..8331f31 --- /dev/null +++ b/deps/spdylay/lib/spdylay_frame.c @@ -0,0 +1,1013 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_frame.h" + +#include +#include +#include +#include + +#include "spdylay_helper.h" + +#define spdylay_frame_get_nv_len(IN, LEN_SIZE) \ + (LEN_SIZE == 2 ? spdylay_get_uint16(IN) : spdylay_get_uint32(IN)) + +#define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE) \ + (LEN_SIZE == 2 ? \ + spdylay_put_uint16be(OUT, VAL) : spdylay_put_uint32be(OUT, VAL)) + +/* Returns the number of bytes in length of name/value pair for the + given protocol version |version|. If |version| is not supported, + returns 0. */ +static size_t spdylay_frame_get_len_size(uint16_t version) +{ + if(SPDYLAY_PROTO_SPDY2 == version) { + return 2; + } else if(SPDYLAY_PROTO_SPDY3 == version) { + return 4; + } else { + /* Unsupported version */ + return 0; + } +} + +static uint8_t spdylay_unpack_pri(const uint8_t *data) +{ + return (data[0] >> 6) & 0x3; +} + +static uint8_t* spdylay_pack_str(uint8_t *buf, const char *str, size_t len, + size_t len_size) +{ + spdylay_frame_put_nv_len(buf, len, len_size); + buf += len_size; + memcpy(buf, str, len); + return buf+len; +} + +static void spdylay_frame_pack_ctrl_hd(uint8_t* buf, const spdylay_ctrl_hd *hd) +{ + spdylay_put_uint16be(&buf[0], hd->version); + buf[0] |= 1 << 7; + spdylay_put_uint16be(&buf[2], hd->type); + spdylay_put_uint32be(&buf[4], hd->length); + buf[4] = hd->flags; +} + +static void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd, + const uint8_t* buf) +{ + hd->version = spdylay_get_uint16(buf) & SPDYLAY_VERSION_MASK; + hd->type = spdylay_get_uint16(&buf[2]); + hd->flags = buf[4]; + hd->length = spdylay_get_uint32(&buf[4]) & SPDYLAY_LENGTH_MASK; +} + +ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + char **nv, size_t nv_offset, + size_t len_size, + spdylay_zlib *deflater) +{ + size_t nvspace; + size_t maxframelen; + ssize_t framelen; + int r; + nvspace = spdylay_frame_count_nv_space(nv, len_size); + r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace); + if(r != 0) { + return SPDYLAY_ERR_NOMEM; + } + maxframelen = nv_offset+spdylay_zlib_deflate_hd_bound(deflater, nvspace); + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, maxframelen); + if(r != 0) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_pack_nv(*nvbuf_ptr, nv, len_size); + framelen = spdylay_zlib_deflate_hd(deflater, + (*buf_ptr)+nv_offset, + maxframelen-nv_offset, + *nvbuf_ptr, nvspace); + if(framelen < 0) { + return framelen; + } + framelen += nv_offset; + return framelen; +} + +int spdylay_frame_count_unpack_nv_space +(size_t *nvlen_ptr, size_t *buflen_ptr, const uint8_t *in, size_t inlen, + size_t len_size) +{ + uint32_t n; + size_t buflen = 0; + size_t nvlen = 0; + size_t off = 0; + int i; + if(inlen < len_size) { + return SPDYLAY_ERR_INVALID_FRAME; + } + /* TODO limit n in a reasonable number */ + n = spdylay_frame_get_nv_len(in, len_size); + off += len_size; + for(i = 0; i < n; ++i) { + uint32_t len; + int j; + for(j = 0; j < 2; ++j) { + if(inlen-off < len_size) { + return SPDYLAY_ERR_INVALID_FRAME; + } + len = spdylay_frame_get_nv_len(in+off, len_size); + off += len_size; + if(inlen-off < len) { + return SPDYLAY_ERR_INVALID_FRAME; + } + buflen += len+1; + off += len; + } + for(j = off, off -= len; off != j; ++off) { + if(in[off] == '\0') { + ++nvlen; + } + } + ++nvlen; + } + if(inlen == off) { + *nvlen_ptr = nvlen; + *buflen_ptr = buflen+(nvlen*2+1)*sizeof(char*); + return 0; + } else { + return SPDYLAY_ERR_INVALID_FRAME; + } +} + +int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, + size_t len_size) +{ + size_t nvlen, buflen; + int r, i; + char *buf, **index, *data; + uint32_t n; + r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, inlen, len_size); + if(r != 0) { + return r; + } + buf = malloc(buflen); + if(buf == NULL) { + return SPDYLAY_ERR_NOMEM; + } + index = (char**)buf; + data = buf+(nvlen*2+1)*sizeof(char*); + n = spdylay_frame_get_nv_len(in, len_size); + in += len_size; + for(i = 0; i < n; ++i) { + uint32_t len; + char *name, *val; + char *stop; + len = spdylay_frame_get_nv_len(in, len_size); + in += len_size; + name = data; + memcpy(data, in, len); + data += len; + *data = '\0'; + ++data; + in += len; + + len = spdylay_frame_get_nv_len(in, len_size); + in += len_size; + val = data; + memcpy(data, in, len); + + for(stop = data+len; data != stop; ++data) { + if(*data == '\0') { + *index++ = name; + *index++ = val; + val = data+1; + } + } + *data = '\0'; + ++data; + in += len; + + *index++ = name; + *index++ = val; + } + *index = NULL; + assert((char*)index-buf == (nvlen*2)*sizeof(char*)); + *nv_ptr = (char**)buf; + return 0; +} + +int spdylay_frame_alloc_unpack_nv(char ***nv_ptr, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *in, size_t inlen, + size_t len_size, + spdylay_zlib *inflater) +{ + ssize_t nvspace; + int r; + nvspace = spdylay_zlib_inflate_hd(inflater, inflatebuf, in, inlen); + if(nvspace < 0) { + return nvspace; + } else { + r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace); + if(r != 0) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_buffer_serialize(inflatebuf, *nvbuf_ptr); + r = spdylay_frame_unpack_nv(nv_ptr, *nvbuf_ptr, nvspace, len_size); + return r; + } +} + +size_t spdylay_frame_count_nv_space(char **nv, size_t len_size) +{ + size_t sum = len_size; + int i; + const char *prev = ""; + size_t prevlen = 0; + for(i = 0; nv[i]; i += 2) { + const char *key = nv[i]; + const char *val = nv[i+1]; + size_t keylen = strlen(key); + size_t vallen = strlen(val); + if(prevlen == keylen && memcmp(prev, key, keylen) == 0) { + /* Join previous value, with NULL character */ + sum += vallen+1; + } else { + prev = key; + prevlen = keylen; + /* SPDY NV header does not include terminating NULL byte */ + sum += keylen+vallen+len_size*2; + } + } + return sum; +} + +ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size) +{ + int i; + uint8_t *bufp = buf+len_size; + uint32_t num_nv = 0; + /* TODO Join values with same keys, using '\0' as a delimiter */ + const char *prev = ""; + uint8_t *prev_vallen_buf = NULL; + uint32_t prev_vallen = 0; + for(i = 0; nv[i]; i += 2) { + const char *key = nv[i]; + const char *val = nv[i+1]; + size_t keylen = strlen(key); + size_t vallen = strlen(val); + if(strcmp(prev, key) == 0) { + prev_vallen += vallen+1; + spdylay_frame_put_nv_len(prev_vallen_buf, prev_vallen, len_size); + *bufp = '\0'; + ++bufp; + memcpy(bufp, val, vallen); + bufp += vallen; + } else { + ++num_nv; + bufp = spdylay_pack_str(bufp, key, keylen, len_size); + prev = key; + prev_vallen_buf = bufp; + prev_vallen = vallen; + bufp = spdylay_pack_str(bufp, val, vallen, len_size); + } + } + spdylay_frame_put_nv_len(buf, num_nv, len_size); + return bufp-buf; +} + +int spdylay_frame_is_ctrl_frame(uint8_t first_byte) +{ + return first_byte & 0x80; +} + +void spdylay_frame_nv_del(char **nv) +{ + free(nv); +} + +char** spdylay_frame_nv_copy(const char **nv) +{ + int i; + char *buf; + char **index, *data; + size_t buflen = 0; + for(i = 0; nv[i]; ++i) { + buflen += strlen(nv[i])+1; + } + buflen += (i+1)*sizeof(char*); + buf = malloc(buflen); + if(buf == NULL) { + return NULL; + } + index = (char**)buf; + data = buf+(i+1)*sizeof(char*); + + for(i = 0; nv[i]; ++i) { + size_t len = strlen(nv[i])+1; + memcpy(data, nv[i], len); + *index++ = data; + data += len; + } + *index = NULL; + return (char**)buf; +} + +static int spdylay_string_compar(const void *lhs, const void *rhs) +{ + return strcmp(*(char **)lhs, *(char **)rhs); +} + +void spdylay_frame_nv_sort(char **nv) +{ + int n; + for(n = 0; nv[n]; ++n); + qsort(nv, n/2, 2*sizeof(char*), spdylay_string_compar); +} + +void spdylay_frame_nv_downcase(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; nv[i][j] != '\0'; ++j) { + if('A' <= nv[i][j] && nv[i][j] <= 'Z') { + nv[i][j] += 'a'-'A'; + } + } + } +} + +char** spdylay_frame_nv_norm_copy(const char **nv) +{ + char **nv_copy; + nv_copy = spdylay_frame_nv_copy(nv); + if(nv_copy != NULL) { + spdylay_frame_nv_downcase(nv_copy); + spdylay_frame_nv_sort(nv_copy); + } + return nv_copy; +} + +/* Table to translate SPDY/3 header names to SPDY/2. */ +static char *spdylay_nv_3to2[] = { + ":host", "host", + ":method", "method", + ":path", "url", + ":scheme", "scheme", + ":status", "status", + ":version", "version", + NULL +}; + +void spdylay_frame_nv_3to2(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; spdylay_nv_3to2[j]; j += 2) { + if(strcmp(nv[i], spdylay_nv_3to2[j]) == 0) { + nv[i] = spdylay_nv_3to2[j+1]; + break; + } + } + } +} + +void spdylay_frame_nv_2to3(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; spdylay_nv_3to2[j]; j += 2) { + if(strcmp(nv[i], spdylay_nv_3to2[j+1]) == 0) { + nv[i] = spdylay_nv_3to2[j]; + break; + } + } + } +} + +void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, + uint16_t version, uint8_t flags, + int32_t stream_id, int32_t assoc_stream_id, + uint8_t pri, char **nv) +{ + memset(frame, 0, sizeof(spdylay_syn_stream)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_SYN_STREAM; + frame->hd.flags = flags; + frame->stream_id = stream_id; + frame->assoc_stream_id = assoc_stream_id; + frame->pri = pri; + frame->nv = nv; +} + +void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame) +{ + spdylay_frame_nv_del(frame->nv); +} + +void spdylay_frame_syn_reply_init(spdylay_syn_reply *frame, + uint16_t version, uint8_t flags, + int32_t stream_id, char **nv) +{ + memset(frame, 0, sizeof(spdylay_syn_reply)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_SYN_REPLY; + frame->hd.flags = flags; + frame->stream_id = stream_id; + frame->nv = nv; +} + +void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame) +{ + spdylay_frame_nv_del(frame->nv); +} + +void spdylay_frame_ping_init(spdylay_ping *frame, + uint16_t version, uint32_t unique_id) +{ + memset(frame, 0, sizeof(spdylay_ping)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_PING; + frame->hd.flags = SPDYLAY_CTRL_FLAG_NONE; + frame->hd.length = 4; + frame->unique_id = unique_id; +} + +void spdylay_frame_ping_free(spdylay_ping *frame) +{} + +void spdylay_frame_goaway_init(spdylay_goaway *frame, + uint16_t version, int32_t last_good_stream_id, + uint32_t status_code) +{ + memset(frame, 0, sizeof(spdylay_goaway)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_GOAWAY; + if(version == SPDYLAY_PROTO_SPDY2) { + frame->hd.length = 4; + } else if(version == SPDYLAY_PROTO_SPDY3) { + frame->hd.length = 8; + frame->status_code = status_code; + } else { + frame->hd.length = 0; + } + frame->last_good_stream_id = last_good_stream_id; +} + +void spdylay_frame_goaway_free(spdylay_goaway *frame) +{} + +void spdylay_frame_headers_init(spdylay_headers *frame, + uint16_t version, uint8_t flags, + int32_t stream_id, char **nv) +{ + memset(frame, 0, sizeof(spdylay_headers)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_HEADERS; + frame->hd.flags = flags; + frame->stream_id = stream_id; + frame->nv = nv; +} + +void spdylay_frame_headers_free(spdylay_headers *frame) +{ + spdylay_frame_nv_del(frame->nv); +} + +void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame, + uint16_t version, + int32_t stream_id, uint32_t status_code) +{ + memset(frame, 0, sizeof(spdylay_rst_stream)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_RST_STREAM; + frame->hd.flags = 0; + frame->hd.length = 8; + frame->stream_id = stream_id; + frame->status_code = status_code; +} + +void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame) +{} + +void spdylay_frame_window_update_init(spdylay_window_update *frame, + uint16_t version, + int32_t stream_id, + int32_t delta_window_size) +{ + memset(frame, 0, sizeof(spdylay_window_update)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_WINDOW_UPDATE; + frame->hd.flags = 0; + frame->hd.length = 8; + frame->stream_id = stream_id; + frame->delta_window_size = delta_window_size; +} + +void spdylay_frame_window_update_free(spdylay_window_update *frame) +{} + +void spdylay_frame_settings_init(spdylay_settings *frame, + uint16_t version, uint8_t flags, + spdylay_settings_entry *iv, size_t niv) +{ + memset(frame, 0, sizeof(spdylay_settings)); + frame->hd.version = version; + frame->hd.type = SPDYLAY_SETTINGS; + frame->hd.flags = flags; + frame->hd.length = 4+niv*8; + frame->niv = niv; + frame->iv = iv; +} + +void spdylay_frame_settings_free(spdylay_settings *frame) +{ + free(frame->iv); +} + +void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, + uint8_t flags, + const spdylay_data_provider *data_prd) +{ + memset(frame, 0, sizeof(spdylay_data)); + frame->stream_id = stream_id; + frame->flags = flags; + frame->data_prd = *data_prd; +} + +void spdylay_frame_data_free(spdylay_data *frame) +{} + +#define SPDYLAY_SYN_STREAM_NV_OFFSET 18 + +ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + spdylay_syn_stream *frame, + spdylay_zlib *deflater) +{ + ssize_t framelen; + size_t len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, + SPDYLAY_SYN_STREAM_NV_OFFSET, + len_size, + deflater); + if(framelen < 0) { + return framelen; + } + frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; + memset(*buf_ptr, 0, SPDYLAY_SYN_STREAM_NV_OFFSET); + /* pack ctrl header after length is determined */ + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[12], frame->assoc_stream_id); + (*buf_ptr)[16] = (frame->pri << 6); + if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + (*buf_ptr)[17] = frame->slot; + } + return framelen; +} + +int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater) +{ + int r; + size_t len_size; + if(payloadlen < 12) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; + frame->assoc_stream_id = + spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK; + frame->pri = spdylay_unpack_pri(payload+8); + if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + frame->slot = payload[9]; + } else { + frame->slot = 0; + } + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+10, payloadlen-10, + len_size, + inflater); + return r; +} + +#define SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET 14 +#define SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET 12 + +ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + spdylay_syn_reply *frame, + spdylay_zlib *deflater) +{ + ssize_t framelen; + size_t len_size; + size_t nv_offset; + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? + SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET : SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET; + + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, nv_offset, + len_size, deflater); + if(framelen < 0) { + return framelen; + } + frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; + memset(*buf_ptr, 0, nv_offset); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + return framelen; +} + +int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater) +{ + int r; + size_t len_size; + size_t nv_offset; + if(payloadlen < 8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4; + frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+nv_offset, payloadlen-nv_offset, + len_size, inflater); + return r; +} + +ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_ping *frame) +{ + ssize_t framelen = 12; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->unique_id); + return framelen; +} + +int spdylay_frame_unpack_ping(spdylay_ping *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + if(payloadlen != 4) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->unique_id = spdylay_get_uint32(payload); + return 0; +} + +ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_goaway *frame) +{ + ssize_t framelen; + int r; + if(frame->hd.version == SPDYLAY_PROTO_SPDY2) { + framelen = 12; + } else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + framelen = 16; + } else { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->last_good_stream_id); + if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code); + } + return framelen; +} + +int spdylay_frame_unpack_goaway(spdylay_goaway *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + if(frame->hd.version == SPDYLAY_PROTO_SPDY2) { + if(payloadlen != 4) { + return SPDYLAY_ERR_INVALID_FRAME; + } + } else if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + if(payloadlen != 8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + } else { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + frame->last_good_stream_id = spdylay_get_uint32(payload) & + SPDYLAY_STREAM_ID_MASK; + if(frame->hd.version == SPDYLAY_PROTO_SPDY3) { + frame->status_code = spdylay_get_uint32(payload+4); + } else { + frame->status_code = 0; + } + return 0; +} + +#define SPDYLAY_SPDY2_HEADERS_NV_OFFSET 14 +#define SPDYLAY_SPDY3_HEADERS_NV_OFFSET 12 + +ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, + uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr, + spdylay_headers *frame, + spdylay_zlib *deflater) +{ + ssize_t framelen; + size_t len_size; + size_t nv_offset; + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? + SPDYLAY_SPDY2_HEADERS_NV_OFFSET : SPDYLAY_SPDY3_HEADERS_NV_OFFSET; + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, nv_offset, + len_size, deflater); + if(framelen < 0) { + return framelen; + } + frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; + memset(*buf_ptr, 0, nv_offset); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + return framelen; +} + +int spdylay_frame_unpack_headers(spdylay_headers *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater) +{ + int r; + size_t len_size; + size_t nv_offset; + if(payloadlen < 8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + len_size = spdylay_frame_get_len_size(frame->hd.version); + if(len_size == 0) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4; + frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+nv_offset, payloadlen-nv_offset, + len_size, inflater); + return r; +} + +ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_rst_stream *frame) +{ + ssize_t framelen = 16; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code); + return framelen; +} + +int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + if(payloadlen != 8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; + frame->status_code = spdylay_get_uint32(payload+4); + return 0; +} + +ssize_t spdylay_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_window_update *frame) +{ + ssize_t framelen = 16; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[12], frame->delta_window_size); + return framelen; +} + +int spdylay_frame_unpack_window_update(spdylay_window_update *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, + size_t payloadlen) +{ + if(payloadlen != 8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; + frame->delta_window_size = spdylay_get_uint32(&payload[4]) & + SPDYLAY_DELTA_WINDOW_SIZE_MASK; + return 0; +} + +ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_settings *frame) +{ + ssize_t framelen = SPDYLAY_FRAME_HEAD_LENGTH+frame->hd.length; + int i, r; + if(frame->hd.version != SPDYLAY_PROTO_SPDY2 && + frame->hd.version != SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->niv); + if(frame->hd.version == SPDYLAY_PROTO_SPDY2) { + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + /* spdy/2 spec says ID is network byte order, but publicly + deployed server sends little endian host byte order. */ + char *id_ptr = (char*)(&frame->iv[i].settings_id); +#ifdef WORDS_BIGENDIAN + (*buf_ptr)[12+off] = id_ptr[3]; + (*buf_ptr)[12+off+1] = id_ptr[2]; + (*buf_ptr)[12+off+2] = id_ptr[1]; +#else /* !WORDS_BIGENDIAN */ + memcpy(&(*buf_ptr)[12+off], id_ptr, 3); +#endif /* !WORDS_BIGENDIAN */ + (*buf_ptr)[15+off] = frame->iv[i].flags; + spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value); + } + } else { + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + spdylay_put_uint32be(&(*buf_ptr)[12+off], frame->iv[i].settings_id); + (*buf_ptr)[12+off] = frame->iv[i].flags; + spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value); + } + } + return framelen; +} + +int spdylay_frame_unpack_settings(spdylay_settings *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + int i; + if(payloadlen < 4) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + if(frame->hd.version != SPDYLAY_PROTO_SPDY2 && + frame->hd.version != SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + frame->niv = spdylay_get_uint32(payload); + if(payloadlen != 4+frame->niv*8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + frame->iv = malloc(frame->niv*sizeof(spdylay_settings_entry)); + if(frame->iv == NULL) { + return SPDYLAY_ERR_NOMEM; + } + if(frame->hd.version == SPDYLAY_PROTO_SPDY2) { + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + /* ID is little endian. See comments in + spdylay_frame_pack_settings(). */ + frame->iv[i].settings_id = 0; +#ifdef WORDS_BIGENDIAN + *(char*)(&frame->iv[i].settings_id[1]) = &payload[4+off+2]; + *(char*)(&frame->iv[i].settings_id[2]) = &payload[4+off+1]; + *(char*)(&frame->iv[i].settings_id[3]) = &payload[4+off+0]; +#else /* !WORDS_BIGENDIAN */ + memcpy(&frame->iv[i].settings_id, &payload[4+off], 3); +#endif /* !WORDS_BIGENDIAN */ + frame->iv[i].flags = payload[7+off]; + frame->iv[i].value = spdylay_get_uint32(&payload[8+off]); + } + } else { + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + frame->iv[i].settings_id = spdylay_get_uint32(&payload[4+off]) & + SPDYLAY_SETTINGS_ID_MASK; + frame->iv[i].flags = payload[4+off]; + frame->iv[i].value = spdylay_get_uint32(&payload[8+off]); + } + } + return 0; +} + +spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv, + size_t niv) +{ + spdylay_settings_entry *iv_copy; + size_t len = niv*sizeof(spdylay_settings_entry); + iv_copy = malloc(len); + if(iv_copy == NULL) { + return NULL; + } + memcpy(iv_copy, iv, len); + return iv_copy; +} + +static int spdylay_settings_entry_compar(const void *lhs, const void *rhs) +{ + return ((spdylay_settings_entry *)lhs)->settings_id + -((spdylay_settings_entry *)rhs)->settings_id; +} + +void spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv) +{ + qsort(iv, niv, sizeof(spdylay_settings_entry), spdylay_settings_entry_compar); +} diff --git a/deps/spdylay/lib/spdylay_frame.h b/deps/spdylay/lib/spdylay_frame.h new file mode 100644 index 0000000..03e0dc5 --- /dev/null +++ b/deps/spdylay/lib/spdylay_frame.h @@ -0,0 +1,628 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_FRAME_H +#define SPDYLAY_FRAME_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_zlib.h" +#include "spdylay_buffer.h" + +#define SPDYLAY_STREAM_ID_MASK 0x7fffffff +#define SPDYLAY_LENGTH_MASK 0xffffff +#define SPDYLAY_VERSION_MASK 0x7fff +#define SPDYLAY_DELTA_WINDOW_SIZE_MASK 0x7fffffff +#define SPDYLAY_SETTINGS_ID_MASK 0xffffff + +/* The length of DATA frame payload. */ +#define SPDYLAY_DATA_PAYLOAD_LENGTH 4096 + +/* The number of bytes of frame header. */ +#define SPDYLAY_FRAME_HEAD_LENGTH 8 + +/* + * Packs SYN_STREAM frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. + * + * frame->hd.length is assigned after length is determined during + * packing process. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_ZLIB + * The deflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + spdylay_syn_stream *frame, + spdylay_zlib *deflater); + +/* + * Unpacks SYN_STREAM frame byte sequence into |frame|. Header is + * given in head and headlen. In spdy/2 spec, headlen is 8 + * bytes. |payload| is the data after length field of the header. + * + * |inflatebuf| is used to buffer name/value pairs while inflating + * them using |inflater|. The caller must reset |inflatebuf| before + * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal + * inflated name/value pairs. This function expands |*nvbuf_ptr| as + * necessary and updates these variables. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_ZLIB + * The inflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater); + +/* + * Packs SYN_REPLY frame |frame| in wire frame format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. + * + * frame->hd.length is assigned after length is determined during + * packing process. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_ZLIB + * The deflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + spdylay_syn_reply *frame, + spdylay_zlib *deflater); + +/* + * Unpacks SYN_REPLY frame byte sequence into |frame|. + * + * |inflatebuf| is used to buffer name/value pairs while inflating + * them using |inflater|. The caller must reset |inflatebuf| before + * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal + * inflated name/value pairs. This function expands |*nvbuf_ptr| as + * necessary and updates these variables. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + * SPDYLAY_ERR_ZLIB + * The inflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater); + +/* + * Packs PING frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_ping *frame); + +/* + * Unpacks PING wire format into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_ping(spdylay_ping *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + +/* + * Packs GOAWAY frame |frame | in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_goaway *frame); + +/* + * Unpacks GOAWAY wire format into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_goaway(spdylay_goaway *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + +/* + * Packs HEADERS frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. + * + * frame->hd.length is assigned after length is determined during + * packing process. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_ZLIB + * The deflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, + uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr, + spdylay_headers *frame, + spdylay_zlib *deflater); + +/* + * Unpacks HEADERS wire format into |frame|. + * + * |inflatebuf| is used to buffer name/value pairs while inflating + * them using |inflater|. The caller must reset |inflatebuf| before + * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal + * inflated name/value pairs. This function expands |*nvbuf_ptr| as + * necessary and updates these variables. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + * SPDYLAY_ERR_ZLIB + * The inflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_unpack_headers(spdylay_headers *frame, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater); + +/* + * Packs RST_STREAM frame |frame| in wire frame format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16 + * bytes long. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + + +/* + * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it + * in |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. In SPDY/3 spec, WINDOW_UPDATE wire format is always 16 + * bytes long. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_window_update *frame); + +/* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_unpack_window_update(spdylay_window_update *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, + size_t payloadlen); + +/* + * Packs SETTINGS frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. + * + * This function returns the size of packed frame if it succeeds, or + * returns one of the following negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_settings *frame); + +/* + * Unpacks SETTINGS wire format into |frame|. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_unpack_settings(spdylay_settings *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + +/* + * Returns number of bytes to pack name/value pairs |nv|. This + * function expects |nv| is sorted in ascending order of key. + * |len_size| is the number of bytes in length of name/value pair and + * it must be 2 or 4. + * + * This function can handles duplicate keys and concatenation of thier + * values with '\0'. + */ +size_t spdylay_frame_count_nv_space(char **nv, size_t len_size); + +/* + * Packs name/value pairs in |nv| in |buf|. |buf| must have at least + * spdylay_frame_count_nv_space(nv) bytes. |len_size| is the number + * of bytes in length of name/value pair and it must be 2 or 4. + */ +ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size); + +/* + * Packs name/value pairs in |nv| in |*buf_ptr| with offset + * |nv_offset|. It means first byte of packed name/value pairs is + * stored in |*buf_ptr|+|nv_offset|. |*buf_ptr| and |*nvbuf_ptr| are + * expanded as necessary. + * + * This function returns the number of the bytes for the frame + * containing this name/value pairs if it succeeds, or one of the + * following negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The deflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + char **nv, size_t nv_offset, + size_t len_size, + spdylay_zlib *deflater); + +/* + * Counts number of name/value pair in |in| and computes length of + * buffers to store unpacked name/value pair and store them in + * |*num_nv_ptr| and |*buf_size_ptr| respectively. |len_size| is the + * number of bytes in length of name/value pair and it must be 2 or + * 4. We use folloing data structure in |*buf_size_ptr|. First part + * of the data is array of pointer to name/value pair. Supporse the + * buf pointer points to the data region and N is the number of + * name/value pair. First (N*2+1)*sizeof(char*) bytes contain array + * of pointer to name/value pair and terminating NULL. Each pointer + * to name/value pair points to the string in remaining data. For + * each name/value pair, the name is copied to the remaining data with + * terminating NULL character. The value is also copied to the + * position after the data with terminating NULL character. The + * corresponding index is assigned to these pointers. If the value + * contains multiple values (delimited by single NULL), for each such + * data, corresponding index is assigned to name/value pointers. In + * this case, the name string is reused. + * + * With the above stragety, |*buf_size_ptr| is calculated as + * (N*2+1)*sizeof(char*)+sum(strlen(name)+1+strlen(value)+1){for each + * name/value pair}. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * The input data are invalid. + */ +int spdylay_frame_count_unpack_nv_space +(size_t *num_nv_ptr, size_t *buf_size_ptr, const uint8_t *in, size_t inlen, + size_t len_size); + +/* + * Unpacks name/value pairs in wire format |in| with length |inlen| + * and stores them in |*nv_ptr|. Thif function allocates enough + * memory to store name/value pairs in |*nv_ptr|. |len_size| is the + * number of bytes in length of name/value pair and it must be 2 or 4. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, + size_t len_size); + +/* + * Unpacks name/value pairs from buffer |in| with length |inlen|. The + * necessary memory area required for output is allocated and its + * pointer is assigned to |nv_ptr|. |inflatebuf| is used for inflate + * operation. |*nvbuf_ptr| is used for temporarily stored inflated + * name/value pair in wire format. It is expanded as necessary. + * |len_size| is the number of bytes used in name/value length. It + * must be either 2 or 4. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The inflate operation failed. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_frame_alloc_unpack_nv(char ***nv_ptr, + spdylay_buffer *inflatebuf, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, + const uint8_t *in, size_t inlen, + size_t len_size, + spdylay_zlib *inflater); + +/* + * Initializes SYN_STREAM frame |frame| with given values. |frame| + * takes ownership of |nv|, so caller must not free it. If stream_id + * is not assigned yet, it must be 0. + */ +void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, + uint16_t version, uint8_t flags, + int32_t stream_id, int32_t assoc_stream_id, + uint8_t pri, char **nv); + +void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame); + +void spdylay_frame_syn_reply_init(spdylay_syn_reply *frame, + uint16_t version, uint8_t flags, + int32_t stream_id, char **nv); + +void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame); + +void spdylay_frame_ping_init(spdylay_ping *frame, uint16_t version, + uint32_t unique_id); + +void spdylay_frame_ping_free(spdylay_ping *frame); + +/* + * Initializes GOAWAY frame |frame| with given values. The + * |status_code| is ignored if |version| == SPDYLAY_PROTO_SPDY2. + */ +void spdylay_frame_goaway_init(spdylay_goaway *frame, uint16_t version, + int32_t last_good_stream_id, + uint32_t status_code); + +void spdylay_frame_goaway_free(spdylay_goaway *frame); + +void spdylay_frame_headers_init(spdylay_headers *frame, uint16_t version, + uint8_t flags, + int32_t stream_id, char **nv); + +void spdylay_frame_headers_free(spdylay_headers *frame); + +void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame, + uint16_t version, + int32_t stream_id, uint32_t status_code); + +void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame); + +void spdylay_frame_window_update_init(spdylay_window_update *frame, + uint16_t version, + int32_t stream_id, + int32_t delta_window_size); + +void spdylay_frame_window_update_free(spdylay_window_update *frame); + +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. + */ +void spdylay_frame_settings_init(spdylay_settings *frame, + uint16_t version, uint8_t flags, + spdylay_settings_entry *iv, size_t niv); + +void spdylay_frame_settings_free(spdylay_settings *frame); + +void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, + uint8_t flags, + const spdylay_data_provider *data_prd); + +void spdylay_frame_data_free(spdylay_data *frame); + +/* + * Returns 1 if the first byte of this frame indicates it is a control + * frame. + */ +int spdylay_frame_is_ctrl_frame(uint8_t first_byte); + +/* + * Deallocates memory of name/value pair |nv|. + */ +void spdylay_frame_nv_del(char **nv); + +/* + * Makes a deep copy of |nv| and returns the copy. This function + * returns the pointer to the copy if it succeeds, or NULL. To free + * allocated memory, use spdylay_frame_nv_del(). + */ +char** spdylay_frame_nv_copy(const char **nv); + +/* + * Sorts |nv| in the ascending order of name. + */ +void spdylay_frame_nv_sort(char **nv); + +/* + * Makes names in |nv| lower cased. + */ +void spdylay_frame_nv_downcase(char **nv); + +/* + * This function first makes a copy of |nv| using + * spdylay_frame_nv_copy(). If it succeeds, then call + * spdylay_frame_nv_downcase() and spdylay_frame_nv_sort() with the + * copied name/value pairs. + * + * This function returns the copied name/value pairs if it succeeds, + * or NULL. + */ +char** spdylay_frame_nv_norm_copy(const char **nv); + +/* + * Translates the |nv| in SPDY/3 header names into SPDY/2. + */ +void spdylay_frame_nv_3to2(char **nv); + +/* + * Translates the |nv| in SPDY/2 header names into SPDY/3. + */ +void spdylay_frame_nv_2to3(char **nv); + +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv, + size_t niv); + +/* + * Sorts the |iv| with the ascending order of the settings_id member. + * The number of the element in the array pointed by the |iv| is given + * by the |niv|. + */ +void spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv); + +#endif /* SPDYLAY_FRAME_H */ diff --git a/deps/spdylay/lib/spdylay_helper.c b/deps/spdylay/lib/spdylay_helper.c new file mode 100644 index 0000000..9243886 --- /dev/null +++ b/deps/spdylay/lib/spdylay_helper.c @@ -0,0 +1,71 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_helper.h" + +#include +#include + +void spdylay_put_uint16be(uint8_t *buf, uint16_t n) +{ + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void spdylay_put_uint32be(uint8_t *buf, uint32_t n) +{ + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t spdylay_get_uint16(const uint8_t *data) +{ + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t spdylay_get_uint32(const uint8_t *data) +{ + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} + +int spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t min_length) +{ + if(min_length > *buflen_ptr) { + min_length = (min_length+4095)/4096*4096; + uint8_t *temp = malloc(min_length); + if(temp == NULL) { + return SPDYLAY_ERR_NOMEM; + } else { + free(*buf_ptr); + *buf_ptr = temp; + *buflen_ptr = min_length; + } + } + return 0; +} diff --git a/deps/spdylay/lib/spdylay_helper.h b/deps/spdylay/lib/spdylay_helper.h new file mode 100644 index 0000000..7355cd7 --- /dev/null +++ b/deps/spdylay/lib/spdylay_helper.h @@ -0,0 +1,80 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_HELPER_H +#define SPDYLAY_HELPER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#define spdylay_min(A, B) ((A) < (B) ? (A) : (B)) +#define spdylay_max(A, B) ((A) > (B) ? (A) : (B)) + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void spdylay_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void spdylay_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t spdylay_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t spdylay_get_uint32(const uint8_t *data); + +/* + * Ensures that buffer |*buf_ptr| with |*buflen_ptr| length has at + * least |min_length| bytes. If |min_length| > |*buflen_ptr|, + * allocates new buffer having at least |min_length| bytes and assigns + * its pointer to |*buf_ptr| and allocated number of bytes to + * |*buflen_ptr|. The memory pointed by |*buf_ptr| previously is + * freed. No memory copy is done between old and new buffer. + * |*buf_ptr| and |*buflen_ptr| are only updated iff this function + * succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t min_length); + +#endif /* SPDYLAY_HELPER_H */ diff --git a/deps/spdylay/lib/spdylay_int.h b/deps/spdylay/lib/spdylay_int.h new file mode 100644 index 0000000..1f7924a --- /dev/null +++ b/deps/spdylay/lib/spdylay_int.h @@ -0,0 +1,38 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_INT_H +#define SPDYLAY_INT_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* Macros, types and constants for internal use */ + +typedef int (*spdylay_compar)(const void *lhs, const void *rhs); + +#endif /* SPDYLAY_INT_H */ diff --git a/deps/spdylay/lib/spdylay_map.c b/deps/spdylay/lib/spdylay_map.c new file mode 100644 index 0000000..8fb8dc4 --- /dev/null +++ b/deps/spdylay/lib/spdylay_map.c @@ -0,0 +1,215 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_map.h" + +void spdylay_map_init(spdylay_map *map) +{ + map->root = NULL; + map->size = 0; +} + +static void spdylay_map_entry_free(spdylay_map_entry *entry) +{ + if(entry != NULL) { + free(entry); + } +} + +static void spdylay_map_entry_free_recur(spdylay_map_entry *entry) +{ + if(entry != NULL) { + spdylay_map_entry_free_recur(entry->left); + spdylay_map_entry_free_recur(entry->right); + free(entry); + } +} + +void spdylay_map_free(spdylay_map *map) +{ + spdylay_map_entry_free_recur(map->root); + map->root = NULL; +} + +/* + * 32 bit Mix Functions by Thomas Wang + * + * http://www.concentric.net/~Ttwang/tech/inthash.htm + */ +static uint32_t hash32shift(uint32_t key) +{ + key = ~key + (key << 15); /* key = (key << 15) - key - 1; */ + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key * 2057; /* key = (key + (key << 3)) + (key << 11); */ + key = key ^ (key >> 16); + return key; +} + +static spdylay_map_entry* spdylay_map_entry_new(key_type key, void *val) +{ + spdylay_map_entry *entry = + (spdylay_map_entry*)malloc(sizeof(spdylay_map_entry)); + if(entry != NULL) { + entry->key = key; + entry->val = val; + entry->left = entry->right = NULL; + entry->priority = hash32shift(key); + } + return entry; +} + +static spdylay_map_entry* rotate_left(spdylay_map_entry *entry) +{ + spdylay_map_entry *root = entry->right; + entry->right = root->left; + root->left = entry; + return root; +} + +static spdylay_map_entry* rotate_right(spdylay_map_entry* entry) +{ + spdylay_map_entry *root = entry->left; + entry->left = root->right; + root->right = entry; + return root; +} + +static spdylay_map_entry* insert_recur(spdylay_map_entry *entry, + key_type key, void *val, + int *error) +{ + if(entry == NULL) { + entry = spdylay_map_entry_new(key, val); + if(entry == NULL) { + *error = SPDYLAY_ERR_NOMEM; + } + } else if(key == entry->key) { + *error = SPDYLAY_ERR_INVALID_ARGUMENT; + } else if(key < entry->key) { + entry->left = insert_recur(entry->left, key, val, error); + } else { + entry->right = insert_recur(entry->right, key, val, error); + } + if(entry->left != NULL && entry->priority > entry->left->priority) { + entry = rotate_right(entry); + } else if(entry->right != NULL && entry->priority > entry->right->priority) { + entry = rotate_left(entry); + } + return entry; +} + +int spdylay_map_insert(spdylay_map *map, key_type key, void *val) +{ + int error = 0; + map->root = insert_recur(map->root, key, val, &error); + if(!error) { + ++map->size; + } + return error; +} + +void* spdylay_map_find(spdylay_map *map, key_type key) +{ + spdylay_map_entry *entry = map->root; + while(entry != NULL) { + if(key < entry->key) { + entry = entry->left; + } else if(key > entry->key) { + entry = entry->right; + } else { + return entry->val; + } + } + return NULL; +} + +static spdylay_map_entry* erase_rotate_recur(spdylay_map_entry *entry) +{ + if(entry->left == NULL) { + spdylay_map_entry *right = entry->right; + spdylay_map_entry_free(entry); + return right; + } else if(entry->right == NULL) { + spdylay_map_entry *left = entry->left; + spdylay_map_entry_free(entry); + return left; + } else if(entry->left->priority < entry->right->priority) { + entry = rotate_right(entry); + return erase_rotate_recur(entry->right); + } else { + entry = rotate_left(entry); + return erase_rotate_recur(entry->left); + } +} + +static spdylay_map_entry* erase_recur(spdylay_map_entry *entry, key_type key, + int *error) +{ + if(entry == NULL) { + *error = SPDYLAY_ERR_INVALID_ARGUMENT; + } else if(key < entry->key) { + entry->left = erase_recur(entry->left, key, error); + } else if(key > entry->key) { + entry->right = erase_recur(entry->right, key, error); + } else { + entry = erase_rotate_recur(entry); + } + return entry; +} + +void spdylay_map_erase(spdylay_map *map, key_type key) +{ + if(map->root != NULL) { + int error = 0; + map->root = erase_recur(map->root, key, &error); + if(!error) { + --map->size; + } + } +} + +size_t spdylay_map_size(spdylay_map *map) +{ + return map->size; +} + +static void for_each(spdylay_map_entry *entry, + void (*func)(key_type key, void *val, void *ptr), + void *ptr) +{ + if(entry != NULL) { + for_each(entry->left, func, ptr); + func(entry->key, entry->val, ptr); + for_each(entry->right, func, ptr); + } +} + +void spdylay_map_each(spdylay_map *map, + void (*func)(key_type key, void *val, void *ptr), + void *ptr) +{ + for_each(map->root, func, ptr); +} diff --git a/deps/spdylay/lib/spdylay_map.h b/deps/spdylay/lib/spdylay_map.h new file mode 100644 index 0000000..6237c61 --- /dev/null +++ b/deps/spdylay/lib/spdylay_map.h @@ -0,0 +1,110 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_MAP_H +#define SPDYLAY_MAP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_int.h" + +/* Implementation of ordered map */ + +typedef uint32_t key_type; +typedef uint32_t pri_type; + +typedef struct spdylay_map_entry { + key_type key; + void *val; + struct spdylay_map_entry *left, *right; + pri_type priority; +} spdylay_map_entry; + +typedef struct { + spdylay_map_entry *root; + size_t size; +} spdylay_map; + +/* + * Initializes the map |map|. + */ +void spdylay_map_init(spdylay_map *map); + +/* + * Deallocates any resources allocated for |map|. The stored items are + * not freed by this function. Use spdylay_map_each() to free each + * item. + */ +void spdylay_map_free(spdylay_map *map); + +/* + * Inserts the new item |val| with the key |key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error code: + * + * SPDYLAY_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_map_insert(spdylay_map *map, key_type key, void *val); + +/* + * Returns the item associated by the key |key|. If there is no such + * item, this function returns NULL. + */ +void* spdylay_map_find(spdylay_map *map, key_type key); + +/* + * Erases the item associated by the key |key|. The erased item is + * not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_ARGUMENT + * The item associated by |key| does not exist. + */ +void spdylay_map_erase(spdylay_map *map, key_type key); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t spdylay_map_size(spdylay_map *map); + +/* + * Applies the function |func| to each key/item pair in the map |map| + * with the optional user supplied pointer |ptr|. This function is + * useful to free item in the map. + */ +void spdylay_map_each(spdylay_map *map, + void (*func)(key_type key, void *val, void *ptr), + void *ptr); + +#endif /* SPDYLAY_MAP_H */ diff --git a/deps/spdylay/lib/spdylay_npn.c b/deps/spdylay/lib/spdylay_npn.c new file mode 100644 index 0000000..73f664c --- /dev/null +++ b/deps/spdylay/lib/spdylay_npn.c @@ -0,0 +1,83 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_npn.h" + +#include + +typedef struct { + const unsigned char *proto; + uint8_t len; + uint16_t version; +} spdylay_npn_proto; + +int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) +{ + int http_selected = 0; + unsigned int i = 0; + static const spdylay_npn_proto proto_list[] = { + { (const unsigned char*)"spdy/2", 6, SPDYLAY_PROTO_SPDY2 }, + { (const unsigned char*)"spdy/3", 6, SPDYLAY_PROTO_SPDY3 } + }; + for(; i < inlen; i += in[i]+1) { + int j; + for(j = 0; j < sizeof(proto_list)/sizeof(spdylay_npn_proto); ++j) { + if(in[i] == proto_list[j].len && + memcmp(&in[i+1], proto_list[j].proto, in[i]) == 0) { + *out = (unsigned char*)&in[i+1]; + *outlen = in[i]; + return proto_list[j].version; + } + } + if(in[i] == 8 && memcmp(&in[i+1], "http/1.1", in[i]) == 0) { + http_selected = 1; + *out = (unsigned char*)&in[i+1]; + *outlen = in[i]; + /* Go through to the next iteration, because "spdy/2" may be + there */ + } + } + if(http_selected) { + return 0; + } else { + return -1; + } +} + +uint16_t spdylay_npn_get_version(const unsigned char *proto, size_t protolen) +{ + if(proto == NULL) { + return 0; + } else { + if(protolen == 6) { + if(memcmp("spdy/2", proto, 6) == 0) { + return SPDYLAY_PROTO_SPDY2; + } else if(memcmp("spdy/3", proto, 6) == 0) { + return SPDYLAY_PROTO_SPDY3; + } + } + return 0; + } +} diff --git a/deps/spdylay/lib/spdylay_npn.h b/deps/spdylay/lib/spdylay_npn.h new file mode 100644 index 0000000..8d60c9d --- /dev/null +++ b/deps/spdylay/lib/spdylay_npn.h @@ -0,0 +1,34 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_NPN_H +#define SPDYLAY_NPN_H + +#ifdef HAVE_CONFIG +# include +#endif /* HAVE_CONFIG */ + +#include + +#endif /* SPDYLAY_NPN_H */ diff --git a/deps/spdylay/lib/spdylay_outbound_item.c b/deps/spdylay/lib/spdylay_outbound_item.c new file mode 100644 index 0000000..3ff7c4b --- /dev/null +++ b/deps/spdylay/lib/spdylay_outbound_item.c @@ -0,0 +1,68 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_outbound_item.h" + +void spdylay_outbound_item_free(spdylay_outbound_item *item) +{ + if(item == NULL) { + return; + } + switch(item->frame_type) { + case SPDYLAY_SYN_STREAM: + spdylay_frame_syn_stream_free(&item->frame->syn_stream); + free(((spdylay_syn_stream_aux_data*)item->aux_data)->data_prd); + break; + case SPDYLAY_SYN_REPLY: + spdylay_frame_syn_reply_free(&item->frame->syn_reply); + break; + case SPDYLAY_RST_STREAM: + spdylay_frame_rst_stream_free(&item->frame->rst_stream); + break; + case SPDYLAY_SETTINGS: + spdylay_frame_settings_free(&item->frame->settings); + break; + case SPDYLAY_NOOP: + /* We don't have any public API to add NOOP, so here is + unreachable. */ + abort(); + case SPDYLAY_PING: + spdylay_frame_ping_free(&item->frame->ping); + break; + case SPDYLAY_GOAWAY: + spdylay_frame_goaway_free(&item->frame->goaway); + break; + case SPDYLAY_HEADERS: + spdylay_frame_headers_free(&item->frame->headers); + break; + case SPDYLAY_WINDOW_UPDATE: + spdylay_frame_window_update_free(&item->frame->window_update); + break; + case SPDYLAY_DATA: + spdylay_frame_data_free(&item->frame->data); + break; + } + free(item->frame); + free(item->aux_data); +} diff --git a/deps/spdylay/lib/spdylay_outbound_item.h b/deps/spdylay/lib/spdylay_outbound_item.h new file mode 100644 index 0000000..ad4475d --- /dev/null +++ b/deps/spdylay/lib/spdylay_outbound_item.h @@ -0,0 +1,54 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_OUTBOUND_ITEM_H +#define SPDYLAY_OUTBOUND_ITEM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_frame.h" + +typedef struct { + spdylay_data_provider *data_prd; + void *stream_user_data; +} spdylay_syn_stream_aux_data; + +typedef struct { + spdylay_frame_type frame_type; + spdylay_frame *frame; + void *aux_data; + int pri; + int64_t seq; +} spdylay_outbound_item; + +/* + * Deallocates resource for |item|. If |item| is NULL, this function + * does nothing. + */ +void spdylay_outbound_item_free(spdylay_outbound_item *item); + +#endif /* SPDYLAY_OUTBOUND_ITEM_H */ diff --git a/deps/spdylay/lib/spdylay_pq.c b/deps/spdylay/lib/spdylay_pq.c new file mode 100644 index 0000000..8422596 --- /dev/null +++ b/deps/spdylay/lib/spdylay_pq.c @@ -0,0 +1,127 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_pq.h" + +int spdylay_pq_init(spdylay_pq *pq, spdylay_compar compar) +{ + pq->capacity = 4096; + pq->q = malloc(pq->capacity); + if(pq->q == NULL) { + return SPDYLAY_ERR_NOMEM; + } + pq->length = 0; + pq->compar = compar; + return 0; +} + +void spdylay_pq_free(spdylay_pq *pq) +{ + free(pq->q); + pq->q = NULL; +} + +static void swap(spdylay_pq *pq, size_t i, size_t j) +{ + void *t = pq->q[i]; + pq->q[i] = pq->q[j]; + pq->q[j] = t; +} + +static void bubble_up(spdylay_pq *pq, size_t index) +{ + if(index == 0) { + return; + } else { + size_t parent = (index-1)/2; + if(pq->compar(pq->q[parent], pq->q[index]) > 0) { + swap(pq, parent, index); + bubble_up(pq, parent); + } + } +} + +int spdylay_pq_push(spdylay_pq *pq, void *item) +{ + if(pq->capacity <= pq->length) { + void *nq = realloc(pq->q, pq->capacity*2); + if(nq == NULL) { + return SPDYLAY_ERR_NOMEM; + } + pq->capacity *= 2; + pq->q = nq; + } + pq->q[pq->length] = item; + ++pq->length; + bubble_up(pq, pq->length-1); + return 0; +} + +void* spdylay_pq_top(spdylay_pq *pq) +{ + if(pq->length == 0) { + return NULL; + } else { + return pq->q[0]; + } +} + +static void bubble_down(spdylay_pq *pq, size_t index) +{ + size_t lchild = index*2+1; + size_t minindex = index; + int i, j; + for(i = 0; i < 2; ++i) { + j = lchild+i; + if(j >= pq->length) { + break; + } + if(pq->compar(pq->q[minindex], pq->q[j]) > 0) { + minindex = j; + } + } + if(minindex != index) { + swap(pq, index, minindex); + bubble_down(pq, minindex); + } +} + +void spdylay_pq_pop(spdylay_pq *pq) +{ + if(pq->length > 0) { + pq->q[0] = pq->q[pq->length-1]; + --pq->length; + bubble_down(pq, 0); + } +} + +int spdylay_pq_empty(spdylay_pq *pq) +{ + return pq->length == 0; +} + +size_t spdylay_pq_size(spdylay_pq *pq) +{ + return pq->length; +} diff --git a/deps/spdylay/lib/spdylay_pq.h b/deps/spdylay/lib/spdylay_pq.h new file mode 100644 index 0000000..9835e21 --- /dev/null +++ b/deps/spdylay/lib/spdylay_pq.h @@ -0,0 +1,99 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_PQ_H +#define SPDYLAY_PQ_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_int.h" + +/* Implementation of priority queue */ + +typedef struct { + /* The pointer to the pointer to the item stored */ + void **q; + /* The number of items sotred */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The compare function between items */ + spdylay_compar compar; +} spdylay_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_pq_init(spdylay_pq *pq, spdylay_compar cmp); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void spdylay_pq_free(spdylay_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_pq_push(spdylay_pq *pq, void *item); + +/* + * Returns item at the top of the queue |pq|. If the queue is empty, + * this function returns NULL. + */ +void* spdylay_pq_top(spdylay_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void spdylay_pq_pop(spdylay_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int spdylay_pq_empty(spdylay_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t spdylay_pq_size(spdylay_pq *pq); + +#endif /* SPDYLAY_PQ_H */ diff --git a/deps/spdylay/lib/spdylay_queue.c b/deps/spdylay/lib/spdylay_queue.c new file mode 100644 index 0000000..d718e58 --- /dev/null +++ b/deps/spdylay/lib/spdylay_queue.c @@ -0,0 +1,93 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_queue.h" + +#include +#include + +void spdylay_queue_init(spdylay_queue *queue) +{ + queue->front = queue->back = NULL; +} + +void spdylay_queue_free(spdylay_queue *queue) +{ + if(!queue) { + return; + } + spdylay_queue_cell *p = queue->front; + while(p) { + spdylay_queue_cell *next = p->next; + free(p); + p = next; + } +} + +int spdylay_queue_push(spdylay_queue *queue, void *data) +{ + spdylay_queue_cell *new_cell = (spdylay_queue_cell*)malloc + (sizeof(spdylay_queue_cell)); + if(!new_cell) { + return SPDYLAY_ERR_NOMEM; + } + new_cell->data = data; + new_cell->next = NULL; + if(queue->back) { + queue->back->next = new_cell; + queue->back = new_cell; + + } else { + queue->front = queue->back = new_cell; + } + return 0; +} + +void spdylay_queue_pop(spdylay_queue *queue) +{ + spdylay_queue_cell *front = queue->front; + assert(front); + queue->front = front->next; + if(front == queue->back) { + queue->back = NULL; + } + free(front); +} + +void* spdylay_queue_front(spdylay_queue *queue) +{ + assert(queue->front); + return queue->front->data; +} + +void* spdylay_queue_back(spdylay_queue *queue) +{ + assert(queue->back); + return queue->back->data; +} + +int spdylay_queue_empty(spdylay_queue *queue) +{ + return queue->front == NULL; +} diff --git a/deps/spdylay/lib/spdylay_queue.h b/deps/spdylay/lib/spdylay_queue.h new file mode 100644 index 0000000..ff20bca --- /dev/null +++ b/deps/spdylay/lib/spdylay_queue.h @@ -0,0 +1,51 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_QUEUE_H +#define SPDYLAY_QUEUE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct spdylay_queue_cell { + void *data; + struct spdylay_queue_cell *next; +} spdylay_queue_cell; + +typedef struct { + spdylay_queue_cell *front, *back; +} spdylay_queue; + +void spdylay_queue_init(spdylay_queue *queue); +void spdylay_queue_free(spdylay_queue *queue); +int spdylay_queue_push(spdylay_queue *queue, void *data); +void spdylay_queue_pop(spdylay_queue *queue); +void* spdylay_queue_front(spdylay_queue *queue); +void* spdylay_queue_back(spdylay_queue *queue); +int spdylay_queue_empty(spdylay_queue *queue); + +#endif /* SPDYLAY_QUEUE_H */ diff --git a/deps/spdylay/lib/spdylay_session.c b/deps/spdylay/lib/spdylay_session.c new file mode 100644 index 0000000..b4d19b4 --- /dev/null +++ b/deps/spdylay/lib/spdylay_session.c @@ -0,0 +1,2344 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_session.h" + +#include +#include +#include +#include +#include + +#include "spdylay_helper.h" + +/* + * Returns non-zero if the number of opened streams is larger than or + * equal to SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS value. + */ +static int spdylay_session_get_max_concurrent_streams_reached +(spdylay_session *session) +{ + uint32_t local_max, remote_max; + local_max = session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]; + remote_max = + session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]; + return spdylay_min(local_max, remote_max) + <= spdylay_map_size(&session->streams); +} + +/* + * Returns non-zero if |error| is non-fatal error. + */ +static int spdylay_is_non_fatal(int error) +{ + return error < 0 && error > SPDYLAY_ERR_FATAL; +} + +/* + * Returns non-zero if |error| is fatal error. + */ +static int spdylay_is_fatal(int error) +{ + return error < SPDYLAY_ERR_FATAL; +} + +/* + * This function should be called when the session wants to drop + * connection after sending GOAWAY. These cases are called as the + * session error. For example, when it receives bad zlib data. + */ +static int spdylay_session_fail_session(spdylay_session *session, + uint32_t status_code) +{ + session->goaway_flags |= SPDYLAY_GOAWAY_FAIL_ON_SEND; + return spdylay_submit_goaway(session, status_code); +} + +int spdylay_session_is_my_stream_id(spdylay_session *session, + int32_t stream_id) +{ + int r; + if(stream_id == 0) { + return 0; + } + r = stream_id % 2; + return (session->server && r == 0) || (!session->server && r == 1); +} + +spdylay_stream* spdylay_session_get_stream(spdylay_session *session, + int32_t stream_id) +{ + return (spdylay_stream*)spdylay_map_find(&session->streams, stream_id); +} + +static int spdylay_outbound_item_compar(const void *lhsx, const void *rhsx) +{ + const spdylay_outbound_item *lhs, *rhs; + lhs = (const spdylay_outbound_item*)lhsx; + rhs = (const spdylay_outbound_item*)rhsx; + if(lhs->pri == rhs->pri) { + return (lhs->seq < rhs->seq) ? -1 : ((lhs->seq > rhs->seq) ? 1 : 0); + } else { + return lhs->pri-rhs->pri; + } +} + +static void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe) +{ + iframe->state = SPDYLAY_RECV_HEAD; + iframe->len = iframe->off = 0; + iframe->headbufoff = 0; +} + +static int spdylay_session_new(spdylay_session **session_ptr, + uint16_t version, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + if(version != SPDYLAY_PROTO_SPDY2 && version != SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + *session_ptr = malloc(sizeof(spdylay_session)); + if(*session_ptr == NULL) { + r = SPDYLAY_ERR_NOMEM; + goto fail_session; + } + memset(*session_ptr, 0, sizeof(spdylay_session)); + + (*session_ptr)->version = version; + + /* next_stream_id, last_recv_stream_id and next_unique_id are + initialized in either spdylay_session_client_new or + spdylay_session_server_new */ + + (*session_ptr)->flow_control = + (*session_ptr)->version == SPDYLAY_PROTO_SPDY3; + + (*session_ptr)->last_ping_unique_id = 0; + + (*session_ptr)->next_seq = 0; + + (*session_ptr)->goaway_flags = SPDYLAY_GOAWAY_NONE; + (*session_ptr)->last_good_stream_id = 0; + + r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater, + (*session_ptr)->version); + if(r != 0) { + goto fail_hd_deflater; + } + r = spdylay_zlib_inflate_hd_init(&(*session_ptr)->hd_inflater, + (*session_ptr)->version); + if(r != 0) { + goto fail_hd_inflater; + } + spdylay_map_init(&(*session_ptr)->streams); + r = spdylay_pq_init(&(*session_ptr)->ob_pq, spdylay_outbound_item_compar); + if(r != 0) { + goto fail_ob_pq; + } + r = spdylay_pq_init(&(*session_ptr)->ob_ss_pq, spdylay_outbound_item_compar); + if(r != 0) { + goto fail_ob_ss_pq; + } + + (*session_ptr)->aob.framebuf = malloc + (SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH); + if((*session_ptr)->aob.framebuf == NULL) { + r = SPDYLAY_ERR_NOMEM; + goto fail_aob_framebuf; + } + (*session_ptr)->aob.framebufmax = SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH; + + (*session_ptr)->nvbuf = malloc(SPDYLAY_INITIAL_NV_BUFFER_LENGTH); + if((*session_ptr)->nvbuf == NULL) { + r = SPDYLAY_ERR_NOMEM; + goto fail_nvbuf; + } + (*session_ptr)->nvbuflen = SPDYLAY_INITIAL_NV_BUFFER_LENGTH; + + spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096); + + memset((*session_ptr)->remote_settings, 0, + sizeof((*session_ptr)->remote_settings)); + (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = + SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS; + (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = + SPDYLAY_INITIAL_WINDOW_SIZE; + + memset((*session_ptr)->local_settings, 0, + sizeof((*session_ptr)->local_settings)); + (*session_ptr)->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = + SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS; + (*session_ptr)->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = + SPDYLAY_INITIAL_WINDOW_SIZE; + + (*session_ptr)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + (*session_ptr)->iframe.buf = malloc(SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH); + if((*session_ptr)->iframe.buf == NULL) { + r = SPDYLAY_ERR_NOMEM; + goto fail_iframe_buf; + } + (*session_ptr)->iframe.bufmax = SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH; + spdylay_inbound_frame_reset(&(*session_ptr)->iframe); + + return 0; + + fail_iframe_buf: + free((*session_ptr)->nvbuf); + fail_nvbuf: + free((*session_ptr)->aob.framebuf); + fail_aob_framebuf: + spdylay_pq_free(&(*session_ptr)->ob_ss_pq); + fail_ob_ss_pq: + spdylay_pq_free(&(*session_ptr)->ob_pq); + fail_ob_pq: + spdylay_map_free(&(*session_ptr)->streams); + spdylay_zlib_inflate_free(&(*session_ptr)->hd_inflater); + fail_hd_inflater: + spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater); + fail_hd_deflater: + free(*session_ptr); + fail_session: + return r; +} + +int spdylay_session_client_new(spdylay_session **session_ptr, + uint16_t version, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + r = spdylay_session_new(session_ptr, version, callbacks, user_data); + if(r == 0) { + /* IDs for use in client */ + (*session_ptr)->next_stream_id = 1; + (*session_ptr)->last_recv_stream_id = 0; + (*session_ptr)->next_unique_id = 1; + } + return r; +} + +int spdylay_session_server_new(spdylay_session **session_ptr, + uint16_t version, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + r = spdylay_session_new(session_ptr, version, callbacks, user_data); + if(r == 0) { + (*session_ptr)->server = 1; + /* IDs for use in client */ + (*session_ptr)->next_stream_id = 2; + (*session_ptr)->last_recv_stream_id = 0; + (*session_ptr)->next_unique_id = 2; + } + return r; +} + +static void spdylay_free_streams(key_type key, void *val, void *ptr) +{ + spdylay_stream_free((spdylay_stream*)val); + free(val); +} + +static void spdylay_session_ob_pq_free(spdylay_pq *pq) +{ + while(!spdylay_pq_empty(pq)) { + spdylay_outbound_item *item = (spdylay_outbound_item*)spdylay_pq_top(pq); + spdylay_outbound_item_free(item); + free(item); + spdylay_pq_pop(pq); + } + spdylay_pq_free(pq); +} + +static void spdylay_active_outbound_item_reset +(spdylay_active_outbound_item *aob) +{ + spdylay_outbound_item_free(aob->item); + free(aob->item); + aob->item = NULL; + aob->framebuflen = aob->framebufoff = 0; +} + +void spdylay_session_del(spdylay_session *session) +{ + if(session == NULL) { + return; + } + spdylay_map_each(&session->streams, spdylay_free_streams, NULL); + spdylay_map_free(&session->streams); + spdylay_session_ob_pq_free(&session->ob_pq); + spdylay_session_ob_pq_free(&session->ob_ss_pq); + spdylay_zlib_deflate_free(&session->hd_deflater); + spdylay_zlib_inflate_free(&session->hd_inflater); + spdylay_active_outbound_item_reset(&session->aob); + free(session->aob.framebuf); + free(session->nvbuf); + spdylay_buffer_free(&session->inflatebuf); + free(session->iframe.buf); + free(session); +} + +int spdylay_session_add_frame(spdylay_session *session, + spdylay_frame_type frame_type, + spdylay_frame *frame, + void *aux_data) +{ + int r; + spdylay_outbound_item *item; + item = malloc(sizeof(spdylay_outbound_item)); + if(item == NULL) { + return SPDYLAY_ERR_NOMEM; + } + item->frame_type = frame_type; + item->frame = frame; + item->aux_data = aux_data; + item->seq = session->next_seq++; + /* Set priority lowest at the moment. */ + item->pri = spdylay_session_get_pri_lowest(session); + switch(frame_type) { + case SPDYLAY_SYN_STREAM: + item->pri = frame->syn_stream.pri; + break; + case SPDYLAY_SYN_REPLY: { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->syn_reply.stream_id); + if(stream) { + item->pri = stream->pri; + } + break; + } + case SPDYLAY_RST_STREAM: { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->rst_stream.stream_id); + if(stream) { + stream->state = SPDYLAY_STREAM_CLOSING; + item->pri = stream->pri; + } + break; + } + case SPDYLAY_SETTINGS: + /* Should SPDYLAY_SETTINGS have higher priority? */ + item->pri = -1; + break; + case SPDYLAY_NOOP: + /* We don't have any public API to add NOOP, so here is + unreachable. */ + abort(); + case SPDYLAY_PING: + /* Ping has "height" priority. Give it -1. */ + item->pri = -1; + break; + case SPDYLAY_GOAWAY: + /* Should GOAWAY have higher priority? */ + break; + case SPDYLAY_HEADERS: { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->headers.stream_id); + if(stream) { + item->pri = stream->pri; + } + break; + } + case SPDYLAY_WINDOW_UPDATE: { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->window_update.stream_id); + if(stream) { + item->pri = stream->pri; + } + break; + } + case SPDYLAY_DATA: { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->data.stream_id); + if(stream) { + item->pri = stream->pri; + } + break; + } + }; + if(frame_type == SPDYLAY_SYN_STREAM) { + r = spdylay_pq_push(&session->ob_ss_pq, item); + } else { + r = spdylay_pq_push(&session->ob_pq, item); + } + if(r != 0) { + free(item); + return r; + } + return 0; +} + +int spdylay_session_add_rst_stream(spdylay_session *session, + int32_t stream_id, uint32_t status_code) +{ + int r; + spdylay_frame *frame; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_rst_stream_init(&frame->rst_stream, session->version, + stream_id, status_code); + r = spdylay_session_add_frame(session, SPDYLAY_RST_STREAM, frame, NULL); + if(r != 0) { + spdylay_frame_rst_stream_free(&frame->rst_stream); + free(frame); + return r; + } + return 0; +} + +spdylay_stream* spdylay_session_open_stream(spdylay_session *session, + int32_t stream_id, + uint8_t flags, uint8_t pri, + spdylay_stream_state initial_state, + void *stream_user_data) +{ + int r; + spdylay_stream *stream = malloc(sizeof(spdylay_stream)); + if(stream == NULL) { + return NULL; + } + spdylay_stream_init(stream, stream_id, flags, pri, initial_state, + session->remote_settings + [SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE], + stream_user_data); + r = spdylay_map_insert(&session->streams, stream_id, stream); + if(r != 0) { + free(stream); + stream = NULL; + } + return stream; +} + +int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + if(stream->state != SPDYLAY_STREAM_INITIAL && + session->callbacks.on_stream_close_callback) { + session->callbacks.on_stream_close_callback(session, stream_id, + status_code, + session->user_data); + } + spdylay_map_erase(&session->streams, stream_id); + spdylay_stream_free(stream); + free(stream); + return 0; + } else { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } +} + +int spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session, + spdylay_stream *stream) +{ + if((stream->shut_flags & SPDYLAY_SHUT_RDWR) == SPDYLAY_SHUT_RDWR) { + return spdylay_session_close_stream(session, stream->stream_id, + SPDYLAY_OK); + } else { + return 0; + } +} + +void spdylay_session_close_pushed_streams(spdylay_session *session, + int32_t stream_id, + spdylay_status_code status_code) +{ + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + int i; + for(i = 0; i < stream->pushed_streams_length; ++i) { + spdylay_session_close_stream(session, stream->pushed_streams[i], + status_code); + } + } +} + +static int spdylay_predicate_stream_for_send(spdylay_stream *stream) +{ + if(stream == NULL) { + return SPDYLAY_ERR_STREAM_CLOSED; + } else if(stream->shut_flags & SPDYLAY_SHUT_WR) { + return SPDYLAY_ERR_STREAM_SHUT_WR; + } else { + return 0; + } +} + +/* + * This function checks SYN_STREAM frame |frame| can be sent at this + * time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The Associated-To-Stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ +static int spdylay_session_predicate_syn_stream_send +(spdylay_session *session, + spdylay_syn_stream *frame) +{ + if(session->goaway_flags) { + /* When GOAWAY is sent or received, peer must not send new + SYN_STREAM. */ + return SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED; + } + /* All 32bit signed stream IDs are spent. */ + if(session->next_stream_id > INT32_MAX) { + return SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE; + } + if(frame->assoc_stream_id != 0) { + /* Check associated stream is active. */ + /* We assume here that if frame->assoc_stream_id != 0, + session->server is always 1 and frame->assoc_stream_id is + odd. */ + if(spdylay_session_get_stream(session, frame->assoc_stream_id) == + NULL) { + return SPDYLAY_ERR_STREAM_CLOSED; + } + } + return 0; +} + +/* + * This function checks SYN_REPLY with the stream ID |stream_id| can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with FIN flag set has already sent) + * SPDYLAY_ERR_INVALID_STREAM_ID + * The stream ID is invalid. + * SPDYLAY_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * SPDYLAY_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid (e.g., SYN_REPLY has + * already sent). + */ +static int spdylay_session_predicate_syn_reply_send(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + int r; + r = spdylay_predicate_stream_for_send(stream); + if(r != 0) { + return r; + } + if(spdylay_session_is_my_stream_id(session, stream_id)) { + return SPDYLAY_ERR_INVALID_STREAM_ID; + } else { + if(stream->state == SPDYLAY_STREAM_OPENING) { + return 0; + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + return SPDYLAY_ERR_STREAM_CLOSING; + } else { + return SPDYLAY_ERR_INVALID_STREAM_STATE; + } + } +} + +/* + * This function checks HEADERS with the stream ID |stream_id| can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with FIN flag set has already sent) + * SPDYLAY_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * SPDYLAY_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid (e.g., if the local peer + * is receiving side and SYN_REPLY has not been sent). + */ +static int spdylay_session_predicate_headers_send(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + int r; + r = spdylay_predicate_stream_for_send(stream); + if(r != 0) { + return r; + } + if(spdylay_session_is_my_stream_id(session, stream_id)) { + if(stream->state != SPDYLAY_STREAM_CLOSING) { + return 0; + } else { + return SPDYLAY_ERR_STREAM_CLOSING; + } + } else { + if(stream->state == SPDYLAY_STREAM_OPENED) { + return 0; + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + return SPDYLAY_ERR_STREAM_CLOSING; + } else { + return SPDYLAY_ERR_INVALID_STREAM_STATE; + } + } +} + +/* + * This function checks WINDOW_UPDATE with the stream ID |stream_id| + * can be sent at this time. Note that FIN flag of the previous frame + * does not affect the transmission of the WINDOW_UPDATE frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + */ +static int spdylay_session_predicate_window_update_send +(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + if(stream == NULL) { + return SPDYLAY_ERR_STREAM_CLOSED; + } + if(stream->state != SPDYLAY_STREAM_CLOSING) { + return 0; + } else { + return SPDYLAY_ERR_STREAM_CLOSING; + } +} + +/* + * Returns the maximum length of next data read. If the flow control + * is enabled, the return value takes into account the current window + * size. + */ +static size_t spdylay_session_next_data_read(spdylay_session *session, + spdylay_stream *stream) +{ + if(session->flow_control == 0) { + return SPDYLAY_DATA_PAYLOAD_LENGTH; + } else if(stream->window_size > 0) { + return stream->window_size < SPDYLAY_DATA_PAYLOAD_LENGTH ? + stream->window_size : SPDYLAY_DATA_PAYLOAD_LENGTH; + } else { + return 0; + } +} + +/* + * This function checks DATA with the stream ID |stream_id| can be + * sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with FIN flag set has already sent) + * SPDYLAY_ERR_DEFERRED_DATA_EXIST + * Another DATA frame has already been deferred. + * SPDYLAY_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * SPDYLAY_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid (e.g., if the local peer + * is receiving side and SYN_REPLY has not been sent). + */ +static int spdylay_session_predicate_data_send(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + int r; + r = spdylay_predicate_stream_for_send(stream); + if(r != 0) { + return r; + } + if(stream->deferred_data != NULL) { + /* stream->deferred_data != NULL means previously queued DATA + frame has not been sent. We don't allow new DATA frame is sent + in this case. */ + return SPDYLAY_ERR_DEFERRED_DATA_EXIST; + } + if(spdylay_session_is_my_stream_id(session, stream_id)) { + /* If stream->state is SPDYLAY_STREAM_CLOSING, RST_STREAM was + queued but not yet sent. In this case, we won't send DATA + frames. This is because in the current architecture, DATA and + RST_STREAM in the same stream have same priority and DATA is + small seq number. So RST_STREAM will not be sent until all DATA + frames are sent. This is not desirable situation; we want to + close stream as soon as possible. To achieve this, we remove + DATA frame before RST_STREAM. */ + if(stream->state != SPDYLAY_STREAM_CLOSING) { + return 0; + } else { + return SPDYLAY_ERR_STREAM_CLOSING; + } + } else { + if(stream->state == SPDYLAY_STREAM_OPENED) { + return 0; + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + return SPDYLAY_ERR_STREAM_CLOSING; + } else { + return SPDYLAY_ERR_INVALID_STREAM_STATE; + } + } +} + +static ssize_t spdylay_session_prep_frame(spdylay_session *session, + spdylay_outbound_item *item) +{ + ssize_t framebuflen; + switch(item->frame_type) { + case SPDYLAY_SYN_STREAM: { + int32_t stream_id; + spdylay_syn_stream_aux_data *aux_data; + int r; + r = spdylay_session_predicate_syn_stream_send(session, + &item->frame->syn_stream); + if(r != 0) { + return r; + } + stream_id = session->next_stream_id; + + item->frame->syn_stream.stream_id = stream_id; + session->next_stream_id += 2; + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->syn_stream.nv); + spdylay_frame_nv_sort(item->frame->syn_stream.nv); + } + framebuflen = spdylay_frame_pack_syn_stream(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, + &item->frame->syn_stream, + &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->syn_stream.nv); + spdylay_frame_nv_sort(item->frame->syn_stream.nv); + } + if(framebuflen < 0) { + return framebuflen; + } + aux_data = (spdylay_syn_stream_aux_data*)item->aux_data; + if(spdylay_session_open_stream(session, stream_id, + item->frame->syn_stream.hd.flags, + item->frame->syn_stream.pri, + SPDYLAY_STREAM_INITIAL, + aux_data->stream_user_data) == NULL) { + return SPDYLAY_ERR_NOMEM; + } + break; + } + case SPDYLAY_SYN_REPLY: { + int r; + r = spdylay_session_predicate_syn_reply_send + (session, item->frame->syn_reply.stream_id); + if(r != 0) { + return r; + } + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->syn_reply.nv); + spdylay_frame_nv_sort(item->frame->syn_reply.nv); + } + framebuflen = spdylay_frame_pack_syn_reply(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, + &item->frame->syn_reply, + &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->syn_reply.nv); + spdylay_frame_nv_sort(item->frame->syn_reply.nv); + } + if(framebuflen < 0) { + return framebuflen; + } + break; + } + case SPDYLAY_RST_STREAM: + framebuflen = spdylay_frame_pack_rst_stream(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->rst_stream); + if(framebuflen < 0) { + return framebuflen; + } + break; + case SPDYLAY_SETTINGS: + framebuflen = spdylay_frame_pack_settings(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->settings); + if(framebuflen < 0) { + return framebuflen; + } + break; + case SPDYLAY_NOOP: + /* We don't have any public API to add NOOP, so here is + unreachable. */ + abort(); + case SPDYLAY_PING: + framebuflen = spdylay_frame_pack_ping(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->ping); + if(framebuflen < 0) { + return framebuflen; + } + break; + case SPDYLAY_HEADERS: { + int r; + r = spdylay_session_predicate_headers_send(session, + item->frame->headers.stream_id); + if(r != 0) { + return r; + } + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->headers.nv); + spdylay_frame_nv_sort(item->frame->headers.nv); + } + framebuflen = spdylay_frame_pack_headers(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, + &item->frame->headers, + &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->headers.nv); + spdylay_frame_nv_sort(item->frame->headers.nv); + } + if(framebuflen < 0) { + return framebuflen; + } + break; + } + case SPDYLAY_WINDOW_UPDATE: { + int r; + r = spdylay_session_predicate_window_update_send + (session, item->frame->window_update.stream_id); + if(r != 0) { + return r; + } + framebuflen = spdylay_frame_pack_window_update(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->window_update); + if(framebuflen < 0) { + return framebuflen; + } + break; + } + case SPDYLAY_GOAWAY: + if(session->goaway_flags & SPDYLAY_GOAWAY_SEND) { + /* TODO The spec does not mandate that both endpoints have to + exchange GOAWAY. This implementation allows receiver of first + GOAWAY can sent its own GOAWAY to tell the remote peer that + last-good-stream-id. */ + return SPDYLAY_ERR_GOAWAY_ALREADY_SENT; + } + framebuflen = spdylay_frame_pack_goaway(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->goaway); + if(framebuflen < 0) { + return framebuflen; + } + break; + case SPDYLAY_DATA: { + size_t next_readmax; + spdylay_stream *stream; + int r; + r = spdylay_session_predicate_data_send(session, + item->frame->data.stream_id); + if(r != 0) { + return r; + } + stream = spdylay_session_get_stream(session, item->frame->data.stream_id); + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = spdylay_session_next_data_read(session, stream); + if(next_readmax == 0) { + spdylay_stream_defer_data(stream, item, SPDYLAY_DEFERRED_FLOW_CONTROL); + return SPDYLAY_ERR_DEFERRED; + } + framebuflen = spdylay_session_pack_data(session, + &session->aob.framebuf, + &session->aob.framebufmax, + next_readmax, + &item->frame->data); + if(framebuflen == SPDYLAY_ERR_DEFERRED) { + spdylay_stream_defer_data(stream, item, SPDYLAY_DEFERRED_NONE); + return SPDYLAY_ERR_DEFERRED; + } else if(framebuflen < 0) { + return framebuflen; + } + break; + } + default: + framebuflen = SPDYLAY_ERR_INVALID_ARGUMENT; + } + return framebuflen; +} + +spdylay_outbound_item* spdylay_session_get_ob_pq_top +(spdylay_session *session) +{ + return (spdylay_outbound_item*)spdylay_pq_top(&session->ob_pq); +} + +spdylay_outbound_item* spdylay_session_get_next_ob_item +(spdylay_session *session) +{ + if(spdylay_pq_empty(&session->ob_pq)) { + if(spdylay_pq_empty(&session->ob_ss_pq)) { + return NULL; + } else { + /* Return item only when concurrent connection limit is not + reached */ + if(spdylay_session_get_max_concurrent_streams_reached(session)) { + return NULL; + } else { + return spdylay_pq_top(&session->ob_ss_pq); + } + } + } else { + if(spdylay_pq_empty(&session->ob_ss_pq)) { + return spdylay_pq_top(&session->ob_pq); + } else { + spdylay_outbound_item *item, *syn_stream_item; + item = spdylay_pq_top(&session->ob_pq); + syn_stream_item = spdylay_pq_top(&session->ob_ss_pq); + if(spdylay_session_get_max_concurrent_streams_reached(session) || + item->pri < syn_stream_item->pri || + (item->pri == syn_stream_item->pri && + item->seq < syn_stream_item->seq)) { + return item; + } else { + return syn_stream_item; + } + } + } +} + +spdylay_outbound_item* spdylay_session_pop_next_ob_item +(spdylay_session *session) +{ + if(spdylay_pq_empty(&session->ob_pq)) { + if(spdylay_pq_empty(&session->ob_ss_pq)) { + return NULL; + } else { + /* Pop item only when concurrent connection limit is not + reached */ + if(spdylay_session_get_max_concurrent_streams_reached(session)) { + return NULL; + } else { + spdylay_outbound_item *item; + item = spdylay_pq_top(&session->ob_ss_pq); + spdylay_pq_pop(&session->ob_ss_pq); + return item; + } + } + } else { + if(spdylay_pq_empty(&session->ob_ss_pq)) { + spdylay_outbound_item *item; + item = spdylay_pq_top(&session->ob_pq); + spdylay_pq_pop(&session->ob_pq); + return item; + } else { + spdylay_outbound_item *item, *syn_stream_item; + item = spdylay_pq_top(&session->ob_pq); + syn_stream_item = spdylay_pq_top(&session->ob_ss_pq); + if(spdylay_session_get_max_concurrent_streams_reached(session) || + item->pri < syn_stream_item->pri || + (item->pri == syn_stream_item->pri && + item->seq < syn_stream_item->seq)) { + spdylay_pq_pop(&session->ob_pq); + return item; + } else { + spdylay_pq_pop(&session->ob_ss_pq); + return syn_stream_item; + } + } + } +} + +/* + * Called after a frame is sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + * SPDYLAY_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int spdylay_session_after_frame_sent(spdylay_session *session) +{ + /* TODO handle FIN flag. */ + spdylay_outbound_item *item = session->aob.item; + spdylay_frame *frame = session->aob.item->frame; + spdylay_frame_type type = session->aob.item->frame_type; + if(type == SPDYLAY_DATA) { + if(session->callbacks.on_data_send_callback) { + session->callbacks.on_data_send_callback + (session, + frame->data.eof ? frame->data.flags : + (frame->data.flags & (~SPDYLAY_DATA_FLAG_FIN)), + frame->data.stream_id, + session->aob.framebuflen-SPDYLAY_HEAD_LEN, session->user_data); + } + } else { + if(session->callbacks.on_ctrl_send_callback) { + session->callbacks.on_ctrl_send_callback + (session, type, frame, session->user_data); + } + } + switch(type) { + case SPDYLAY_SYN_STREAM: { + spdylay_stream *stream = + spdylay_session_get_stream(session, frame->syn_stream.stream_id); + if(stream) { + spdylay_syn_stream_aux_data *aux_data; + stream->state = SPDYLAY_STREAM_OPENING; + if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR); + } + if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + } + spdylay_session_close_stream_if_shut_rdwr(session, stream); + /* We assume aux_data is a pointer to spdylay_syn_stream_aux_data */ + aux_data = (spdylay_syn_stream_aux_data*)item->aux_data; + if(aux_data->data_prd) { + int r; + /* spdylay_submit_data() makes a copy of aux_data->data_prd */ + r = spdylay_submit_data(session, frame->syn_stream.stream_id, + SPDYLAY_DATA_FLAG_FIN, aux_data->data_prd); + if(r != 0) { + /* FATAL error */ + assert(r < SPDYLAY_ERR_FATAL); + /* TODO If r is not FATAL, we should send RST_STREAM. */ + return r; + } + } + } + break; + } + case SPDYLAY_SYN_REPLY: { + spdylay_stream *stream = + spdylay_session_get_stream(session, frame->syn_reply.stream_id); + if(stream) { + stream->state = SPDYLAY_STREAM_OPENED; + if(frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR); + } + spdylay_session_close_stream_if_shut_rdwr(session, stream); + if(item->aux_data) { + /* We assume aux_data is a pointer to spdylay_data_provider */ + spdylay_data_provider *data_prd = + (spdylay_data_provider*)item->aux_data; + int r; + r = spdylay_submit_data(session, frame->syn_reply.stream_id, + SPDYLAY_DATA_FLAG_FIN, data_prd); + if(r != 0) { + /* FATAL error */ + assert(r < SPDYLAY_ERR_FATAL); + /* TODO If r is not FATAL, we should send RST_STREAM. */ + return r; + } + } + } + break; + } + case SPDYLAY_RST_STREAM: + if(!session->server && + spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) && + frame->rst_stream.status_code == SPDYLAY_CANCEL) { + spdylay_session_close_pushed_streams(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + } + spdylay_session_close_stream(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + break; + case SPDYLAY_SETTINGS: + /* nothing to do */ + break; + case SPDYLAY_NOOP: + /* We don't have any public API to add NOOP, so here is + unreachable. */ + abort(); + case SPDYLAY_PING: + /* We record the time now and show application code RTT when + reply PING is received. */ + session->last_ping_unique_id = frame->ping.unique_id; + break; + case SPDYLAY_GOAWAY: + session->goaway_flags |= SPDYLAY_GOAWAY_SEND; + break; + case SPDYLAY_HEADERS: { + spdylay_stream *stream = + spdylay_session_get_stream(session, frame->headers.stream_id); + if(stream) { + if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR); + } + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + break; + } + case SPDYLAY_WINDOW_UPDATE: + break; + case SPDYLAY_DATA: + if(frame->data.eof && (frame->data.flags & SPDYLAY_DATA_FLAG_FIN)) { + spdylay_stream *stream = + spdylay_session_get_stream(session, frame->data.stream_id); + if(stream) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR); + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + } + break; + }; + if(type == SPDYLAY_DATA) { + int r; + /* If session is closed or RST_STREAM was queued, we won't send + further data. */ + if(frame->data.eof || + spdylay_session_predicate_data_send(session, + frame->data.stream_id) != 0) { + spdylay_active_outbound_item_reset(&session->aob); + } else { + spdylay_outbound_item* item = spdylay_session_get_next_ob_item(session); + /* If priority of this stream is higher or equal to other stream + waiting at the top of the queue, we continue to send this + data. */ + if(item == NULL || session->aob.item->pri <= item->pri) { + size_t next_readmax; + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, frame->data.stream_id); + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = spdylay_session_next_data_read(session, stream); + if(next_readmax == 0) { + spdylay_stream_defer_data(stream, session->aob.item, + SPDYLAY_DEFERRED_FLOW_CONTROL); + session->aob.item = NULL; + spdylay_active_outbound_item_reset(&session->aob); + return 0; + } + r = spdylay_session_pack_data(session, + &session->aob.framebuf, + &session->aob.framebufmax, + next_readmax, + &frame->data); + if(r == SPDYLAY_ERR_DEFERRED) { + spdylay_stream_defer_data(stream, session->aob.item, + SPDYLAY_DEFERRED_NONE); + session->aob.item = NULL; + spdylay_active_outbound_item_reset(&session->aob); + } else if(r < 0) { + /* We don't return other error code other than + SPDYLAY_ERR_CALLBACK_FAILURE here. */ + spdylay_active_outbound_item_reset(&session->aob); + return SPDYLAY_ERR_CALLBACK_FAILURE; + } else { + session->aob.framebuflen = r; + session->aob.framebufoff = 0; + } + } else { + r = spdylay_pq_push(&session->ob_pq, session->aob.item); + if(r == 0) { + session->aob.item = NULL; + spdylay_active_outbound_item_reset(&session->aob); + } else { + /* FATAL error */ + assert(r < SPDYLAY_ERR_FATAL); + spdylay_active_outbound_item_reset(&session->aob); + return r; + } + } + } + } else { + spdylay_active_outbound_item_reset(&session->aob); + } + return 0; +} + +int spdylay_session_send(spdylay_session *session) +{ + int r; + while(1) { + const uint8_t *data; + size_t datalen; + ssize_t sentlen; + if(session->aob.item == NULL) { + spdylay_outbound_item *item; + ssize_t framebuflen; + item = spdylay_session_pop_next_ob_item(session); + if(item == NULL) { + break; + } + framebuflen = spdylay_session_prep_frame(session, item); + if(framebuflen == SPDYLAY_ERR_DEFERRED) { + continue; + } else if(framebuflen < 0) { + /* The library is responsible for the transmission of + WINDOW_UPDATE frame, so we don't call error callback for + it. */ + if(session->callbacks.on_ctrl_not_send_callback && + spdylay_is_non_fatal(framebuflen) && + item->frame_type != SPDYLAY_WINDOW_UPDATE) { + session->callbacks.on_ctrl_not_send_callback(session, + item->frame_type, + item->frame, + framebuflen, + session->user_data); + } + spdylay_outbound_item_free(item); + free(item); + if(spdylay_is_fatal(framebuflen)) { + return framebuflen; + } else { + continue; + } + } + session->aob.item = item; + session->aob.framebuflen = framebuflen; + /* Call before_send callback */ + if(item->frame_type != SPDYLAY_DATA && + session->callbacks.before_ctrl_send_callback) { + session->callbacks.before_ctrl_send_callback + (session, item->frame_type, item->frame, session->user_data); + } + } + data = session->aob.framebuf + session->aob.framebufoff; + datalen = session->aob.framebuflen - session->aob.framebufoff; + sentlen = session->callbacks.send_callback(session, data, datalen, 0, + session->user_data); + if(sentlen < 0) { + if(sentlen == SPDYLAY_ERR_WOULDBLOCK) { + return 0; + } else { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + } else { + session->aob.framebufoff += sentlen; + if(session->flow_control && + session->aob.item->frame_type == SPDYLAY_DATA) { + spdylay_frame *frame; + spdylay_stream *stream; + frame = session->aob.item->frame; + stream = spdylay_session_get_stream(session, frame->data.stream_id); + if(stream) { + stream->window_size -= spdylay_get_uint32(&session->aob.framebuf[4]); + } + } + if(session->aob.framebufoff == session->aob.framebuflen) { + /* Frame has completely sent */ + r = spdylay_session_after_frame_sent(session); + if(r < 0) { + /* FATAL */ + assert(r < SPDYLAY_ERR_FATAL); + return r; + } + } + } + } + return 0; +} + +static ssize_t spdylay_recv(spdylay_session *session, uint8_t *buf, size_t len) +{ + ssize_t r; + r = session->callbacks.recv_callback + (session, buf, len, 0, session->user_data); + if(r > 0) { + if(r > len) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + } else if(r < 0) { + if(r != SPDYLAY_ERR_WOULDBLOCK) { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + return r; +} + +static void spdylay_session_call_on_request_recv +(spdylay_session *session, int32_t stream_id) +{ + if(session->callbacks.on_request_recv_callback) { + session->callbacks.on_request_recv_callback(session, stream_id, + session->user_data); + } +} + +static void spdylay_session_call_on_ctrl_frame_received +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame) +{ + if(session->callbacks.on_ctrl_recv_callback) { + session->callbacks.on_ctrl_recv_callback + (session, type, frame, session->user_data); + } +} + +/* + * Checks whether received stream_id is valid. + * This function returns 1 if it succeeds, or 0. + */ +static int spdylay_session_is_new_peer_stream_id(spdylay_session *session, + int32_t stream_id) +{ + if(stream_id == 0) { + return 0; + } + if(session->server) { + return stream_id % 2 == 1 && session->last_recv_stream_id < stream_id; + } else { + return stream_id % 2 == 0 && session->last_recv_stream_id < stream_id; + } +} + +/* + * Returns non-zero iff version == session->version + */ +static int spdylay_session_check_version(spdylay_session *session, + uint16_t version) +{ + return session->version == version; +} + +/* + * Returns non-zero iff name/value pairs |nv| are good shape. + * Currently, we only checks whether names are lower cased. The spdy/2 + * spec requires that names must be lower cased. + */ +static int spdylay_session_check_nv(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + int j; + for(j = 0; nv[i][j] != '\0'; ++j) { + if('A' <= nv[i][j] && nv[i][j] <= 'Z') { + return 0; + } + } + } + return 1; +} + +/* + * Validates SYN_STREAM frame |frame|. This function returns 0 if it + * succeeds, or non-zero spdylay_status_code. + */ +static int spdylay_session_validate_syn_stream(spdylay_session *session, + spdylay_syn_stream *frame) +{ + if(!spdylay_session_check_version(session, frame->hd.version)) { + return SPDYLAY_UNSUPPORTED_VERSION; + } + if(session->server) { + if(frame->assoc_stream_id != 0) { + return SPDYLAY_PROTOCOL_ERROR; + } + } else { + if(frame->assoc_stream_id == 0) { + /* spdy/2 spec: When a client receives a SYN_STREAM from the + server with an Associated-To-Stream-ID of 0, it must reply with + a RST_STREAM with error code INVALID_STREAM. */ + return SPDYLAY_INVALID_STREAM; + } + if((frame->hd.flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) == 0 || + frame->assoc_stream_id % 2 == 0 || + spdylay_session_get_stream(session, frame->assoc_stream_id) == NULL) { + /* It seems spdy/2 spec does not say which status code should be + returned in these cases. */ + return SPDYLAY_PROTOCOL_ERROR; + } + } + if(spdylay_session_get_max_concurrent_streams_reached(session)) { + /* spdy/2 spec does not clearly say what to do when max concurrent + streams number is reached. The mod_spdy sends + SPDYLAY_REFUSED_STREAM and we think it is reasonable. So we + follow it. */ + return SPDYLAY_REFUSED_STREAM; + } + if(!spdylay_session_check_nv(frame->nv)) { + return SPDYLAY_PROTOCOL_ERROR; + } + return 0; +} + + +static int spdylay_session_handle_invalid_stream +(spdylay_session *session, + int32_t stream_id, + spdylay_frame_type type, + spdylay_frame *frame, + spdylay_status_code status_code) +{ + int r; + r = spdylay_session_add_rst_stream(session, stream_id, status_code); + if(r != 0) { + return r; + } + if(session->callbacks.on_invalid_ctrl_recv_callback) { + session->callbacks.on_invalid_ctrl_recv_callback + (session, type, frame, session->user_data); + } + return 0; +} + +int spdylay_session_on_syn_stream_received(spdylay_session *session, + spdylay_frame *frame) +{ + int r = 0; + int status_code; + if(session->goaway_flags) { + /* We don't accept SYN_STREAM after GOAWAY is sent or received. */ + return 0; + } + if(session->last_recv_stream_id == frame->syn_stream.stream_id) { + /* SPDY/3 spec says if an endpoint receives same stream ID twice, + it MUST issue a stream error with status code + PROTOCOL_ERROR. */ + status_code = SPDYLAY_PROTOCOL_ERROR; + } else if(!spdylay_session_is_new_peer_stream_id + (session, frame->syn_stream.stream_id)) { + /* SPDY/3 spec says if an endpoint receives a SYN_STREAM with a + stream ID which is less than any previously received + SYN_STREAM, it MUST issue a session error with status + PROTOCOL_ERROR */ + if(session->callbacks.on_invalid_ctrl_recv_callback) { + session->callbacks.on_invalid_ctrl_recv_callback(session, + SPDYLAY_SYN_STREAM, + frame, + session->user_data); + } + return spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } else { + session->last_recv_stream_id = frame->syn_stream.stream_id; + status_code = spdylay_session_validate_syn_stream(session, + &frame->syn_stream); + } + if(status_code == 0) { + uint8_t flags = frame->syn_stream.hd.flags; + if((flags & SPDYLAY_CTRL_FLAG_FIN) && + (flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL)) { + /* If the stream is UNIDIRECTIONAL and FIN bit set, we can close + stream upon receiving SYN_STREAM. So, the stream needs not to + be opened. */ + } else { + spdylay_stream *stream; + stream = spdylay_session_open_stream(session, frame->syn_stream.stream_id, + frame->syn_stream.hd.flags, + frame->syn_stream.pri, + SPDYLAY_STREAM_OPENING, + NULL); + if(stream) { + if(flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + } + if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR); + } + /* We don't call spdylay_session_close_stream_if_shut_rdwr() + here because either SPDYLAY_CTRL_FLAG_FIN or + SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL is not set here. */ + } + } + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_STREAM, + frame); + if(flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, + frame->syn_stream.stream_id); + if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) { + /* Note that we call on_stream_close_callback without opening + stream. */ + if(session->callbacks.on_stream_close_callback) { + session->callbacks.on_stream_close_callback + (session, frame->syn_stream.stream_id, SPDYLAY_OK, + session->user_data); + } + } + } + } else { + r = spdylay_session_handle_invalid_stream + (session, frame->syn_stream.stream_id, SPDYLAY_SYN_STREAM, frame, + status_code); + } + return r; +} + +int spdylay_session_on_syn_reply_received(spdylay_session *session, + spdylay_frame *frame) +{ + int r = 0; + int valid = 0; + int status_code = SPDYLAY_PROTOCOL_ERROR; + spdylay_stream *stream; + if(!spdylay_session_check_version(session, frame->syn_reply.hd.version)) { + return 0; + } + if((stream = spdylay_session_get_stream(session, + frame->syn_reply.stream_id)) && + (stream->shut_flags & SPDYLAY_SHUT_RD) == 0 && + spdylay_session_check_nv(frame->syn_reply.nv)) { + if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) { + if(stream->state == SPDYLAY_STREAM_OPENING) { + valid = 1; + stream->state = SPDYLAY_STREAM_OPENED; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_REPLY, + frame); + if(frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + /* This is the last frame of this stream, so disallow + further receptions. */ + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + /* This is race condition. SPDYLAY_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + valid = 1; + } else { + if(session->version == SPDYLAY_PROTO_SPDY3) { + /* SPDY/3 spec says if multiple SYN_REPLY frames for the + same active stream ID are received, the receiver must + issue a stream error with the status code + STREAM_IN_USE. */ + status_code = SPDYLAY_STREAM_IN_USE; + } + } + } + } + if(!valid) { + r = spdylay_session_handle_invalid_stream + (session, frame->syn_reply.stream_id, SPDYLAY_SYN_REPLY, frame, + status_code); + } + return r; +} + +int spdylay_session_on_rst_stream_received(spdylay_session *session, + spdylay_frame *frame) +{ + if(!spdylay_session_check_version(session, frame->rst_stream.hd.version)) { + return 0; + } + if(session->server && + !spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) && + frame->rst_stream.status_code == SPDYLAY_CANCEL) { + spdylay_session_close_pushed_streams(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + } + spdylay_session_close_stream(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + return 0; +} + +static void spdylay_update_initial_window_size_func(key_type key, void *value, + void *ptr) +{ + int32_t *vals; + vals = (int32_t*)ptr; + spdylay_stream_update_initial_window_size((spdylay_stream*)value, + vals[0], vals[1]); +} + +/* + * Updates the initial window size of all active streams. + */ +static void spdylay_session_update_initial_window_size +(spdylay_session *session, + int32_t new_initial_window_size) +{ + int32_t vals[2]; + vals[0] = new_initial_window_size; + vals[1] = session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]; + spdylay_map_each(&session->streams, + spdylay_update_initial_window_size_func, + vals); +} + +void spdylay_session_update_local_settings(spdylay_session *session, + spdylay_settings_entry *iv, + size_t niv) +{ + int i; + for(i = 0; i < niv; ++i) { + assert(iv[i].settings_id > 0 && iv[i].settings_id <= SPDYLAY_SETTINGS_MAX); + session->local_settings[iv[i].settings_id] = iv[i].value; + } +} + +int spdylay_session_on_settings_received(spdylay_session *session, + spdylay_frame *frame) +{ + int i, check[SPDYLAY_SETTINGS_MAX+1]; + if(!spdylay_session_check_version(session, frame->settings.hd.version)) { + return 0; + } + /* Check ID/value pairs and persist them if necessary. */ + memset(check, 0, sizeof(check)); + for(i = 0; i < frame->settings.niv; ++i) { + const spdylay_settings_entry *entry = &frame->settings.iv[i]; + /* SPDY/3 spec says if the multiple values for the same ID were + found, use the first one and ignore the rest. */ + if(entry->settings_id > SPDYLAY_SETTINGS_MAX || entry->settings_id == 0 || + check[entry->settings_id] == 1) { + continue; + } + check[entry->settings_id] = 1; + if(entry->settings_id == SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE && + session->flow_control) { + /* Update the initial window size of the all active streams */ + /* Check that initial_window_size < (1u << 31) */ + if(entry->value < (1u << 31)) { + spdylay_session_update_initial_window_size(session, entry->value); + } + } + session->remote_settings[entry->settings_id] = entry->value; + } + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SETTINGS, frame); + return 0; +} + +int spdylay_session_on_ping_received(spdylay_session *session, + spdylay_frame *frame) +{ + int r = 0; + if(!spdylay_session_check_version(session, frame->ping.hd.version)) { + return 0; + } + if(frame->ping.unique_id != 0) { + if(session->last_ping_unique_id == frame->ping.unique_id) { + /* This is ping reply from peer */ + /* Assign 0 to last_ping_unique_id so that we can ignore same + ID. */ + session->last_ping_unique_id = 0; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame); + } else if((session->server && frame->ping.unique_id % 2 == 1) || + (!session->server && frame->ping.unique_id % 2 == 0)) { + /* Peer sent ping, so ping it back */ + r = spdylay_session_add_ping(session, frame->ping.unique_id); + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame); + } + } + return r; +} + +int spdylay_session_on_goaway_received(spdylay_session *session, + spdylay_frame *frame) +{ + if(!spdylay_session_check_version(session, frame->goaway.hd.version)) { + return 0; + } + session->last_good_stream_id = frame->goaway.last_good_stream_id; + session->goaway_flags |= SPDYLAY_GOAWAY_RECV; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_GOAWAY, frame); + return 0; +} + +int spdylay_session_on_window_update_received(spdylay_session *session, + spdylay_frame *frame) +{ + spdylay_stream *stream; + if(!spdylay_session_check_version(session, frame->window_update.hd.version)) { + return 0; + } + if(!session->flow_control) { + return 0; + } + stream = spdylay_session_get_stream(session, frame->window_update.stream_id); + if(stream) { + if(INT32_MAX-frame->window_update.delta_window_size < stream->window_size) { + int r; + r = spdylay_session_handle_invalid_stream + (session, frame->window_update.stream_id, SPDYLAY_WINDOW_UPDATE, frame, + SPDYLAY_FLOW_CONTROL_ERROR); + return r; + } else { + stream->window_size += frame->window_update.delta_window_size; + if(stream->deferred_data != NULL && + (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) { + int r; + r = spdylay_pq_push(&session->ob_pq, stream->deferred_data); + if(r == 0) { + spdylay_stream_detach_deferred_data(stream); + } else if(r < 0) { + /* FATAL */ + assert(r < SPDYLAY_ERR_FATAL); + return r; + } + } + spdylay_session_call_on_ctrl_frame_received(session, + SPDYLAY_WINDOW_UPDATE, frame); + } + } + return 0; +} + +int spdylay_session_on_headers_received(spdylay_session *session, + spdylay_frame *frame) +{ + int r = 0; + int valid = 0; + spdylay_stream *stream; + if(!spdylay_session_check_version(session, frame->headers.hd.version)) { + return 0; + } + if((stream = spdylay_session_get_stream(session, + frame->headers.stream_id)) && + (stream->shut_flags & SPDYLAY_SHUT_RD) == 0 && + spdylay_session_check_nv(frame->headers.nv)) { + if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) { + if(stream->state == SPDYLAY_STREAM_OPENED) { + valid = 1; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, + frame); + if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + /* This is race condition. SPDYLAY_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + valid = 1; + } + } else { + /* If this is remote peer initiated stream, it is OK unless it + have sent FIN frame already. But if stream is in + SPDYLAY_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + valid = 1; + if(stream->state != SPDYLAY_STREAM_CLOSING) { + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, + frame); + if(frame->headers.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, + frame->headers.stream_id); + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + } + } + } + if(!valid) { + r = spdylay_session_handle_invalid_stream + (session, frame->headers.stream_id, SPDYLAY_HEADERS, frame, + SPDYLAY_PROTOCOL_ERROR); + } + return r; +} + +/* For errors, this function only returns FATAL error. */ +static int spdylay_session_process_ctrl_frame(spdylay_session *session) +{ + int r = 0; + uint16_t type; + spdylay_frame frame; + memcpy(&type, &session->iframe.headbuf[2], sizeof(uint16_t)); + type = ntohs(type); + switch(type) { + case SPDYLAY_SYN_STREAM: + spdylay_buffer_reset(&session->inflatebuf); + r = spdylay_frame_unpack_syn_stream(&frame.syn_stream, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(frame.syn_stream.nv); + } + r = spdylay_session_on_syn_stream_received(session, &frame); + spdylay_frame_syn_stream_free(&frame.syn_stream); + /* TODO if r indicates mulformed NV pairs (multiple nulls) or + invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for + other control frames. */ + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_SYN_REPLY: + spdylay_buffer_reset(&session->inflatebuf); + r = spdylay_frame_unpack_syn_reply(&frame.syn_reply, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(frame.syn_reply.nv); + } + r = spdylay_session_on_syn_reply_received(session, &frame); + spdylay_frame_syn_reply_free(&frame.syn_reply); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_RST_STREAM: + r = spdylay_frame_unpack_rst_stream(&frame.rst_stream, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len); + if(r == 0) { + r = spdylay_session_on_rst_stream_received(session, &frame); + spdylay_frame_rst_stream_free(&frame.rst_stream); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_SETTINGS: + r = spdylay_frame_unpack_settings(&frame.settings, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len); + if(r == 0) { + r = spdylay_session_on_settings_received(session, &frame); + spdylay_frame_settings_free(&frame.settings); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_NOOP: + break; + case SPDYLAY_PING: + r = spdylay_frame_unpack_ping(&frame.ping, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len); + if(r == 0) { + r = spdylay_session_on_ping_received(session, &frame); + spdylay_frame_ping_free(&frame.ping); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_GOAWAY: + r = spdylay_frame_unpack_goaway(&frame.goaway, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len); + if(r == 0) { + r = spdylay_session_on_goaway_received(session, &frame); + spdylay_frame_goaway_free(&frame.goaway); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_HEADERS: + spdylay_buffer_reset(&session->inflatebuf); + r = spdylay_frame_unpack_headers(&frame.headers, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(frame.headers.nv); + } + r = spdylay_session_on_headers_received(session, &frame); + spdylay_frame_headers_free(&frame.headers); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + case SPDYLAY_WINDOW_UPDATE: + r = spdylay_frame_unpack_window_update(&frame.window_update, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len); + if(r == 0) { + r = spdylay_session_on_window_update_received(session, &frame); + spdylay_frame_window_update_free(&frame.window_update); + } else if(spdylay_is_non_fatal(r)) { + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); + } + break; + } + if(spdylay_is_fatal(r)) { + return r; + } else { + return 0; + } +} + +int spdylay_session_on_data_received(spdylay_session *session, + uint8_t flags, int32_t length, + int32_t stream_id) +{ + int r = 0; + spdylay_status_code status_code = 0; + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) { + int valid = 0; + if(spdylay_session_is_my_stream_id(session, stream_id)) { + if(stream->state == SPDYLAY_STREAM_OPENED) { + valid = 1; + if(session->callbacks.on_data_recv_callback) { + session->callbacks.on_data_recv_callback + (session, flags, stream_id, length, session->user_data); + } + } else if(stream->state != SPDYLAY_STREAM_CLOSING) { + status_code = SPDYLAY_PROTOCOL_ERROR; + } + } else if(stream->state != SPDYLAY_STREAM_CLOSING) { + /* It is OK if this is remote peer initiated stream and we did + not receive FIN unless stream is in SPDYLAY_STREAM_CLOSING + state. This is a race condition. */ + valid = 1; + if(session->callbacks.on_data_recv_callback) { + session->callbacks.on_data_recv_callback + (session, flags, stream_id, length, session->user_data); + } + if(flags & SPDYLAY_DATA_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, stream_id); + } + } + if(valid) { + if(flags & SPDYLAY_DATA_FLAG_FIN) { + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + spdylay_session_close_stream_if_shut_rdwr(session, stream); + } + } + } else { + status_code = SPDYLAY_PROTOCOL_ERROR; + } + } else { + status_code = SPDYLAY_INVALID_STREAM; + } + if(status_code != 0) { + r = spdylay_session_add_rst_stream(session, stream_id, status_code); + } + return r; +} + +/* For errors, this function only returns FATAL error. */ +static int spdylay_session_process_data_frame(spdylay_session *session) +{ + uint8_t flags; + int32_t length; + int32_t stream_id; + int r; + stream_id = spdylay_get_uint32(session->iframe.headbuf) & + SPDYLAY_STREAM_ID_MASK; + flags = session->iframe.headbuf[4]; + length = spdylay_get_uint32(&session->iframe.headbuf[4]) & + SPDYLAY_LENGTH_MASK; + r = spdylay_session_on_data_received(session, flags, length, stream_id); + if(spdylay_is_fatal(r)) { + return r; + } else { + return 0; + } +} + +/* + * Accumulates received bytes |delta_size| and decides whether to send + * WINDOW_UPDATE. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +static int spdylay_session_update_recv_window_size(spdylay_session *session, + int32_t stream_id, + int32_t delta_size) +{ + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + stream->recv_window_size += delta_size; + /* This is just a heuristics. */ + if(stream->recv_window_size*2 >= + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]) { + int r; + r = spdylay_session_add_window_update(session, stream_id, + stream->recv_window_size); + if(r == 0) { + stream->recv_window_size = 0; + } else { + return r; + } + } + } + return 0; +} + +ssize_t spdylay_session_mem_recv(spdylay_session *session, + const uint8_t *in, size_t inlen) +{ + const uint8_t *inmark, *inlimit; + inmark = in; + inlimit = in+inlen; + while(1) { + ssize_t r; + if(session->iframe.state == SPDYLAY_RECV_HEAD) { + uint32_t payloadlen; + size_t remheadbytes; + size_t readlen; + size_t bufavail = inlimit-inmark; + if(bufavail == 0) { + break; + } + remheadbytes = SPDYLAY_HEAD_LEN-session->iframe.headbufoff; + readlen = spdylay_min(remheadbytes, bufavail); + memcpy(session->iframe.headbuf+session->iframe.headbufoff, + inmark, readlen); + inmark += readlen; + session->iframe.headbufoff += readlen; + if(session->iframe.headbufoff == SPDYLAY_HEAD_LEN) { + session->iframe.state = SPDYLAY_RECV_PAYLOAD; + payloadlen = spdylay_get_uint32(&session->iframe.headbuf[4]) & + SPDYLAY_LENGTH_MASK; + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + /* control frame */ + session->iframe.len = payloadlen; + r = spdylay_reserve_buffer(&session->iframe.buf, + &session->iframe.bufmax, + session->iframe.len); + if(r != 0) { + /* FATAL */ + assert(r < SPDYLAY_ERR_FATAL); + return r; + } + session->iframe.off = 0; + } else { + session->iframe.len = payloadlen; + session->iframe.off = 0; + } + } else { + break; + } + } + if(session->iframe.state == SPDYLAY_RECV_PAYLOAD) { + size_t rempayloadlen = session->iframe.len - session->iframe.off; + size_t bufavail, readlen; + int32_t data_stream_id = 0; + uint8_t data_flags = SPDYLAY_DATA_FLAG_NONE; + bufavail = inlimit-inmark; + if(rempayloadlen > 0 && bufavail == 0) { + break; + } + readlen = spdylay_min(bufavail, rempayloadlen); + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); + } else { + /* For data frame, We don't buffer data. Instead, just pass + received data to callback function. */ + data_stream_id = spdylay_get_uint32(session->iframe.headbuf) & + SPDYLAY_STREAM_ID_MASK; + data_flags = session->iframe.headbuf[4]; + if(session->callbacks.on_data_chunk_recv_callback) { + session->callbacks.on_data_chunk_recv_callback(session, + data_flags, + data_stream_id, + inmark, + readlen, + session->user_data); + } + } + session->iframe.off += readlen; + inmark += readlen; + + if(session->flow_control && + !spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + if(readlen > 0 && + (session->iframe.len != session->iframe.off || + (data_flags & SPDYLAY_DATA_FLAG_FIN) == 0)) { + r = spdylay_session_update_recv_window_size(session, + data_stream_id, + readlen); + if(r < 0) { + /* FATAL */ + assert(r < SPDYLAY_ERR_FATAL); + return r; + } + } + } + if(session->iframe.len == session->iframe.off) { + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + r = spdylay_session_process_ctrl_frame(session); + } else { + r = spdylay_session_process_data_frame(session); + } + if(r < 0) { + /* FATAL */ + assert(r < SPDYLAY_ERR_FATAL); + return r; + } + spdylay_inbound_frame_reset(&session->iframe); + } + } + } + return inmark-in; +} + +int spdylay_session_recv(spdylay_session *session) +{ + uint8_t buf[SPDYLAY_INBOUND_BUFFER_LENGTH]; + while(1) { + ssize_t readlen; + readlen = spdylay_recv(session, buf, sizeof(buf)); + if(readlen > 0) { + ssize_t proclen = spdylay_session_mem_recv(session, buf, readlen); + if(proclen < 0) { + return proclen; + } + assert(proclen == readlen); + } else if(readlen == 0 || readlen == SPDYLAY_ERR_WOULDBLOCK) { + return 0; + } else if(readlen == SPDYLAY_ERR_EOF) { + return readlen; + } else if(readlen < 0) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + } +} + +int spdylay_session_want_read(spdylay_session *session) +{ + /* If these flags are set, we don't want to read. The application + should drop the connection. */ + if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) && + (session->goaway_flags & SPDYLAY_GOAWAY_SEND)) { + return 0; + } + /* Unless GOAWAY is sent or received, we always want to read + incoming frames. After GOAWAY is sent or received, we are only + interested in active streams. */ + return !session->goaway_flags || spdylay_map_size(&session->streams) > 0; +} + +int spdylay_session_want_write(spdylay_session *session) +{ + /* If these flags are set, we don't want to write any data. The + application should drop the connection. */ + if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) && + (session->goaway_flags & SPDYLAY_GOAWAY_SEND)) { + return 0; + } + /* + * Unless GOAWAY is sent or received, we want to write frames if + * there is pending ones. If pending frame is SYN_STREAM and + * concurrent stream limit is reached, we don't want to write + * SYN_STREAM. After GOAWAY is sent or received, we want to write + * frames if there is pending ones AND there are active frames. + */ + return (session->aob.item != NULL || !spdylay_pq_empty(&session->ob_pq) || + (!spdylay_pq_empty(&session->ob_ss_pq) && + !spdylay_session_get_max_concurrent_streams_reached(session))) && + (!session->goaway_flags || spdylay_map_size(&session->streams) > 0); +} + +int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id) +{ + int r; + spdylay_frame *frame; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_ping_init(&frame->ping, session->version, unique_id); + r = spdylay_session_add_frame(session, SPDYLAY_PING, frame, NULL); + if(r != 0) { + spdylay_frame_ping_free(&frame->ping); + free(frame); + } + return r; +} + +int spdylay_session_add_goaway(spdylay_session *session, + int32_t last_good_stream_id, + uint32_t status_code) +{ + int r; + spdylay_frame *frame; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_goaway_init(&frame->goaway, session->version, + last_good_stream_id, status_code); + r = spdylay_session_add_frame(session, SPDYLAY_GOAWAY, frame, NULL); + if(r != 0) { + spdylay_frame_goaway_free(&frame->goaway); + free(frame); + } + return r; +} + +int spdylay_session_add_window_update(spdylay_session *session, + int32_t stream_id, + int32_t delta_window_size) +{ + int r; + spdylay_frame *frame; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_window_update_init(&frame->window_update, session->version, + stream_id, delta_window_size); + r = spdylay_session_add_frame(session, SPDYLAY_WINDOW_UPDATE, frame, NULL); + if(r != 0) { + spdylay_frame_window_update_free(&frame->window_update); + free(frame); + } + return r; +} + +ssize_t spdylay_session_pack_data(spdylay_session *session, + uint8_t **buf_ptr, size_t *buflen_ptr, + size_t datamax, + spdylay_data *frame) +{ + ssize_t framelen = datamax+8, r; + int eof; + uint8_t flags; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; + } + eof = 0; + r = frame->data_prd.read_callback + (session, frame->stream_id, (*buf_ptr)+8, datamax, + &eof, &frame->data_prd.source, session->user_data); + if(r < 0) { + return r; + } else if(datamax < r) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } + memset(*buf_ptr, 0, SPDYLAY_HEAD_LEN); + spdylay_put_uint32be(&(*buf_ptr)[0], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[4], r); + flags = 0; + if(eof) { + frame->eof = 1; + if(frame->flags & SPDYLAY_DATA_FLAG_FIN) { + flags |= SPDYLAY_DATA_FLAG_FIN; + } + } + (*buf_ptr)[4] = flags; + return r+8; +} + +uint32_t spdylay_session_get_next_unique_id(spdylay_session *session) +{ + uint32_t ret_id; + if(session->next_unique_id > SPDYLAY_MAX_UNIQUE_ID) { + if(session->server) { + session->next_unique_id = 2; + } else { + session->next_unique_id = 1; + } + } + ret_id = session->next_unique_id; + session->next_unique_id += 2; + return ret_id; +} + +void* spdylay_session_get_stream_user_data(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + return stream->stream_user_data; + } else { + return NULL; + } +} + +int spdylay_session_resume_data(spdylay_session *session, int32_t stream_id) +{ + int r; + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream == NULL || stream->deferred_data == NULL || + (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + r = spdylay_pq_push(&session->ob_pq, stream->deferred_data); + if(r == 0) { + spdylay_stream_detach_deferred_data(stream); + } + return r; +} + +uint8_t spdylay_session_get_pri_lowest(spdylay_session *session) +{ + if(session->version == SPDYLAY_PROTO_SPDY2) { + return SPDYLAY_SPDY2_PRI_LOWEST; + } else if(session->version == SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_SPDY3_PRI_LOWEST; + } else { + return 0; + } +} + +size_t spdylay_session_get_outbound_queue_size(spdylay_session *session) +{ + return spdylay_pq_size(&session->ob_pq)+spdylay_pq_size(&session->ob_ss_pq); +} diff --git a/deps/spdylay/lib/spdylay_session.h b/deps/spdylay/lib/spdylay_session.h new file mode 100644 index 0000000..c1408e1 --- /dev/null +++ b/deps/spdylay/lib/spdylay_session.h @@ -0,0 +1,490 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_SESSION_H +#define SPDYLAY_SESSION_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_pq.h" +#include "spdylay_map.h" +#include "spdylay_frame.h" +#include "spdylay_zlib.h" +#include "spdylay_stream.h" +#include "spdylay_buffer.h" +#include "spdylay_outbound_item.h" + +typedef struct { + spdylay_outbound_item *item; + /* Buffer for outbound frames. Used to pack one frame. The memory + pointed by framebuf is initially allocated by + spdylay_session_{client,server}_new() and deallocated by + spdylay_session_del() */ + uint8_t *framebuf; + /* The capacity of framebuf in bytes */ + size_t framebufmax; + /* The length of the frame stored in framebuf */ + size_t framebuflen; + /* The number of bytes has been sent */ + size_t framebufoff; +} spdylay_active_outbound_item; + +/* Buffer length for inbound raw byte stream. */ +#define SPDYLAY_INBOUND_BUFFER_LENGTH 16384 + +#define SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH (SPDYLAY_DATA_PAYLOAD_LENGTH+8) +#define SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH \ + SPDYLAY_INITIAL_OUTBOUND_FRAMEBUF_LENGTH +#define SPDYLAY_INITIAL_NV_BUFFER_LENGTH 4096 + +#define SPDYLAY_INITIAL_WINDOW_SIZE 65536 + +typedef enum { + SPDYLAY_RECV_HEAD, + SPDYLAY_RECV_PAYLOAD +} spdylay_inbound_state; + +#define SPDYLAY_HEAD_LEN 8 + +/* Maximum unique ID in use for PING. If unique ID exeeds this number, + it wraps to 1 (client) or 2 (server) */ +#define SPDYLAY_MAX_UNIQUE_ID ((1u << 31)-1) + +typedef struct { + spdylay_inbound_state state; + uint8_t headbuf[SPDYLAY_HEAD_LEN]; + /* How many bytes are filled in headbuf */ + size_t headbufoff; + /* Payload for control frames. It is not used for DATA frames */ + uint8_t *buf; + /* Capacity of buf */ + size_t bufmax; + /* length in Length field */ + size_t len; + /* How many bytes are filled in buf */ + size_t off; +} spdylay_inbound_frame; + +typedef enum { + SPDYLAY_GOAWAY_NONE = 0, + /* Flag means GOAWAY frame is sent to the remote peer. */ + SPDYLAY_GOAWAY_SEND = 0x1, + /* Flag means GOAWAY frame is received from the remote peer. */ + SPDYLAY_GOAWAY_RECV = 0x2, + /* Flag means connection should be dropped after sending GOAWAY. */ + SPDYLAY_GOAWAY_FAIL_ON_SEND = 0x4 +} spdylay_goaway_flag; + +struct spdylay_session { + /* The protocol version: either SPDYLAY_PROTO_SPDY2 or + SPDYLAY_PROTO_SPDY3 */ + uint16_t version; + uint8_t server; + /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ + uint32_t next_stream_id; + int32_t last_recv_stream_id; + /* Counter of unique ID of PING. Wraps when it exceeds + SPDYLAY_MAX_UNIQUE_ID */ + uint32_t next_unique_id; + + /* Sequence number of outbound frame to maintain the order of + enqueue if priority is equal. */ + int64_t next_seq; + + spdylay_map /* */ streams; + /* Queue for outbound frames other than SYN_STREAM */ + spdylay_pq /* */ ob_pq; + /* Queue for outbound SYN_STREAM frame */ + spdylay_pq /* */ ob_ss_pq; + + spdylay_active_outbound_item aob; + + spdylay_inbound_frame iframe; + + /* Buffer used to store inflated name/value pairs in wire format + temporarily on pack/unpack. */ + uint8_t *nvbuf; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; + /* Buffer used to store name/value pairs while inflating them using + zlib on unpack */ + spdylay_buffer inflatebuf; + + spdylay_zlib hd_deflater; + spdylay_zlib hd_inflater; + + /* The last unique ID sent to the peer. */ + uint32_t last_ping_unique_id; + + /* Flags indicating GOAWAY is sent and/or recieved. The flags are + composed by bitwise OR-ing spdylay_goaway_flag. */ + uint8_t goaway_flags; + /* This is the value in GOAWAY frame sent by remote endpoint. */ + int32_t last_good_stream_id; + + /* Flag to indicate whether this session enforces flow + control. Nonzero for flow control enabled. */ + uint8_t flow_control; + + /* Settings value received from the remote endpoint. We just use ID + as index. The index = 0 is unused. */ + uint32_t remote_settings[SPDYLAY_SETTINGS_MAX+1]; + /* Settings value of the local endpoint. */ + uint32_t local_settings[SPDYLAY_SETTINGS_MAX+1]; + + spdylay_session_callbacks callbacks; + void *user_data; +}; + +/* TODO stream timeout etc */ + +/* + * Returns nonzero value if |stream_id| is initiated by local + * endpoint. + */ +int spdylay_session_is_my_stream_id(spdylay_session *session, + int32_t stream_id); + +/* + * Adds frame |frame| of type |frame_type| to the outbound queue in + * |session|. |aux_data| is a pointer to the arbitrary data. Its + * interpretation is defined per |frame_type|. When this function + * succeeds, it takes ownership of |frame| and |aux_data|, so caller + * must not free them on success. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_add_frame(spdylay_session *session, + spdylay_frame_type frame_type, + spdylay_frame *frame, + void *aux_data); + +/* + * Adds RST_STREAM frame for the stream |stream_id| with status code + * |status_code|. This is a convenient function built on top of + * spdylay_session_add_frame() to add RST_STREAM easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_add_rst_stream(spdylay_session *session, + int32_t stream_id, uint32_t status_code); + +/* + * Adds PING frame with unique ID |unique_id|. This is a convenient + * functin built on top of spdylay_session_add_frame() to add PING + * easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id); + +/* + * Adds GOAWAY frame with last-good-stream-ID |last_good_stream_id| + * and the status code |status_code|. The |status_code| is ignored if + * the protocol version is SPDYLAY_PROTO_SPDY2. This is a convenient + * function built on top of spdylay_session_add_frame() to add GOAWAY + * easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_add_goaway(spdylay_session *session, + int32_t last_good_stream_id, + uint32_t status_code); + +/* + * Adds WINDOW_UPDATE frame with stream ID |stream_id| and + * delta-window-size |delta_window_size|. This is a convenient + * function built on top of spdylay_session_add_frame() to add + * WINDOW_UPDATE easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_add_window_update(spdylay_session *session, + int32_t stream_id, + int32_t delta_window_size); + +/* + * Creates new stream in |session| with stream ID |stream_id|, + * priority |pri| and flags |flags|. SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL + * flag is set in |flags|, this stream is + * unidirectional. SPDYLAY_CTRL_FLAG_FIN flag is set in |flags|, the + * sender of SYN_STREAM will not send any further data in this + * stream. Since this function is called when SYN_STREAM is sent or + * received, these flags are taken from SYN_STREAM. The state of + * stream is set to |initial_state|. |stream_user_data| is a pointer + * to the arbitrary user supplied data to be associated to this + * stream. + * + * This function returns a pointer to created new stream object, or + * NULL. + */ +spdylay_stream* spdylay_session_open_stream(spdylay_session *session, + int32_t stream_id, + uint8_t flags, uint8_t pri, + spdylay_stream_state initial_state, + void *stream_user_data); + +/* + * Closes stream whose stream ID is |stream_id|. The reason of closure + * is indicated by |status_code|. When closing the stream, + * on_stream_close_callback will be called. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code); + +/* + * Closes all pushed streams which associate them to stream + * |stream_id| with the status code |status_code|. + */ +void spdylay_session_close_pushed_streams(spdylay_session *session, + int32_t stream_id, + spdylay_status_code status_code); + +/* + * If further receptions and transmissions over the stream |stream_id| + * are disallowed, close the stream with status code |status_code|. + * + * This function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * SPDYLAY_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session, + spdylay_stream *stream); + +/* + * Called when SYN_STREAM is received, assuming |frame.syn_stream| is + * properly initialized. This function does first validate received + * frame and then open stream and call callback functions. This + * function does not return error if frame is not valid. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_syn_stream_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when SYN_REPLY is received, assuming |frame.syn_reply| is + * properly initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_syn_reply_received(spdylay_session *session, + spdylay_frame *frame); + + +/* + * Called when RST_STREAM is received, assuming |frame.rst_stream| is + * properly initialized. + * + * This function returns 0 and never fail. + */ +int spdylay_session_on_rst_stream_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when SETTINGS is received, assuming |frame.settings| is + * properly initialized. + * + * This function returns 0 and never fail. + */ +int spdylay_session_on_settings_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when PING is received, assuming |frame.ping| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_ping_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when GOAWAY is received, assuming |frame.goaway| is properly + * initialized. + * + * This function returns 0 and never fail. + */ +int spdylay_session_on_goaway_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when HEADERS is recieved, assuming |frame.headers| is + * properly initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_headers_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when WINDOW_UPDATE is recieved, assuming + * |frame.window_update| is properly initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_window_update_received(spdylay_session *session, + spdylay_frame *frame); + +/* + * Called when DATA is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_session_on_data_received(spdylay_session *session, + uint8_t flags, int32_t length, + int32_t stream_id); + +/* + * Returns spdylay_stream* object whose stream ID is |stream_id|. It + * could be NULL if such stream does not exist. + */ +spdylay_stream* spdylay_session_get_stream(spdylay_session *session, + int32_t stream_id); + +/* + * Packs DATA frame |frame| in wire frame format and stores it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. It packs header in first 8 bytes. Remaining bytes + * are the DATA apyload and are filled using |frame->data_prd|. The + * length of payload is at most |datamax| bytes. + * + * This function returns the size of packed frame if it succeeds, or + * one of the following negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + * SPDYLAY_ERR_DEFERRED + * The DATA frame is postponed. + * SPDYLAY_ERR_CALLBACK_FAILURE + * The read_callback failed. + */ +ssize_t spdylay_session_pack_data(spdylay_session *session, + uint8_t **buf_ptr, size_t *buflen_ptr, + size_t datamax, + spdylay_data *frame); + +/* + * Returns next unique ID which can be used with PING. + */ +uint32_t spdylay_session_get_next_unique_id(spdylay_session *session); + +/* + * Returns top of outbound frame queue. This function returns NULL if + * queue is empty. + */ +spdylay_outbound_item* spdylay_session_get_ob_pq_top(spdylay_session *session); + +/* + * Pops and returns next item to send. If there is no such item, + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +spdylay_outbound_item* spdylay_session_pop_next_ob_item +(spdylay_session *session); + +/* + * Returns next item to send. If there is no such item, this function + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_pq is empty but + * session->ob_ss_pq has item and max concurrent streams is reached, + * then this function returns NULL. + */ +spdylay_outbound_item* spdylay_session_get_next_ob_item +(spdylay_session *session); + +/* + * Returns lowest priority value. + */ +uint8_t spdylay_session_get_pri_lowest(spdylay_session *session); + +/* + * Updates local settings with the |iv|. The number of elements in the + * array pointed by the |iv| is given by the |niv|. This function + * assumes that the all settings_id member in |iv| are in range 1 to + * SPDYLAY_SETTINGS_MAX, inclusive. + */ +void spdylay_session_update_local_settings(spdylay_session *session, + spdylay_settings_entry *iv, + size_t niv); + +#endif /* SPDYLAY_SESSION_H */ diff --git a/deps/spdylay/lib/spdylay_stream.c b/deps/spdylay/lib/spdylay_stream.c new file mode 100644 index 0000000..a652f23 --- /dev/null +++ b/deps/spdylay/lib/spdylay_stream.c @@ -0,0 +1,100 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_stream.h" + +#include + +void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id, + uint8_t flags, uint8_t pri, + spdylay_stream_state initial_state, + int32_t initial_window_size, + void *stream_user_data) +{ + stream->stream_id = stream_id; + stream->flags = flags; + stream->pri = pri; + stream->state = initial_state; + stream->shut_flags = SPDYLAY_SHUT_NONE; + stream->pushed_streams = NULL; + stream->pushed_streams_length = 0; + stream->pushed_streams_capacity = 0; + stream->stream_user_data = stream_user_data; + stream->deferred_data = NULL; + stream->deferred_flags = SPDYLAY_DEFERRED_NONE; + stream->window_size = initial_window_size; + stream->recv_window_size = 0; +} + +void spdylay_stream_free(spdylay_stream *stream) +{ + free(stream->pushed_streams); + spdylay_outbound_item_free(stream->deferred_data); + free(stream->deferred_data); +} + +void spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag) +{ + stream->shut_flags |= flag; +} + +int spdylay_stream_add_pushed_stream(spdylay_stream *stream, int32_t stream_id) +{ + if(stream->pushed_streams_capacity == stream->pushed_streams_length) { + int32_t *streams; + size_t capacity = stream->pushed_streams_capacity == 0 ? + 5 : stream->pushed_streams_capacity*2; + streams = realloc(stream->pushed_streams, capacity*sizeof(uint32_t)); + if(streams == NULL) { + return SPDYLAY_ERR_NOMEM; + } + stream->pushed_streams = streams; + stream->pushed_streams_capacity = capacity; + } + stream->pushed_streams[stream->pushed_streams_length++] = stream_id; + return 0; +} + +void spdylay_stream_defer_data(spdylay_stream *stream, + spdylay_outbound_item *data, + uint8_t flags) +{ + assert(stream->deferred_data == NULL); + stream->deferred_data = data; + stream->deferred_flags = flags; +} + +void spdylay_stream_detach_deferred_data(spdylay_stream *stream) +{ + stream->deferred_data = NULL; + stream->deferred_flags = SPDYLAY_DEFERRED_NONE; +} + +void spdylay_stream_update_initial_window_size(spdylay_stream *stream, + int32_t new_initial_window_size, + int32_t old_initial_window_size) +{ + stream->window_size = + new_initial_window_size-(old_initial_window_size-stream->window_size); +} diff --git a/deps/spdylay/lib/spdylay_stream.h b/deps/spdylay/lib/spdylay_stream.h new file mode 100644 index 0000000..0279fb6 --- /dev/null +++ b/deps/spdylay/lib/spdylay_stream.h @@ -0,0 +1,161 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_STREAM_H +#define SPDYLAY_STREAM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_outbound_item.h" + +/* + * If local peer is stream initiator: + * SPDYLAY_STREAM_OPENING : upon sending SYN_STREAM + * SPDYLAY_STREAM_OPENED : upon receiving SYN_REPLY + * SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM + * + * If remote peer is stream initiator: + * SPDYLAY_STREAM_OPENING : upon receiving SYN_STREAM + * SPDYLAY_STREAM_OPENED : upon sending SYN_REPLY + * SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM + */ +typedef enum { + /* Initial state */ + SPDYLAY_STREAM_INITIAL, + /* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is + not received yet. For receiver: SYN_STREAM has been received, + but it does not send SYN_REPLY yet. */ + SPDYLAY_STREAM_OPENING, + /* For stream initiator: SYN_REPLY is received. For receiver: + SYN_REPLY is sent. */ + SPDYLAY_STREAM_OPENED, + /* RST_STREAM is received, but somehow we need to keep stream in + memory. */ + SPDYLAY_STREAM_CLOSING +} spdylay_stream_state; + +typedef enum { + SPDYLAY_SHUT_NONE = 0, + /* Indicates further receptions will be disallowed. */ + SPDYLAY_SHUT_RD = 0x01, + /* Indicates further transmissions will be disallowed. */ + SPDYLAY_SHUT_WR = 0x02, + /* Indicates both further receptions and transmissions will be + disallowed. */ + SPDYLAY_SHUT_RDWR = SPDYLAY_SHUT_RD | SPDYLAY_SHUT_WR +} spdylay_shut_flag; + +typedef enum { + SPDYLAY_DEFERRED_NONE = 0, + /* Indicates the DATA is deferred due to flow control. */ + SPDYLAY_DEFERRED_FLOW_CONTROL = 0x01 +} spdylay_deferred_flag; + +typedef struct { + int32_t stream_id; + spdylay_stream_state state; + /* Use same value in SYN_STREAM frame */ + uint8_t flags; + /* Use same scheme in SYN_STREAM frame */ + uint8_t pri; + /* Bitwise OR of zero or more spdylay_shut_flag values */ + uint8_t shut_flags; + /* The array of server-pushed stream IDs which associate them to + this stream. */ + int32_t *pushed_streams; + /* The number of stored pushed stream ID in |pushed_streams| */ + size_t pushed_streams_length; + /* The maximum number of stream ID the |pushed_streams| can + store. */ + size_t pushed_streams_capacity; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Deferred DATA frame */ + spdylay_outbound_item *deferred_data; + /* The flags for defered DATA. Bitwise OR of zero or more + spdylay_deferred_flag values */ + uint8_t deferred_flags; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. */ + int32_t recv_window_size; +} spdylay_stream; + +void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id, + uint8_t flags, uint8_t pri, + spdylay_stream_state initial_state, + int32_t initial_window_size, + void *stream_user_data); + +void spdylay_stream_free(spdylay_stream *stream); + +/* + * Disallow either further receptions or transmissions, or both. + * |flag| is bitwise OR of one or more of spdylay_shut_flag. + */ +void spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag); + +/* + * Add server-pushed |stream_id| to this stream. This happens when + * server-pushed stream is associated to this stream. This function + * returns 0 if it succeeds, or negative error code. + * + * RETURN VALUE + * ------------ + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_stream_add_pushed_stream(spdylay_stream *stream, int32_t stream_id); + +/* + * Defer DATA frame |data|. We won't call this function in the + * situation where stream->deferred_data != NULL. If |flags| is + * bitwise OR of zero or more spdylay_deferred_flag values. + */ +void spdylay_stream_defer_data(spdylay_stream *stream, + spdylay_outbound_item *data, + uint8_t flags); + +/* + * Detaches deferred data from this stream. This function does not + * free deferred data. + */ +void spdylay_stream_detach_deferred_data(spdylay_stream *stream); + +/* + * Updates the initial window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + */ +void spdylay_stream_update_initial_window_size(spdylay_stream *stream, + int32_t new_initial_window_size, + int32_t old_initial_window_size); + +#endif /* SPDYLAY_STREAM */ diff --git a/deps/spdylay/lib/spdylay_submit.c b/deps/spdylay/lib/spdylay_submit.c new file mode 100644 index 0000000..d99cf74 --- /dev/null +++ b/deps/spdylay/lib/spdylay_submit.c @@ -0,0 +1,307 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_submit.h" + +#include + +#include "spdylay_session.h" +#include "spdylay_frame.h" + +static int spdylay_submit_syn_stream_shared +(spdylay_session *session, + uint8_t flags, + int32_t assoc_stream_id, + uint8_t pri, + const char **nv, + const spdylay_data_provider *data_prd, + void *stream_user_data) +{ + int r; + spdylay_frame *frame; + char **nv_copy; + uint8_t flags_copy; + spdylay_data_provider *data_prd_copy = NULL; + spdylay_syn_stream_aux_data *aux_data; + if(pri > spdylay_session_get_pri_lowest(session)) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + if(assoc_stream_id != 0) { + if(session->server == 0) { + assoc_stream_id = 0; + } else if(spdylay_session_is_my_stream_id(session, assoc_stream_id)) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + } + if(data_prd != NULL && data_prd->read_callback != NULL) { + data_prd_copy = malloc(sizeof(spdylay_data_provider)); + if(data_prd_copy == NULL) { + return SPDYLAY_ERR_NOMEM; + } + *data_prd_copy = *data_prd; + } + aux_data = malloc(sizeof(spdylay_syn_stream_aux_data)); + if(aux_data == NULL) { + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + aux_data->data_prd = data_prd_copy; + aux_data->stream_user_data = stream_user_data; + + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + free(aux_data); + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + nv_copy = spdylay_frame_nv_norm_copy(nv); + if(nv_copy == NULL) { + free(frame); + free(aux_data); + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + flags_copy = 0; + if(flags & SPDYLAY_CTRL_FLAG_FIN) { + flags_copy |= SPDYLAY_CTRL_FLAG_FIN; + } + if(flags & SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL) { + flags_copy |= SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL; + } + spdylay_frame_syn_stream_init(&frame->syn_stream, + session->version, flags_copy, + 0, assoc_stream_id, pri, nv_copy); + r = spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame, + aux_data); + if(r != 0) { + spdylay_frame_syn_stream_free(&frame->syn_stream); + free(frame); + free(aux_data); + free(data_prd_copy); + } + return r; +} + +int spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags, + int32_t assoc_stream_id, uint8_t pri, + const char **nv, void *stream_user_data) +{ + return spdylay_submit_syn_stream_shared(session, flags, assoc_stream_id, + pri, nv, NULL, stream_user_data); +} + +int spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags, + int32_t stream_id, const char **nv) +{ + int r; + spdylay_frame *frame; + char **nv_copy; + uint8_t flags_copy; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + nv_copy = spdylay_frame_nv_norm_copy(nv); + if(nv_copy == NULL) { + free(frame); + return SPDYLAY_ERR_NOMEM; + } + flags_copy = 0; + if(flags & SPDYLAY_CTRL_FLAG_FIN) { + flags_copy |= SPDYLAY_CTRL_FLAG_FIN; + } + spdylay_frame_syn_reply_init(&frame->syn_reply, session->version, flags_copy, + stream_id, nv_copy); + r = spdylay_session_add_frame(session, SPDYLAY_SYN_REPLY, frame, NULL); + if(r != 0) { + spdylay_frame_syn_reply_free(&frame->syn_reply); + free(frame); + } + return r; +} + +int spdylay_submit_headers(spdylay_session *session, uint8_t flags, + int32_t stream_id, const char **nv) +{ + int r; + spdylay_frame *frame; + char **nv_copy; + uint8_t flags_copy; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + nv_copy = spdylay_frame_nv_norm_copy(nv); + if(nv_copy == NULL) { + free(frame); + return SPDYLAY_ERR_NOMEM; + } + flags_copy = 0; + if(flags & SPDYLAY_CTRL_FLAG_FIN) { + flags_copy |= SPDYLAY_CTRL_FLAG_FIN; + } + spdylay_frame_headers_init(&frame->headers, session->version, flags_copy, + stream_id, nv_copy); + r = spdylay_session_add_frame(session, SPDYLAY_HEADERS, frame, NULL); + if(r != 0) { + spdylay_frame_headers_free(&frame->headers); + free(frame); + } + return r; +} + +int spdylay_submit_ping(spdylay_session *session) +{ + return spdylay_session_add_ping(session, + spdylay_session_get_next_unique_id(session)); +} + +int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id, + uint32_t status_code) +{ + return spdylay_session_add_rst_stream(session, stream_id, status_code); +} + +int spdylay_submit_goaway(spdylay_session *session, uint32_t status_code) +{ + return spdylay_session_add_goaway(session, session->last_recv_stream_id, + status_code); +} + +int spdylay_submit_settings(spdylay_session *session, uint8_t flags, + const spdylay_settings_entry *iv, size_t niv) +{ + spdylay_frame *frame; + spdylay_settings_entry *iv_copy; + int check[SPDYLAY_SETTINGS_MAX+1]; + int i, r; + memset(check, 0, sizeof(check)); + for(i = 0; i < niv; ++i) { + if(iv[i].settings_id > SPDYLAY_SETTINGS_MAX || iv[i].settings_id == 0 || + check[iv[i].settings_id] == 1) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } else { + check[iv[i].settings_id] = 1; + } + } + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + iv_copy = spdylay_frame_iv_copy(iv, niv); + if(iv_copy == NULL) { + free(frame); + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_iv_sort(iv_copy, niv); + spdylay_frame_settings_init(&frame->settings, session->version, + flags, iv_copy, niv); + r = spdylay_session_add_frame(session, SPDYLAY_SETTINGS, frame, NULL); + if(r == 0) { + spdylay_session_update_local_settings(session, iv_copy, niv); + } else { + spdylay_frame_settings_free(&frame->settings); + free(frame); + } + return r; +} + +int spdylay_submit_request(spdylay_session *session, uint8_t pri, + const char **nv, + const spdylay_data_provider *data_prd, + void *stream_user_data) +{ + int flags; + flags = 0; + if(data_prd == NULL || data_prd->read_callback == NULL) { + flags |= SPDYLAY_CTRL_FLAG_FIN; + } + return spdylay_submit_syn_stream_shared(session, flags, 0, pri, nv, data_prd, + stream_user_data); +} + +int spdylay_submit_response(spdylay_session *session, + int32_t stream_id, const char **nv, + const spdylay_data_provider *data_prd) +{ + int r; + spdylay_frame *frame; + char **nv_copy; + uint8_t flags = 0; + spdylay_data_provider *data_prd_copy = NULL; + if(data_prd != NULL && data_prd->read_callback != NULL) { + data_prd_copy = malloc(sizeof(spdylay_data_provider)); + if(data_prd_copy == NULL) { + return SPDYLAY_ERR_NOMEM; + } + *data_prd_copy = *data_prd; + } + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + nv_copy = spdylay_frame_nv_norm_copy(nv); + if(nv_copy == NULL) { + free(frame); + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + if(data_prd_copy == NULL) { + flags |= SPDYLAY_CTRL_FLAG_FIN; + } + spdylay_frame_syn_reply_init(&frame->syn_reply, session->version, flags, + stream_id, nv_copy); + r = spdylay_session_add_frame(session, SPDYLAY_SYN_REPLY, frame, + data_prd_copy); + if(r != 0) { + spdylay_frame_syn_reply_free(&frame->syn_reply); + free(frame); + free(data_prd_copy); + } + return r; +} + +int spdylay_submit_data(spdylay_session *session, int32_t stream_id, + uint8_t flags, + const spdylay_data_provider *data_prd) +{ + int r; + spdylay_frame *frame; + uint8_t nflags = 0; + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + return SPDYLAY_ERR_NOMEM; + } + if(flags & SPDYLAY_DATA_FLAG_FIN) { + nflags |= SPDYLAY_DATA_FLAG_FIN; + } + spdylay_frame_data_init(&frame->data, stream_id, nflags, data_prd); + r = spdylay_session_add_frame(session, SPDYLAY_DATA, frame, NULL); + if(r != 0) { + spdylay_frame_data_free(&frame->data); + free(frame); + } + return r; +} diff --git a/deps/spdylay/lib/spdylay_submit.h b/deps/spdylay/lib/spdylay_submit.h new file mode 100644 index 0000000..b7bad41 --- /dev/null +++ b/deps/spdylay/lib/spdylay_submit.h @@ -0,0 +1,34 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_SUBMIT_H +#define SPDYLAY_SUBMIT_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* SPDYLAY_SUBMIT_H */ diff --git a/deps/spdylay/lib/spdylay_zlib.c b/deps/spdylay/lib/spdylay_zlib.c new file mode 100644 index 0000000..70ce1a1 --- /dev/null +++ b/deps/spdylay/lib/spdylay_zlib.c @@ -0,0 +1,358 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_zlib.h" + +#include + +#define COMPRESSION_LEVEL 9 +#define WINDOW_BITS 11 +#define MEM_LEVEL 1 + +static const char spdy2_hd_dict[] = + "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" + "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" + "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" + "-agent10010120020120220320420520630030130230330430530630740040140240340440" + "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" + "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" + "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" + "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" + "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" + "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" + "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" + "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" + ".1statusversionurl"; + +static const uint8_t spdy3_hd_dict[] = { + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, /* - - - - o p t i */ + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, /* o n s - - - - h */ + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, /* e a d - - - - p */ + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, /* o s t - - - - p */ + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, /* u t - - - - d e */ + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, /* l e t e - - - - */ + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, /* t r a c e - - - */ + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, /* - a c c e p t - */ + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* t - c h a r s e */ + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, /* t - - - - a c c */ + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e p t - e n c o */ + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, /* d i n g - - - - */ + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, /* a c c e p t - l */ + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, /* a n g u a g e - */ + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */ + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, /* t - r a n g e s */ + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, /* - - - - a g e - */ + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, /* - - - a l l o w */ + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, /* - - - - a u t h */ + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, /* o r i z a t i o */ + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, /* n - - - - c a c */ + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, /* h e - c o n t r */ + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, /* o l - - - - c o */ + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, /* n n e c t i o n */ + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, /* e n t - b a s e */ + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e n t - e n c o */ + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, /* d i n g - - - - */ + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, /* c o n t e n t - */ + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, /* l a n g u a g e */ + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */ + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, /* e n t - l e n g */ + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, /* t h - - - - c o */ + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, /* n t e n t - l o */ + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* c a t i o n - - */ + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, /* t - m d 5 - - - */ + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, /* - c o n t e n t */ + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, /* - r a n g e - - */ + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */ + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, /* t - t y p e - - */ + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, /* - - d a t e - - */ + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, /* - - e t a g - - */ + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, /* - - e x p e c t */ + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, /* - - - - e x p i */ + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, /* r e s - - - - f */ + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, /* r o m - - - - h */ + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, /* o s t - - - - i */ + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, /* f - m a t c h - */ + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, /* - - - i f - m o */ + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, /* d i f i e d - s */ + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, /* i n c e - - - - */ + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, /* i f - n o n e - */ + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, /* m a t c h - - - */ + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, /* - i f - r a n g */ + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, /* e - - - - i f - */ + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, /* u n m o d i f i */ + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, /* e d - s i n c e */ + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, /* - - - - l a s t */ + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, /* - m o d i f i e */ + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, /* d - - - - l o c */ + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, /* a t i o n - - - */ + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, /* - m a x - f o r */ + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, /* w a r d s - - - */ + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, /* - p r a g m a - */ + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, /* - - - p r o x y */ + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, /* - a u t h e n t */ + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, /* i c a t e - - - */ + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, /* - p r o x y - a */ + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, /* u t h o r i z a */ + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, /* t i o n - - - - */ + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, /* r a n g e - - - */ + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, /* - r e f e r e r */ + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, /* - - - - r e t r */ + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, /* y - a f t e r - */ + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, /* - - - s e r v e */ + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, /* r - - - - t e - */ + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, /* - - - t r a i l */ + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, /* e r - - - - t r */ + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, /* a n s f e r - e */ + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, /* n c o d i n g - */ + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, /* - - - u p g r a */ + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, /* d e - - - - u s */ + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, /* e r - a g e n t */ + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, /* - - - - v a r y */ + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, /* - - - - v i a - */ + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, /* - - - w a r n i */ + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, /* n g - - - - w w */ + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, /* w - a u t h e n */ + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, /* t i c a t e - - */ + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, /* - - m e t h o d */ + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, /* - - - - g e t - */ + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, /* - - - s t a t u */ + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, /* s - - - - 2 0 0 */ + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, /* - O K - - - - v */ + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* e r s i o n - - */ + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, /* - - H T T P - 1 */ + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, /* - 1 - - - - u r */ + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, /* l - - - - p u b */ + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, /* l i c - - - - s */ + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, /* e t - c o o k i */ + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, /* e - - - - k e e */ + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, /* p - a l i v e - */ + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, /* - - - o r i g i */ + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, /* n 1 0 0 1 0 1 2 */ + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, /* 0 1 2 0 2 2 0 5 */ + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, /* 2 0 6 3 0 0 3 0 */ + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, /* 2 3 0 3 3 0 4 3 */ + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, /* 0 5 3 0 6 3 0 7 */ + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, /* 4 0 2 4 0 5 4 0 */ + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, /* 6 4 0 7 4 0 8 4 */ + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, /* 0 9 4 1 0 4 1 1 */ + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, /* 4 1 2 4 1 3 4 1 */ + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, /* 4 4 1 5 4 1 6 4 */ + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, /* 1 7 5 0 2 5 0 4 */ + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, /* 5 0 5 2 0 3 - N */ + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, /* o n - A u t h o */ + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, /* r i t a t i v e */ + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, /* - I n f o r m a */ + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, /* t i o n 2 0 4 - */ + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, /* N o - C o n t e */ + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, /* n t 3 0 1 - M o */ + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, /* v e d - P e r m */ + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, /* a n e n t l y 4 */ + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, /* 0 0 - B a d - R */ + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, /* e q u e s t 4 0 */ + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, /* 1 - U n a u t h */ + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, /* o r i z e d 4 0 */ + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, /* 3 - F o r b i d */ + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, /* d e n 4 0 4 - N */ + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, /* o t - F o u n d */ + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, /* 5 0 0 - I n t e */ + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, /* r n a l - S e r */ + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, /* v e r - E r r o */ + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, /* r 5 0 1 - N o t */ + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, /* - I m p l e m e */ + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, /* n t e d 5 0 3 - */ + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, /* S e r v i c e - */ + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, /* U n a v a i l a */ + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, /* b l e J a n - F */ + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, /* e b - M a r - A */ + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, /* p r - M a y - J */ + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, /* u n - J u l - A */ + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, /* u g - S e p t - */ + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, /* O c t - N o v - */ + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, /* D e c - 0 0 - 0 */ + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, /* 0 - 0 0 - M o n */ + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, /* - - T u e - - W */ + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, /* e d - - T h u - */ + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, /* - F r i - - S a */ + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, /* t - - S u n - - */ + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, /* G M T c h u n k */ + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, /* e d - t e x t - */ + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, /* h t m l - i m a */ + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, /* g e - p n g - i */ + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, /* m a g e - j p g */ + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, /* - i m a g e - g */ + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* i f - a p p l i */ + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* m l - a p p l i */ + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */ + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, /* h t m l - x m l */ + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, /* - t e x t - p l */ + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, /* a i n - t e x t */ + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, /* - j a v a s c r */ + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, /* i p t - p u b l */ + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, /* i c p r i v a t */ + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, /* e m a x - a g e */ + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, /* - g z i p - d e */ + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, /* f l a t e - s d */ + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* c h c h a r s e */ + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, /* t - u t f - 8 c */ + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, /* h a r s e t - i */ + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, /* s o - 8 8 5 9 - */ + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, /* 1 - u t f - - - */ + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e /* - e n q - 0 - */ +}; + +static const uint8_t* spdylay_select_hd_dict(size_t *len_ptr, uint16_t version) +{ + const uint8_t *hd_dict; + hd_dict = NULL; + if(version == SPDYLAY_PROTO_SPDY2) { + hd_dict = (const uint8_t*)spdy2_hd_dict; + *len_ptr = sizeof(spdy2_hd_dict); + } else if(version == SPDYLAY_PROTO_SPDY3) { + hd_dict = spdy3_hd_dict; + *len_ptr = sizeof(spdy3_hd_dict); + } + return hd_dict; +} + +int spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater, uint16_t version) +{ + const unsigned char *hd_dict; + size_t hd_dict_length; + deflater->zst.next_in = Z_NULL; + deflater->zst.zalloc = Z_NULL; + deflater->zst.zfree = Z_NULL; + deflater->zst.opaque = Z_NULL; + deflater->version = version; + hd_dict = spdylay_select_hd_dict(&hd_dict_length, version); + if(hd_dict == NULL) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + if(Z_OK != deflateInit2(&deflater->zst, COMPRESSION_LEVEL, Z_DEFLATED, + WINDOW_BITS, MEM_LEVEL, Z_DEFAULT_STRATEGY)) { + return SPDYLAY_ERR_ZLIB; + } + if(Z_OK != deflateSetDictionary(&deflater->zst, (uint8_t*)hd_dict, + hd_dict_length)) { + spdylay_zlib_deflate_free(deflater); + return SPDYLAY_ERR_ZLIB; + } + return 0; +} + +int spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater, uint16_t version) +{ + const unsigned char *hd_dict; + size_t hd_dict_length; + inflater->zst.next_in = Z_NULL; + inflater->zst.avail_in = 0; + inflater->zst.zalloc = Z_NULL; + inflater->zst.zfree = Z_NULL; + inflater->version = version; + hd_dict = spdylay_select_hd_dict(&hd_dict_length, version); + if(hd_dict == NULL) { + return SPDYLAY_ERR_UNSUPPORTED_VERSION; + } + if(Z_OK != inflateInit(&inflater->zst)) { + spdylay_zlib_inflate_free(inflater); + return SPDYLAY_ERR_ZLIB; + } + return 0; +} + +void spdylay_zlib_deflate_free(spdylay_zlib *deflater) +{ + deflateEnd(&deflater->zst); +} + +void spdylay_zlib_inflate_free(spdylay_zlib *inflater) +{ + inflateEnd(&inflater->zst); +} + +ssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater, + uint8_t *out, size_t outlen, + const uint8_t *in, size_t inlen) +{ + int r; + deflater->zst.avail_in = inlen; + deflater->zst.next_in = (uint8_t*)in; + deflater->zst.avail_out = outlen; + deflater->zst.next_out = out; + r = deflate(&deflater->zst, Z_SYNC_FLUSH); + if(r == Z_OK) { + return outlen-deflater->zst.avail_out; + } else { + return SPDYLAY_ERR_ZLIB; + } +} + +size_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len) +{ + return deflateBound(&deflater->zst, len); +} + +ssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater, + spdylay_buffer* buf, + const uint8_t *in, size_t inlen) +{ + int r; + inflater->zst.avail_in = inlen; + inflater->zst.next_in = (uint8_t*)in; + while(1) { + if(spdylay_buffer_avail(buf) == 0) { + if((r = spdylay_buffer_alloc(buf)) != 0) { + return r; + } + } + inflater->zst.avail_out = spdylay_buffer_avail(buf); + inflater->zst.next_out = spdylay_buffer_get(buf); + r = inflate(&inflater->zst, Z_NO_FLUSH); + if(r == Z_STREAM_ERROR || r == Z_STREAM_END || r == Z_DATA_ERROR) { + return SPDYLAY_ERR_ZLIB; + } else if(r == Z_NEED_DICT) { + const uint8_t *hd_dict; + size_t hd_dict_length; + hd_dict = spdylay_select_hd_dict(&hd_dict_length, inflater->version); + assert(hd_dict); + if(Z_OK != inflateSetDictionary(&inflater->zst, (uint8_t*)hd_dict, + hd_dict_length)) { + return SPDYLAY_ERR_ZLIB; + } + } else { + if(r == Z_OK) { + size_t adv = spdylay_buffer_avail(buf)-inflater->zst.avail_out; + spdylay_buffer_advance(buf, adv); + } + if(inflater->zst.avail_in == 0 && inflater->zst.avail_out > 0) { + break; + } + } + } + return spdylay_buffer_length(buf); +} diff --git a/deps/spdylay/lib/spdylay_zlib.h b/deps/spdylay/lib/spdylay_zlib.h new file mode 100644 index 0000000..c26f2a9 --- /dev/null +++ b/deps/spdylay/lib/spdylay_zlib.h @@ -0,0 +1,120 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_ZLIB_H +#define SPDYLAY_ZLIB_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ +#include + +#include "spdylay_buffer.h" + +/* This structure is used for both deflater and inflater. */ +typedef struct { + z_stream zst; + /* The protocol version to select the dictionary later. */ + uint16_t version; +} spdylay_zlib; + +/* + * Initializes |deflater| for deflating name/values pairs in the + * frame of the protocol version |version|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The z_stream initialization failed. + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + */ +int spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater, uint16_t version); + +/* + * Initializes |inflater| for inflating name/values pairs in the + * frame of the protocol version |version|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The z_stream initialization failed. + * SPDYLAY_ERR_UNSUPPORTED_VERSION + * The version is not supported. + */ +int spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater, uint16_t version); + +/* + * Deallocates any resources allocated for |deflater|. + */ +void spdylay_zlib_deflate_free(spdylay_zlib *deflater); + +/* + * Deallocates any resources allocated for |inflater|. + */ +void spdylay_zlib_inflate_free(spdylay_zlib *inflater); + +/* + * Returns the maximum length when |len| bytes of data are deflated by + * |deflater|. + */ +size_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len); + +/* + * Deflates data stored in |in| with length |inlen|. The output is + * written to |out| with length |outlen|. This is not a strict + * requirement but |outlen| should have at least + * spdylay_zlib_deflate_hd_bound(|inlen|) bytes for successful + * operation. + * + * This function returns the number of bytes outputted if it succeeds, + * or one of the following negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The deflate operation failed. + */ +ssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater, + uint8_t *out, size_t outlen, + const uint8_t *in, size_t inlen); + +/* + * Inflates data stored in |in| with length |inlen|. The output is + * added to |buf|. + * + * This function returns the number of bytes outputted if it succeeds, + * or one of the following negative error codes: + * + * SPDYLAY_ERR_ZLIB + * The inflate operation failed. + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +ssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater, + spdylay_buffer* buf, + const uint8_t *in, size_t inlen); + +#endif /* SPDYLAY_ZLIB_H */ diff --git a/deps/spdylay/m4/README b/deps/spdylay/m4/README new file mode 100644 index 0000000..a416f2e --- /dev/null +++ b/deps/spdylay/m4/README @@ -0,0 +1 @@ +Empty m4 directory to make `autoreconf -i` happy. diff --git a/deps/spdylay/m4/ac_compile_stdcxx_11.m4 b/deps/spdylay/m4/ac_compile_stdcxx_11.m4 new file mode 100644 index 0000000..746c44e --- /dev/null +++ b/deps/spdylay/m4/ac_compile_stdcxx_11.m4 @@ -0,0 +1,22 @@ +# AC_COMPILE_STDCXX_11 +AC_DEFUN([AC_COMPILE_STDCXX_11], [ + + AC_CACHE_CHECK(if g++ supports C++11 features with -std=c++0x, + ac_cv_cxx_compile_cxx11_cxx, + [AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS -std=gnu++0x" + AC_TRY_COMPILE([ + #include + std::function func; + ],, + ac_cv_cxx_compile_cxx11_cxx=yes, ac_cv_cxx_compile_cxx11_cxx=no) + CXXFLAGS="$ac_save_CXXFLAGS" + AC_LANG_RESTORE + ]) + + if test "$ac_cv_cxx_compile_cxx11_cxx" = yes; then + AC_DEFINE(HAVE_STDCXX_11,,[Define if g++ supports C++11 features. ]) + fi +]) diff --git a/deps/spdylay/m4/ax_have_epoll.m4 b/deps/spdylay/m4/ax_have_epoll.m4 new file mode 100644 index 0000000..07ceb49 --- /dev/null +++ b/deps/spdylay/m4/ax_have_epoll.m4 @@ -0,0 +1,104 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This macro determines whether the system supports the epoll I/O event +# interface. A neat usage example would be: +# +# AX_HAVE_EPOLL( +# [AX_CONFIG_FEATURE_ENABLE(epoll)], +# [AX_CONFIG_FEATURE_DISABLE(epoll)]) +# AX_CONFIG_FEATURE( +# [epoll], [This platform supports epoll(7)], +# [HAVE_EPOLL], [This platform supports epoll(7).]) +# +# The epoll interface was added to the Linux kernel in version 2.5.45, and +# the macro verifies that a kernel newer than this is installed. This +# check is somewhat unreliable if doesn't match the +# running kernel, but it is necessary regardless, because glibc comes with +# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to +# compile and link even if the kernel is too old; the problem would then +# be detected only at runtime. +# +# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to +# epoll_wait(). The availability of that function can be tested with the +# second macro. Generally speaking, it is safe to assume that +# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the +# other way round. +# +# LICENSE +# +# Copyright (c) 2008 Peter Simons +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include +#include +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl diff --git a/deps/spdylay/tests/Makefile.am b/deps/spdylay/tests/Makefile.am new file mode 100644 index 0000000..0c89f46 --- /dev/null +++ b/deps/spdylay/tests/Makefile.am @@ -0,0 +1,45 @@ +# Spdylay - SPDY Library + +# Copyright (c) 2012 Tatsuhiro Tsujikawa + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if HAVE_CUNIT + +check_PROGRAMS = main + +OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \ + spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \ + spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c + +HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \ + spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \ + spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h + +main_SOURCES = $(HFILES) $(OBJECTS) + +main_LDADD = ${top_builddir}/lib/libspdylay.la +main_LDFLAGS = -static @CUNIT_LIBS@ +AM_CFLAGS = -Wall -g -O2 -I${top_srcdir}/lib -I${top_srcdir}/lib/includes -I${top_builddir}/lib/includes \ + @CUNIT_CFLAGS@ + +TESTS = main end_to_end.py + +endif # HAVE_CUNIT diff --git a/deps/spdylay/tests/end_to_end.py b/deps/spdylay/tests/end_to_end.py new file mode 100755 index 0000000..1fdcd1c --- /dev/null +++ b/deps/spdylay/tests/end_to_end.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +"""End to end tests for the example programs. + +This test assumes the examples have already been built. + +At the moment top_buiddir is not in the environment, but top_builddir would be +more reliable than '..', so it's worth trying to pull it from the environment. +""" + +__author__ = 'Jim Morrison ' + + +import os +import subprocess +import time +import unittest + + +_PORT = 9893 + + +def _run_server(port, args): + srcdir = os.environ.get('srcdir', '.') + testdata = '%s/testdata' % srcdir + top_builddir = os.environ.get('top_builddir', '..') + base_args = ['%s/examples/spdyd' % top_builddir, '-d', testdata] + if args: + base_args.extend(args) + base_args.extend([str(port), '%s/privkey.pem' % testdata, + '%s/cacert.pem' % testdata]) + return subprocess.Popen(base_args) + +def _check_server_up(port): + # Check this check for now. + time.sleep(1) + +def _kill_server(server): + while server.returncode is None: + server.terminate() + time.sleep(1) + server.poll() + + +class EndToEndSpdyTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.setUpServer([]) + + @classmethod + def setUpServer(cls, args): + cls.server = _run_server(_PORT, args) + _check_server_up(_PORT) + + @classmethod + def tearDownClass(cls): + _kill_server(cls.server) + + def setUp(self): + build_dir = os.environ.get('top_builddir', '..') + self.client = '%s/examples/spdycat' % build_dir + self.stdout = 'No output' + + def call(self, path, args): + full_args = [self.client,'http://localhost:%d%s' % (_PORT, path)] + args + p = subprocess.Popen(full_args, stdout=subprocess.PIPE, + stdin=subprocess.PIPE) + self.stdout, self.stderr = p.communicate() + return p.returncode + + +class EndToEndSpdy2Tests(EndToEndSpdyTests): + def testSimpleRequest(self): + self.assertEquals(0, self.call('/', [])) + + def testSimpleRequestSpdy3(self): + self.assertEquals(0, self.call('/', ['-v', '-3'])) + self.assertIn('NPN selected the protocol: spdy/3', self.stdout) + + def testFailedRequests(self): + self.assertEquals( + 2, self.call('/', ['https://localhost:25/', 'http://localhost:79'])) + + def testOneFailedRequest(self): + self.assertEquals(1, subprocess.call([self.client, 'http://localhost:2/'])) + + +class EndToEndSpdy3Tests(EndToEndSpdyTests): + @classmethod + def setUpClass(cls): + cls.setUpServer(['-3']) + + def testSimpleRequest(self): + self.assertEquals(0, self.call('/', ['-v'])) + self.assertIn('NPN selected the protocol: spdy/3', self.stdout) + + +if __name__ == '__main__': + unittest.main() diff --git a/deps/spdylay/tests/main.c b/deps/spdylay/tests/main.c new file mode 100644 index 0000000..f2b2549 --- /dev/null +++ b/deps/spdylay/tests/main.c @@ -0,0 +1,196 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +/* include test cases' include files here */ +#include "spdylay_pq_test.h" +#include "spdylay_map_test.h" +#include "spdylay_queue_test.h" +#include "spdylay_buffer_test.h" +#include "spdylay_zlib_test.h" +#include "spdylay_session_test.h" +#include "spdylay_frame_test.h" +#include "spdylay_stream_test.h" +#include "spdylay_npn_test.h" + +int init_suite1(void) +{ + return 0; +} + +int clean_suite1(void) +{ + return 0; +} + + +int main(int argc, char* argv[]) +{ + CU_pSuite pSuite = NULL; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libspdylay_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* add the tests to the suite */ + if(!CU_add_test(pSuite, "pq", test_spdylay_pq) || + !CU_add_test(pSuite, "map", test_spdylay_map) || + !CU_add_test(pSuite, "queue", test_spdylay_queue) || + !CU_add_test(pSuite, "buffer", test_spdylay_buffer) || + !CU_add_test(pSuite, "zlib_spdy2", test_spdylay_zlib_spdy2) || + !CU_add_test(pSuite, "zlib_spdy3", test_spdylay_zlib_spdy3) || + !CU_add_test(pSuite, "npn", test_spdylay_npn) || + !CU_add_test(pSuite, "session_recv", test_spdylay_session_recv) || + !CU_add_test(pSuite, "session_recv_invalid_stream_id", + test_spdylay_session_recv_invalid_stream_id) || + !CU_add_test(pSuite, "session_add_frame", + test_spdylay_session_add_frame) || + !CU_add_test(pSuite, "session_on_syn_stream_received", + test_spdylay_session_on_syn_stream_received) || + !CU_add_test(pSuite, "session_on_syn_stream_received_with_push", + test_spdylay_session_on_syn_stream_received_with_push) || + !CU_add_test(pSuite, "session_on_syn_reply_received", + test_spdylay_session_on_syn_reply_received) || + !CU_add_test(pSuite, "session_send_syn_stream", + test_spdylay_session_send_syn_stream) || + !CU_add_test(pSuite, "session_send_syn_reply", + test_spdylay_session_send_syn_reply) || + !CU_add_test(pSuite, "submit_response", test_spdylay_submit_response) || + !CU_add_test(pSuite, "submit_response_without_data", + test_spdylay_submit_response_with_null_data_read_callback) || + !CU_add_test(pSuite, "submit_request_with_data", + test_spdylay_submit_request_with_data) || + !CU_add_test(pSuite, "submit_request_without_data", + test_spdylay_submit_request_with_null_data_read_callback) || + !CU_add_test(pSuite, "submit_syn_stream", + test_spdylay_submit_syn_stream) || + !CU_add_test(pSuite, "submit_syn_reply", test_spdylay_submit_syn_reply) || + !CU_add_test(pSuite, "submit_headers", test_spdylay_submit_headers) || + !CU_add_test(pSuite, "session_reply_fail", + test_spdylay_session_reply_fail) || + !CU_add_test(pSuite, "session_on_headers_received", + test_spdylay_session_on_headers_received) || + !CU_add_test(pSuite, "session_on_window_update_received", + test_spdylay_session_on_window_update_received) || + !CU_add_test(pSuite, "session_on_ping_received", + test_spdylay_session_on_ping_received) || + !CU_add_test(pSuite, "session_on_goaway_received", + test_spdylay_session_on_goaway_received) || + !CU_add_test(pSuite, "session_on_data_received", + test_spdylay_session_on_data_received) || + !CU_add_test(pSuite, "session_on_rst_stream_received", + test_spdylay_session_on_rst_received) || + !CU_add_test(pSuite, "session_is_my_stream_id", + test_spdylay_session_is_my_stream_id) || + !CU_add_test(pSuite, "session_send_rst_stream", + test_spdylay_session_send_rst_stream) || + !CU_add_test(pSuite, "session_get_next_ob_item", + test_spdylay_session_get_next_ob_item) || + !CU_add_test(pSuite, "session_pop_next_ob_item", + test_spdylay_session_pop_next_ob_item) || + !CU_add_test(pSuite, "session_on_request_recv_callback", + test_spdylay_session_on_request_recv_callback) || + !CU_add_test(pSuite, "session_on_stream_close", + test_spdylay_session_on_stream_close) || + !CU_add_test(pSuite, "session_max_concurrent_streams", + test_spdylay_session_max_concurrent_streams) || + !CU_add_test(pSuite, "session_data_backoff_by_high_pri_frame", + test_spdylay_session_data_backoff_by_high_pri_frame) || + !CU_add_test(pSuite, "session_stop_data_with_rst_stream", + test_spdylay_session_stop_data_with_rst_stream) || + !CU_add_test(pSuite, "session_stream_close_on_syn_stream", + test_spdylay_session_stream_close_on_syn_stream) || + !CU_add_test(pSuite, "session_recv_invalid_frame", + test_spdylay_session_recv_invalid_frame) || + !CU_add_test(pSuite, "session_defer_data", + test_spdylay_session_defer_data) || + !CU_add_test(pSuite, "session_flow_control", + test_spdylay_session_flow_control) || + !CU_add_test(pSuite, "session_on_ctrl_not_send", + test_spdylay_session_on_ctrl_not_send) || + !CU_add_test(pSuite, "session_on_settings_received", + test_spdylay_session_on_settings_received) || + !CU_add_test(pSuite, "session_submit_settings", + test_spdylay_submit_settings) || + !CU_add_test(pSuite, "session_get_outbound_queue_size", + test_spdylay_session_get_outbound_queue_size) || + !CU_add_test(pSuite, "frame_unpack_nv_spdy2", + test_spdylay_frame_unpack_nv_spdy2) || + !CU_add_test(pSuite, "frame_unpack_nv_spdy3", + test_spdylay_frame_unpack_nv_spdy3) || + !CU_add_test(pSuite, "frame_count_nv_space", + test_spdylay_frame_count_nv_space) || + !CU_add_test(pSuite, "frame_count_unpack_nv_space", + test_spdylay_frame_count_unpack_nv_space) || + !CU_add_test(pSuite, "frame_pack_ping", test_spdylay_frame_pack_ping) || + !CU_add_test(pSuite, "frame_pack_goaway_spdy2", + test_spdylay_frame_pack_goaway_spdy2) || + !CU_add_test(pSuite, "frame_pack_goaway_spdy3", + test_spdylay_frame_pack_goaway_spdy3) || + !CU_add_test(pSuite, "frame_pack_syn_stream_spdy2", + test_spdylay_frame_pack_syn_stream_spdy2) || + !CU_add_test(pSuite, "frame_pack_syn_stream_spdy3", + test_spdylay_frame_pack_syn_stream_spdy3) || + !CU_add_test(pSuite, "frame_pack_syn_reply_spdy2", + test_spdylay_frame_pack_syn_reply_spdy2) || + !CU_add_test(pSuite, "frame_pack_syn_reply_spdy3", + test_spdylay_frame_pack_syn_reply_spdy3) || + !CU_add_test(pSuite, "frame_pack_headers_spdy2", + test_spdylay_frame_pack_headers_spdy2) || + !CU_add_test(pSuite, "frame_pack_headers_spdy3", + test_spdylay_frame_pack_headers_spdy3) || + !CU_add_test(pSuite, "frame_pack_window_update", + test_spdylay_frame_pack_window_update) || + !CU_add_test(pSuite, "frame_pack_settings_spdy2", + test_spdylay_frame_pack_settings_spdy2) || + !CU_add_test(pSuite, "frame_pack_settings_spdy3", + test_spdylay_frame_pack_settings_spdy3) || + !CU_add_test(pSuite, "frame_nv_sort", test_spdylay_frame_nv_sort) || + !CU_add_test(pSuite, "frame_nv_downcase", + test_spdylay_frame_nv_downcase) || + !CU_add_test(pSuite, "frame_pack_nv_duplicate_keys", + test_spdylay_frame_pack_nv_duplicate_keys) || + !CU_add_test(pSuite, "frame_nv_2to3", test_spdylay_frame_nv_2to3) || + !CU_add_test(pSuite, "frame_nv_3to2", test_spdylay_frame_nv_3to2) || + !CU_add_test(pSuite, "stream_add_pushed_stream", + test_spdylay_stream_add_pushed_stream)) { + CU_cleanup_registry(); + return CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + return CU_get_error(); +} diff --git a/deps/spdylay/tests/spdylay_buffer_test.c b/deps/spdylay/tests/spdylay_buffer_test.c new file mode 100644 index 0000000..8d992b0 --- /dev/null +++ b/deps/spdylay/tests/spdylay_buffer_test.c @@ -0,0 +1,81 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_buffer_test.h" + +#include + +#include + +#include "spdylay_buffer.h" + +void test_spdylay_buffer() +{ + spdylay_buffer buffer; + uint8_t out[1024]; + spdylay_buffer_init(&buffer, 8); + CU_ASSERT(0 == spdylay_buffer_length(&buffer)); + CU_ASSERT(0 == spdylay_buffer_avail(&buffer)); + CU_ASSERT(NULL == spdylay_buffer_get(&buffer)); + CU_ASSERT(0 == spdylay_buffer_alloc(&buffer)); + + CU_ASSERT(8 == spdylay_buffer_avail(&buffer)); + CU_ASSERT(NULL != spdylay_buffer_get(&buffer)); + memcpy(spdylay_buffer_get(&buffer), "012", 3); + spdylay_buffer_advance(&buffer, 3); + CU_ASSERT(3 == spdylay_buffer_length(&buffer)); + + CU_ASSERT(5 == spdylay_buffer_avail(&buffer)); + memcpy(spdylay_buffer_get(&buffer), "34567", 5); + spdylay_buffer_advance(&buffer, 5); + CU_ASSERT(8 == spdylay_buffer_length(&buffer)); + + CU_ASSERT(0 == spdylay_buffer_avail(&buffer)); + CU_ASSERT(0 == spdylay_buffer_alloc(&buffer)); + memcpy(spdylay_buffer_get(&buffer), "89ABCDE", 7); + spdylay_buffer_advance(&buffer, 7); + CU_ASSERT(15 == spdylay_buffer_length(&buffer)); + + CU_ASSERT(1 == spdylay_buffer_avail(&buffer)); + + spdylay_buffer_serialize(&buffer, out); + CU_ASSERT(0 == memcmp("0123456789ABCDE", out, 15)); + + spdylay_buffer_reset(&buffer); + + CU_ASSERT(0 == spdylay_buffer_length(&buffer)); + CU_ASSERT(0 == spdylay_buffer_avail(&buffer)); + CU_ASSERT(NULL == spdylay_buffer_get(&buffer)); + CU_ASSERT(0 == spdylay_buffer_alloc(&buffer)); + + CU_ASSERT(8 == spdylay_buffer_avail(&buffer)); + memcpy(spdylay_buffer_get(&buffer), "Hello", 5); + spdylay_buffer_advance(&buffer, 5); + CU_ASSERT(5 == spdylay_buffer_length(&buffer)); + + spdylay_buffer_serialize(&buffer, out); + CU_ASSERT(0 == memcmp("Hello", out, 5)); + + spdylay_buffer_free(&buffer); +} diff --git a/deps/spdylay/tests/spdylay_buffer_test.h b/deps/spdylay/tests/spdylay_buffer_test.h new file mode 100644 index 0000000..99ea485 --- /dev/null +++ b/deps/spdylay/tests/spdylay_buffer_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_BUFFER_TEST_H +#define SPDYLAY_BUFFER_TEST_H + +void test_spdylay_buffer(); + +#endif // SPDYLAY_BUFFER_TEST_H diff --git a/deps/spdylay/tests/spdylay_frame_test.c b/deps/spdylay/tests/spdylay_frame_test.c new file mode 100644 index 0000000..251ce09 --- /dev/null +++ b/deps/spdylay/tests/spdylay_frame_test.c @@ -0,0 +1,586 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_frame_test.h" + +#include + +#include "spdylay_frame.h" +#include "spdylay_helper.h" + +static const char *headers[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "x-head", "foo", + "x-head", "bar", + "version", "HTTP/1.1", + NULL +}; + +void test_spdylay_frame_unpack_nv_with(size_t len_size) +{ + uint8_t out[1024]; + char **nv; + size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size); + CU_ASSERT(0 == spdylay_frame_unpack_nv(&nv, out, inlen, len_size)); + CU_ASSERT(strcmp("method", nv[0]) == 0); + CU_ASSERT(strcmp("GET", nv[1]) == 0); + CU_ASSERT(strcmp("scheme", nv[2]) == 0); + CU_ASSERT(strcmp("https", nv[3]) == 0); + CU_ASSERT(strcmp("url", nv[4]) == 0); + CU_ASSERT(strcmp("/", nv[5]) == 0); + CU_ASSERT(strcmp("x-head", nv[6]) == 0); + CU_ASSERT(strcmp("foo", nv[7]) == 0); + CU_ASSERT(strcmp("x-head", nv[8]) == 0); + CU_ASSERT(strcmp("bar", nv[9]) == 0); + CU_ASSERT(strcmp("version", nv[10]) == 0); + CU_ASSERT(strcmp("HTTP/1.1", nv[11]) == 0); + spdylay_frame_nv_del(nv); +} + +void test_spdylay_frame_unpack_nv_spdy2() +{ + test_spdylay_frame_unpack_nv_with(2); +} + +void test_spdylay_frame_unpack_nv_spdy3() +{ + test_spdylay_frame_unpack_nv_with(4); +} + +void test_spdylay_frame_pack_nv_duplicate_keys() +{ + uint8_t out[1024]; + size_t len_size = 2; + const char *nv_src[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "X-hEad", "foo", + "x-heaD", "bar", + "version", "HTTP/1.1", + NULL + }; + char **nv = spdylay_frame_nv_norm_copy(nv_src); + /* size_t inlen = */ spdylay_frame_pack_nv(out, nv, len_size); + const uint8_t *outptr = out; + int pairs = spdylay_get_uint16(outptr); + CU_ASSERT(pairs == 5); + outptr += 2; + + int len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 6); + CU_ASSERT(memcmp(outptr, "method", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 3); + CU_ASSERT(memcmp(outptr, "GET", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 6); + CU_ASSERT(memcmp(outptr, "scheme", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 5); + CU_ASSERT(memcmp(outptr, "https", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 3); + CU_ASSERT(memcmp(outptr, "url", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 1); + CU_ASSERT(memcmp(outptr, "/", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 7); + CU_ASSERT(memcmp(outptr, "version", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 8); + CU_ASSERT(memcmp(outptr, "HTTP/1.1", len) == 0); + outptr += len; + + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 6); + CU_ASSERT(memcmp(outptr, "x-head", len) == 0); + outptr += len; + + len = spdylay_get_uint16(outptr); + outptr += 2; + CU_ASSERT(len == 7); + CU_ASSERT(memcmp(outptr, "foo\0bar", len) == 0); + outptr += len; + + spdylay_frame_nv_del(nv); +} + +void test_spdylay_frame_count_nv_space() +{ + size_t len_size = 2; + CU_ASSERT(74 == spdylay_frame_count_nv_space((char**)headers, len_size)); + len_size = 4; + CU_ASSERT(96 == spdylay_frame_count_nv_space((char**)headers, len_size)); +} + +void test_spdylay_frame_count_unpack_nv_space() +{ + size_t nvlen, buflen; + uint8_t out[1024]; + size_t len_size = 2; + size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size); + uint16_t temp; + CU_ASSERT(0 == spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, + out, inlen, len_size)); + CU_ASSERT(6 == nvlen); + CU_ASSERT(166 == buflen); + + /* Trailing garbage */ + CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, + out, inlen+2, len_size)); + + /* Change number of nv pair to a bogus value */ + temp = spdylay_get_uint16(out); + spdylay_put_uint16be(out, temp+1); + CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + len_size)); + spdylay_put_uint16be(out, temp); + + /* Change the length of name to a bogus value */ + temp = spdylay_get_uint16(out+2); + spdylay_put_uint16be(out+2, temp+1); + CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + len_size)); + spdylay_put_uint16be(out+2, 65535); + CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + len_size)); +} + +void test_spdylay_frame_pack_ping() +{ + spdylay_frame frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY2, 1); + framelen = spdylay_frame_pack_ping(&buf, &buflen, &frame.ping); + CU_ASSERT(0 == spdylay_frame_unpack_ping + (&oframe.ping, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); + CU_ASSERT(1 == oframe.ping.unique_id); + free(buf); + spdylay_frame_ping_free(&oframe.ping); + spdylay_frame_ping_free(&frame.ping); +} + +void test_spdylay_frame_pack_goaway_version(uint16_t version) +{ + spdylay_frame frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + spdylay_frame_goaway_init(&frame.goaway, version, 1000000007, + SPDYLAY_GOAWAY_PROTOCOL_ERROR); + framelen = spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway); + CU_ASSERT(0 == spdylay_frame_unpack_goaway + (&oframe.goaway, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); + CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id); + if(version == SPDYLAY_PROTO_SPDY2) { + /* The status code is ignored in SPDY/2 */ + CU_ASSERT(0 == oframe.goaway.status_code); + } else if(version == SPDYLAY_PROTO_SPDY3) { + CU_ASSERT(SPDYLAY_GOAWAY_PROTOCOL_ERROR == oframe.goaway.status_code); + } + CU_ASSERT(version == oframe.headers.hd.version); + CU_ASSERT(SPDYLAY_GOAWAY == oframe.headers.hd.type); + CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.headers.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + free(buf); + spdylay_frame_goaway_free(&oframe.goaway); + spdylay_frame_goaway_free(&frame.goaway); +} + +void test_spdylay_frame_pack_goaway_spdy2() +{ + test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_frame_pack_goaway_spdy3() +{ + test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY3); +} + +void test_spdylay_frame_pack_syn_stream_version(uint16_t version) +{ + spdylay_zlib deflater, inflater; + spdylay_frame frame, oframe; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + spdylay_buffer inflatebuf; + ssize_t framelen; + spdylay_buffer_init(&inflatebuf, 4096); + spdylay_zlib_deflate_hd_init(&deflater, version); + spdylay_zlib_inflate_hd_init(&inflater, version); + spdylay_frame_syn_stream_init(&frame.syn_stream, version, + SPDYLAY_CTRL_FLAG_FIN, 65536, 1000000007, 3, + spdylay_frame_nv_copy(headers)); + framelen = spdylay_frame_pack_syn_stream(&buf, &buflen, + &nvbuf, &nvbuflen, + &frame.syn_stream, &deflater); + CU_ASSERT(0 == spdylay_frame_unpack_syn_stream + (&oframe.syn_stream, + &inflatebuf, + &nvbuf, &nvbuflen, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH, + &inflater)); + CU_ASSERT(65536 == oframe.syn_stream.stream_id); + CU_ASSERT(1000000007 == oframe.syn_stream.assoc_stream_id); + CU_ASSERT(version == oframe.syn_stream.hd.version); + CU_ASSERT(SPDYLAY_SYN_STREAM == oframe.syn_stream.hd.type); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.syn_stream.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + CU_ASSERT(strcmp("method", oframe.syn_stream.nv[0]) == 0); + CU_ASSERT(strcmp("GET", oframe.syn_stream.nv[1]) == 0); + CU_ASSERT(NULL == oframe.syn_stream.nv[12]); + free(buf); + free(nvbuf); + spdylay_frame_syn_stream_free(&oframe.syn_stream); + spdylay_frame_syn_stream_free(&frame.syn_stream); + spdylay_zlib_inflate_free(&inflater); + spdylay_zlib_deflate_free(&deflater); + spdylay_buffer_free(&inflatebuf); +} + +void test_spdylay_frame_pack_syn_stream_spdy2() +{ + test_spdylay_frame_pack_syn_stream_version(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_frame_pack_syn_stream_spdy3() +{ + test_spdylay_frame_pack_syn_stream_version(SPDYLAY_PROTO_SPDY3); +} + +void test_spdylay_frame_pack_syn_reply_version(uint16_t version) +{ + spdylay_zlib deflater, inflater; + spdylay_frame frame, oframe; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + spdylay_buffer inflatebuf; + ssize_t framelen; + spdylay_buffer_init(&inflatebuf, 4096); + spdylay_zlib_deflate_hd_init(&deflater, version); + spdylay_zlib_inflate_hd_init(&inflater, version); + spdylay_frame_syn_reply_init(&frame.syn_reply, version, + SPDYLAY_CTRL_FLAG_FIN, 3, + spdylay_frame_nv_copy(headers)); + framelen = spdylay_frame_pack_syn_reply(&buf, &buflen, + &nvbuf, &nvbuflen, + &frame.syn_reply, &deflater); + CU_ASSERT(0 == spdylay_frame_unpack_syn_reply + (&oframe.syn_reply, + &inflatebuf, + &nvbuf, &nvbuflen, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH, + &inflater)); + CU_ASSERT(3 == oframe.syn_reply.stream_id); + CU_ASSERT(version == oframe.syn_reply.hd.version); + CU_ASSERT(SPDYLAY_SYN_REPLY == oframe.syn_reply.hd.type); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.syn_reply.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + CU_ASSERT(strcmp("method", oframe.syn_reply.nv[0]) == 0); + CU_ASSERT(strcmp("GET", oframe.syn_reply.nv[1]) == 0); + CU_ASSERT(NULL == oframe.syn_reply.nv[12]); + free(buf); + free(nvbuf); + spdylay_frame_syn_reply_free(&oframe.syn_reply); + spdylay_frame_syn_reply_free(&frame.syn_reply); + spdylay_zlib_inflate_free(&inflater); + spdylay_zlib_deflate_free(&deflater); + spdylay_buffer_free(&inflatebuf); +} + +void test_spdylay_frame_pack_syn_reply_spdy2() +{ + test_spdylay_frame_pack_syn_reply_version(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_frame_pack_syn_reply_spdy3() +{ + test_spdylay_frame_pack_syn_reply_version(SPDYLAY_PROTO_SPDY3); +} + +void test_spdylay_frame_pack_headers_version(uint16_t version) +{ + spdylay_zlib deflater, inflater; + spdylay_frame frame, oframe; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + spdylay_buffer inflatebuf; + ssize_t framelen; + spdylay_buffer_init(&inflatebuf, 4096); + spdylay_zlib_deflate_hd_init(&deflater, version); + spdylay_zlib_inflate_hd_init(&inflater, version); + spdylay_frame_headers_init(&frame.headers, version, + SPDYLAY_CTRL_FLAG_FIN, 3, + spdylay_frame_nv_copy(headers)); + framelen = spdylay_frame_pack_headers(&buf, &buflen, + &nvbuf, &nvbuflen, + &frame.headers, &deflater); + CU_ASSERT(0 == spdylay_frame_unpack_headers + (&oframe.headers, + &inflatebuf, + &nvbuf, &nvbuflen, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH, + &inflater)); + CU_ASSERT(3 == oframe.headers.stream_id); + CU_ASSERT(version == oframe.headers.hd.version); + CU_ASSERT(SPDYLAY_HEADERS == oframe.headers.hd.type); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == oframe.headers.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + CU_ASSERT(strcmp("method", oframe.headers.nv[0]) == 0); + CU_ASSERT(strcmp("GET", oframe.headers.nv[1]) == 0); + CU_ASSERT(NULL == oframe.headers.nv[12]); + free(buf); + free(nvbuf); + spdylay_frame_headers_free(&oframe.headers); + spdylay_frame_headers_free(&frame.headers); + spdylay_zlib_inflate_free(&inflater); + spdylay_zlib_deflate_free(&deflater); + spdylay_buffer_free(&inflatebuf); +} + +void test_spdylay_frame_pack_headers_spdy2() +{ + test_spdylay_frame_pack_headers_version(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_frame_pack_headers_spdy3() +{ + test_spdylay_frame_pack_headers_version(SPDYLAY_PROTO_SPDY3); +} + +void test_spdylay_frame_pack_window_update() +{ + spdylay_frame frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3, + 1000000007, 4096); + framelen = spdylay_frame_pack_window_update(&buf, &buflen, + &frame.window_update); + CU_ASSERT(0 == spdylay_frame_unpack_window_update + (&oframe.window_update, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); + CU_ASSERT(1000000007 == oframe.window_update.stream_id); + CU_ASSERT(4096 == oframe.window_update.delta_window_size); + CU_ASSERT(SPDYLAY_PROTO_SPDY3 == oframe.headers.hd.version); + CU_ASSERT(SPDYLAY_WINDOW_UPDATE == oframe.headers.hd.type); + CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.headers.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + free(buf); + spdylay_frame_window_update_free(&oframe.window_update); + spdylay_frame_window_update_free(&frame.window_update); +} + + +void test_spdylay_frame_pack_settings_version(uint16_t version) +{ + spdylay_frame frame, oframe; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; + int i; + spdylay_settings_entry iv[3]; + iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH; + iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE; + iv[0].value = 256; + iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv[1].value = 100; + iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv[2].value = 65536; + + spdylay_frame_settings_init + (&frame.settings, version, SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS, + spdylay_frame_iv_copy(iv, 3), 3); + framelen = spdylay_frame_pack_settings(&buf, &buflen, &frame.settings); + CU_ASSERT(8+4+3*8 == framelen); + + CU_ASSERT(0 == spdylay_frame_unpack_settings + (&oframe.settings, + &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, + &buf[SPDYLAY_FRAME_HEAD_LENGTH], + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); + + CU_ASSERT(version == oframe.settings.hd.version); + CU_ASSERT(SPDYLAY_SETTINGS == oframe.settings.hd.type); + CU_ASSERT(SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS == oframe.settings.hd.flags); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.settings.hd.length); + + CU_ASSERT(3 == oframe.settings.niv); + for(i = 0; i < 3; ++i) { + CU_ASSERT(iv[i].settings_id == oframe.settings.iv[i].settings_id); + CU_ASSERT(iv[i].flags == oframe.settings.iv[i].flags); + CU_ASSERT(iv[i].value == oframe.settings.iv[i].value); + } + + free(buf); + spdylay_frame_settings_free(&frame.settings); + spdylay_frame_settings_free(&oframe.settings); +} + +void test_spdylay_frame_pack_settings_spdy2() +{ + test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_frame_pack_settings_spdy3() +{ + test_spdylay_frame_pack_settings_version(SPDYLAY_PROTO_SPDY3); +} + +void test_spdylay_frame_nv_sort() +{ + char *nv[7]; + nv[0] = (char*)"version"; + nv[1] = (char*)"HTTP/1.1"; + nv[2] = (char*)"method"; + nv[3] = (char*)"GET"; + nv[4] = (char*)"scheme"; + nv[5] = (char*)"https"; + nv[6] = NULL; + spdylay_frame_nv_sort(nv); + CU_ASSERT(strcmp("method", nv[0]) == 0); + CU_ASSERT(strcmp("GET", nv[1]) == 0); + CU_ASSERT(strcmp("scheme", nv[2]) == 0); + CU_ASSERT(strcmp("https", nv[3]) == 0); + CU_ASSERT(strcmp("version", nv[4]) == 0); + CU_ASSERT(strcmp("HTTP/1.1", nv[5]) == 0); +} + +void test_spdylay_frame_nv_downcase() +{ + const char *nv_src[] = { + "VERSION", "HTTP/1.1", + "Content-Length", "1000000007", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_downcase(nv); + CU_ASSERT(0 == strcmp("version", nv[0])); + CU_ASSERT(0 == strcmp("HTTP/1.1", nv[1])); + CU_ASSERT(0 == strcmp("content-length", nv[2])); + CU_ASSERT(0 == strcmp("1000000007", nv[3])); + spdylay_frame_nv_del(nv); +} + +void test_spdylay_frame_nv_2to3() +{ + const char *nv_src[] = { + "host", "localhost", + "method", "GET", + "url", "/", + "accept", "*/*", + "scheme", "https", + "status", "200 OK", + "version", "HTTP/1.1", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_2to3(nv); + CU_ASSERT(0 == strcmp(":host", nv[0])); + CU_ASSERT(0 == strcmp(":method", nv[2])); + CU_ASSERT(0 == strcmp(":path", nv[4])); + CU_ASSERT(0 == strcmp("accept", nv[6])); + CU_ASSERT(0 == strcmp(":scheme", nv[8])); + CU_ASSERT(0 == strcmp(":status", nv[10])); + CU_ASSERT(0 == strcmp(":version", nv[12])); + spdylay_frame_nv_del(nv); +} + +void test_spdylay_frame_nv_3to2() +{ + const char *nv_src[] = { + ":host", "localhost", + ":method", "GET", + ":path", "/", + "accept", "*/*", + ":scheme", "https", + ":status", "200 OK", + ":version", "HTTP/1.1", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_3to2(nv); + CU_ASSERT(0 == strcmp("host", nv[0])); + CU_ASSERT(0 == strcmp("method", nv[2])); + CU_ASSERT(0 == strcmp("url", nv[4])); + CU_ASSERT(0 == strcmp("accept", nv[6])); + CU_ASSERT(0 == strcmp("scheme", nv[8])); + CU_ASSERT(0 == strcmp("status", nv[10])); + CU_ASSERT(0 == strcmp("version", nv[12])); + spdylay_frame_nv_del(nv); +} diff --git a/deps/spdylay/tests/spdylay_frame_test.h b/deps/spdylay/tests/spdylay_frame_test.h new file mode 100644 index 0000000..a53aa4a --- /dev/null +++ b/deps/spdylay/tests/spdylay_frame_test.h @@ -0,0 +1,50 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_FRAME_TEST_H +#define SPDYLAY_FRAME_TEST_H + +void test_spdylay_frame_unpack_nv_spdy2(); +void test_spdylay_frame_unpack_nv_spdy3(); +void test_spdylay_frame_pack_nv_duplicate_keys(); +void test_spdylay_frame_count_nv_space(); +void test_spdylay_frame_count_unpack_nv_space(); +void test_spdylay_frame_pack_ping(); +void test_spdylay_frame_pack_goaway_spdy2(); +void test_spdylay_frame_pack_goaway_spdy3(); +void test_spdylay_frame_pack_syn_stream_spdy2(); +void test_spdylay_frame_pack_syn_stream_spdy3(); +void test_spdylay_frame_pack_syn_reply_spdy2(); +void test_spdylay_frame_pack_syn_reply_spdy3(); +void test_spdylay_frame_pack_headers_spdy2(); +void test_spdylay_frame_pack_headers_spdy3(); +void test_spdylay_frame_pack_window_update(); +void test_spdylay_frame_pack_settings_spdy2(); +void test_spdylay_frame_pack_settings_spdy3(); +void test_spdylay_frame_nv_sort(); +void test_spdylay_frame_nv_downcase(); +void test_spdylay_frame_nv_2to3(); +void test_spdylay_frame_nv_3to2(); + +#endif /* SPDYLAY_FRAME_TEST_H */ diff --git a/deps/spdylay/tests/spdylay_map_test.c b/deps/spdylay/tests/spdylay_map_test.c new file mode 100644 index 0000000..99b2cb9 --- /dev/null +++ b/deps/spdylay/tests/spdylay_map_test.c @@ -0,0 +1,65 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_map_test.h" + +#include + +#include "spdylay_map.h" + +void test_spdylay_map() +{ + spdylay_map map; + spdylay_map_init(&map); + CU_ASSERT(0 == spdylay_map_insert(&map, 1, "foo")); + CU_ASSERT(strcmp("foo", spdylay_map_find(&map, 1)) == 0); + CU_ASSERT(1 == spdylay_map_size(&map)); + CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT == spdylay_map_insert(&map, 1, "FOO")); + CU_ASSERT(1 == spdylay_map_size(&map)); + CU_ASSERT(strcmp("foo", spdylay_map_find(&map, 1)) == 0); + CU_ASSERT(0 == spdylay_map_insert(&map, 2, "bar")); + CU_ASSERT(2 == spdylay_map_size(&map)); + CU_ASSERT(0 == spdylay_map_insert(&map, 3, "baz")); + CU_ASSERT(3 == spdylay_map_size(&map)); + CU_ASSERT(0 == spdylay_map_insert(&map, 4, "shrubbery")); + CU_ASSERT(4 == spdylay_map_size(&map)); + CU_ASSERT(strcmp("baz", spdylay_map_find(&map, 3)) == 0); + + spdylay_map_erase(&map, 3); + CU_ASSERT(3 == spdylay_map_size(&map)); + CU_ASSERT(NULL == spdylay_map_find(&map, 3)); + spdylay_map_erase(&map, 1); + CU_ASSERT(2 == spdylay_map_size(&map)); + CU_ASSERT(NULL == spdylay_map_find(&map, 1)); + + /* Erasing non-existent entry */ + spdylay_map_erase(&map, 1); + CU_ASSERT(2 == spdylay_map_size(&map)); + CU_ASSERT(NULL == spdylay_map_find(&map, 1)); + + CU_ASSERT(strcmp("bar", spdylay_map_find(&map, 2)) == 0); + CU_ASSERT(strcmp("shrubbery", spdylay_map_find(&map, 4)) == 0); + + spdylay_map_free(&map); +} diff --git a/deps/spdylay/tests/spdylay_map_test.h b/deps/spdylay/tests/spdylay_map_test.h new file mode 100644 index 0000000..65900a9 --- /dev/null +++ b/deps/spdylay/tests/spdylay_map_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_MAP_TEST_H +#define SPDYLAY_MAP_TEST_H + +void test_spdylay_map(); + +#endif /* SPDYLAY_MAP_TEST_H */ diff --git a/deps/spdylay/tests/spdylay_npn_test.c b/deps/spdylay/tests/spdylay_npn_test.c new file mode 100644 index 0000000..3b28cc3 --- /dev/null +++ b/deps/spdylay/tests/spdylay_npn_test.c @@ -0,0 +1,80 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +static void spdy2() +{ + const unsigned char spdy[] = { + 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + 6, 's', 'p', 'd', 'y', '/', '2', + 6, 's', 'p', 'd', 'y', '/', '3' + }; + unsigned char outlen; + unsigned char* out; + CU_ASSERT(SPDYLAY_PROTO_SPDY2 == + spdylay_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); + CU_ASSERT(6 == outlen); + CU_ASSERT(memcmp("spdy/2", out, outlen) == 0); +} + +static void http11() +{ + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', + 8, 's', 'p', 'd', 'y', '/', '2', '.', '1', + 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + }; + unsigned char outlen; + unsigned char* out; + CU_ASSERT(0 == spdylay_select_next_protocol(&out, &outlen, + spdy, sizeof(spdy))); + CU_ASSERT(8 == outlen); + CU_ASSERT(memcmp("http/1.1", out, outlen) == 0); +} + +static void no_overlap() +{ + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', + 8, 's', 'p', 'd', 'y', '/', '2', '.', '1', + 8, 'h', 't', 't', 'p', '/', '1', '.', '0', + }; + unsigned char outlen = 0; + unsigned char* out = NULL; + CU_ASSERT(-1 == spdylay_select_next_protocol(&out, &outlen, + spdy, sizeof(spdy))); + CU_ASSERT(0 == outlen); + CU_ASSERT(NULL == out); +} + +void test_spdylay_npn() +{ + spdy2(); + http11(); + no_overlap(); +} diff --git a/deps/spdylay/tests/spdylay_npn_test.h b/deps/spdylay/tests/spdylay_npn_test.h new file mode 100644 index 0000000..59a6c45 --- /dev/null +++ b/deps/spdylay/tests/spdylay_npn_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Twist Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_NPN_TEST_H +#define SPDYLAY_NPN_TEST_H + +void test_spdylay_npn(); + +#endif // SPDYLAY_NPN_TEST_H diff --git a/deps/spdylay/tests/spdylay_pq_test.c b/deps/spdylay/tests/spdylay_pq_test.c new file mode 100644 index 0000000..e5bfef6 --- /dev/null +++ b/deps/spdylay/tests/spdylay_pq_test.c @@ -0,0 +1,66 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_pq_test.h" + +#include + +#include "spdylay_pq.h" + +static int pq_compar(const void *lhs, const void *rhs) +{ + return strcmp(lhs, rhs); +} + +void test_spdylay_pq() +{ + spdylay_pq pq; + spdylay_pq_init(&pq, pq_compar); + CU_ASSERT(spdylay_pq_empty(&pq)); + CU_ASSERT(0 == spdylay_pq_size(&pq)); + CU_ASSERT(0 == spdylay_pq_push(&pq, "foo")); + CU_ASSERT(0 == spdylay_pq_empty(&pq)); + CU_ASSERT(1 == spdylay_pq_size(&pq)); + CU_ASSERT(strcmp("foo", spdylay_pq_top(&pq)) == 0); + CU_ASSERT(0 == spdylay_pq_push(&pq, "bar")); + CU_ASSERT(strcmp("bar", spdylay_pq_top(&pq)) == 0); + CU_ASSERT(0 == spdylay_pq_push(&pq, "baz")); + CU_ASSERT(strcmp("bar", spdylay_pq_top(&pq)) == 0); + CU_ASSERT(0 == spdylay_pq_push(&pq, "C")); + CU_ASSERT(4 == spdylay_pq_size(&pq)); + CU_ASSERT(strcmp("C", spdylay_pq_top(&pq)) == 0); + spdylay_pq_pop(&pq); + CU_ASSERT(3 == spdylay_pq_size(&pq)); + CU_ASSERT(strcmp("bar", spdylay_pq_top(&pq)) == 0); + spdylay_pq_pop(&pq); + CU_ASSERT(strcmp("baz", spdylay_pq_top(&pq)) == 0); + spdylay_pq_pop(&pq); + CU_ASSERT(strcmp("foo", spdylay_pq_top(&pq)) == 0); + spdylay_pq_pop(&pq); + CU_ASSERT(spdylay_pq_empty(&pq)); + CU_ASSERT(0 == spdylay_pq_size(&pq)); + CU_ASSERT(0 == spdylay_pq_top(&pq)); + spdylay_pq_free(&pq); +} + diff --git a/deps/spdylay/tests/spdylay_pq_test.h b/deps/spdylay/tests/spdylay_pq_test.h new file mode 100644 index 0000000..1856c04 --- /dev/null +++ b/deps/spdylay/tests/spdylay_pq_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_PQ_TEST_H +#define SPDYLAY_PQ_TEST_H + +void test_spdylay_pq(); + +#endif /* SPDYLAY_PQ_TEST_H */ diff --git a/deps/spdylay/tests/spdylay_queue_test.c b/deps/spdylay/tests/spdylay_queue_test.c new file mode 100644 index 0000000..d46526f --- /dev/null +++ b/deps/spdylay/tests/spdylay_queue_test.c @@ -0,0 +1,49 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_queue_test.h" + +#include + +#include "spdylay_queue.h" + +void test_spdylay_queue() +{ + int ints[] = { 1, 2, 3, 4, 5 }; + int i; + spdylay_queue queue; + spdylay_queue_init(&queue); + CU_ASSERT(spdylay_queue_empty(&queue)); + for(i = 0; i < 5; ++i) { + spdylay_queue_push(&queue, &ints[i]); + CU_ASSERT_EQUAL(ints[0], *(int*)(spdylay_queue_front(&queue))); + CU_ASSERT(!spdylay_queue_empty(&queue)); + } + for(i = 0; i < 5; ++i) { + CU_ASSERT_EQUAL(ints[i], *(int*)(spdylay_queue_front(&queue))); + spdylay_queue_pop(&queue); + } + CU_ASSERT(spdylay_queue_empty(&queue)); + spdylay_queue_free(&queue); +} diff --git a/deps/spdylay/tests/spdylay_queue_test.h b/deps/spdylay/tests/spdylay_queue_test.h new file mode 100644 index 0000000..3540837 --- /dev/null +++ b/deps/spdylay/tests/spdylay_queue_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_QUEUE_TEST_H +#define SPDYLAY_QUEUE_TEST_H + +void test_spdylay_queue(); + +#endif // SPDYLAY_QUEUE_TEST_H diff --git a/deps/spdylay/tests/spdylay_session_test.c b/deps/spdylay/tests/spdylay_session_test.c new file mode 100644 index 0000000..fbe6c7a --- /dev/null +++ b/deps/spdylay/tests/spdylay_session_test.c @@ -0,0 +1,2045 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_session_test.h" + +#include + +#include +#include +#include + +#include "spdylay_session.h" +#include "spdylay_stream.h" + +typedef struct { + uint8_t buf[4096]; + size_t length; +} accumulator; + +typedef struct { + uint8_t data[8192]; + uint8_t* datamark; + uint8_t* datalimit; + size_t feedseq[8192]; + size_t seqidx; +} scripted_data_feed; + +typedef struct { + accumulator *acc; + scripted_data_feed *df; + int ctrl_recv_cb_called, invalid_ctrl_recv_cb_called; + int ctrl_send_cb_called; + spdylay_frame_type sent_frame_type; + int ctrl_not_send_cb_called; + spdylay_frame_type not_sent_frame_type; + int not_sent_error; + int stream_close_cb_called; + size_t data_source_length; + int32_t stream_id; + size_t block_count; +} my_user_data; + +static void scripted_data_feed_init(scripted_data_feed *df, + uint8_t *data, size_t data_length) +{ + memset(df, 0, sizeof(scripted_data_feed)); + memcpy(df->data, data, data_length); + df->datamark = df->data; + df->datalimit = df->data+data_length; + df->feedseq[0] = data_length; +} + +static ssize_t null_send_callback(spdylay_session *session, + const uint8_t* data, size_t len, int flags, + void *user_data) +{ + return len; +} + +static ssize_t fail_send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data) +{ + return SPDYLAY_ERR_CALLBACK_FAILURE; +} + +static ssize_t scripted_recv_callback(spdylay_session *session, + uint8_t* data, size_t len, int flags, + void *user_data) +{ + scripted_data_feed *df = ((my_user_data*)user_data)->df; + size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + if(wlen <= len) { + ++df->seqidx; + } else { + df->feedseq[df->seqidx] -= wlen; + } + return wlen; +} + +static ssize_t accumulator_send_callback(spdylay_session *session, + const uint8_t *buf, size_t len, + int flags, void* user_data) +{ + accumulator *acc = ((my_user_data*)user_data)->acc; + assert(acc->length+len < sizeof(acc->buf)); + memcpy(acc->buf+acc->length, buf, len); + acc->length += len; + return len; +} + +static void on_ctrl_recv_callback(spdylay_session *session, + spdylay_frame_type type, + spdylay_frame *frame, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ++ud->ctrl_recv_cb_called; +} + +static void on_invalid_ctrl_recv_callback(spdylay_session *session, + spdylay_frame_type type, + spdylay_frame *frame, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ++ud->invalid_ctrl_recv_cb_called; +} + +static void on_ctrl_send_callback(spdylay_session *session, + spdylay_frame_type type, + spdylay_frame *frame, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ++ud->ctrl_send_cb_called; + ud->sent_frame_type = type; +} + +static void on_ctrl_not_send_callback(spdylay_session *session, + spdylay_frame_type type, + spdylay_frame *frame, + int error, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ++ud->ctrl_not_send_cb_called; + ud->not_sent_frame_type = type; + ud->not_sent_error = error; +} + +static ssize_t fixed_length_data_source_read_callback +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t len, int *eof, + spdylay_data_source *source, void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + size_t wlen; + if(len < ud->data_source_length) { + wlen = len; + } else { + wlen = ud->data_source_length; + } + ud->data_source_length -= wlen; + if(ud->data_source_length == 0) { + *eof = 1; + } + return wlen; +} + +static void on_request_recv_callback(spdylay_session *session, + int32_t stream_id, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ud->stream_id = stream_id; +} + +static void no_stream_user_data_stream_close_callback +(spdylay_session *session, + int32_t stream_id, + spdylay_status_code status_code, + void *user_data) +{ + my_user_data* my_data = (my_user_data*)user_data; + ++my_data->stream_close_cb_called; +} + +static char** dup_nv(const char **src) +{ + return spdylay_frame_nv_copy(src); +} + +static spdylay_settings_entry* dup_iv(const spdylay_settings_entry *iv, + size_t niv) +{ + return spdylay_frame_iv_copy(iv, niv); +} + +void test_spdylay_session_recv() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + const char *nv[] = { + "url", "/", NULL + }; + uint8_t *framedata = NULL, *nvbuf = NULL; + size_t framedatalen = 0, nvbuflen = 0; + ssize_t framelen; + spdylay_frame frame; + int i; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + user_data.df = &df; + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, + 1, 0, 3, dup_nv(nv)); + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, + &session->hd_deflater); + scripted_data_feed_init(&df, framedata, framelen); + /* Send 1 byte per each read */ + for(i = 0; i < framelen; ++i) { + df.feedseq[i] = 1; + } + free(framedata); + free(nvbuf); + spdylay_frame_syn_stream_free(&frame.syn_stream); + + user_data.ctrl_recv_cb_called = 0; + while(df.seqidx < framelen) { + CU_ASSERT(0 == spdylay_session_recv(session)); + } + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + spdylay_session_del(session); +} + +void test_spdylay_session_add_frame() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + accumulator_send_callback, + NULL, + }; + accumulator acc; + my_user_data user_data; + const char *nv[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "version", "HTTP/1.1", + NULL + }; + spdylay_frame *frame; + spdylay_syn_stream_aux_data *aux_data = + malloc(sizeof(spdylay_syn_stream_aux_data)); + const uint8_t hd_ans1[] = { + 0x80, 0x02, 0x00, 0x01 + }; + uint32_t temp32; + memset(aux_data, 0, sizeof(spdylay_syn_stream_aux_data)); + acc.length = 0; + user_data.acc = &acc; + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &user_data)); + + frame = malloc(sizeof(spdylay_frame)); + spdylay_frame_syn_stream_init(&frame->syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 0, 0, 3, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame, + aux_data)); + CU_ASSERT(0 == spdylay_pq_empty(&session->ob_ss_pq)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(memcmp(hd_ans1, acc.buf, 4) == 0); + /* check stream id */ + memcpy(&temp32, &acc.buf[8], 4); + temp32 = ntohl(temp32); + CU_ASSERT(1 == temp32); + /* check assoc stream id */ + memcpy(&temp32, &acc.buf[12], 4); + temp32 = ntohl(temp32); + CU_ASSERT(0 == temp32); + /* check pri */ + temp32 = (acc.buf[16] >> 6) & 0x3; + CU_ASSERT(3 == temp32); + + spdylay_session_del(session); +} + +void test_spdylay_session_recv_invalid_stream_id() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + NULL, + scripted_recv_callback, + NULL, + on_invalid_ctrl_recv_callback + }; + scripted_data_feed df; + my_user_data user_data; + const char *nv[] = { NULL }; + uint8_t *framedata = NULL, *nvbuf = NULL; + size_t framedatalen = 0, nvbuflen = 0; + ssize_t framelen; + spdylay_frame frame; + + user_data.df = &df; + user_data.invalid_ctrl_recv_cb_called = 0; + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, + &session->hd_deflater); + scripted_data_feed_init(&df, framedata, framelen); + spdylay_frame_syn_stream_free(&frame.syn_stream); + + CU_ASSERT(0 == spdylay_session_recv(session)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 100, dup_nv(nv)); + framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_reply, + &session->hd_deflater); + scripted_data_feed_init(&df, framedata, framelen); + spdylay_frame_syn_reply_free(&frame.syn_reply); + + CU_ASSERT(0 == spdylay_session_recv(session)); + CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + + free(framedata); + free(nvbuf); + spdylay_session_del(session); +} + +void test_spdylay_session_on_syn_stream_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; + spdylay_frame frame; + spdylay_stream *stream; + int32_t stream_id = 1; + uint8_t pri = 3; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, + stream_id, 0, pri, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + stream = spdylay_session_get_stream(session, stream_id); + CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state); + CU_ASSERT(pri == stream->pri); + + /* Same stream ID twice leads stream error */ + user_data.invalid_ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + CU_ASSERT(SPDYLAY_STREAM_CLOSING == stream->state); + + /* assoc_stream_id != 0 from client is invalid. */ + frame.syn_stream.stream_id = 3; + frame.syn_stream.assoc_stream_id = 1; + user_data.invalid_ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + + /* Upper cased name/value pairs */ + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, + 5, 0, 3, dup_nv(upcase_nv)); + user_data.invalid_ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + + /* Stream ID less than previouly received SYN_STREAM leads session + error */ + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, + 3, 0, 3, dup_nv(nv)); + user_data.invalid_ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + CU_ASSERT(session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_on_syn_stream_received_with_push() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + spdylay_frame frame; + spdylay_stream *stream; + int32_t stream_id = 2; + int32_t assoc_stream_id = 1; + uint8_t pri = 3; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_session_open_stream(session, assoc_stream_id, SPDYLAY_CTRL_FLAG_NONE, + pri, SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL, + stream_id, assoc_stream_id, pri, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + stream = spdylay_session_get_stream(session, stream_id); + CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state); + + /* assoc_stream_id == 0 is invalid */ + frame.syn_stream.stream_id = 4; + frame.syn_stream.assoc_stream_id = 0; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + /* Push without SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL is invalid */ + frame.syn_stream.stream_id = 6; + frame.syn_stream.assoc_stream_id = 1; + frame.syn_stream.hd.flags = SPDYLAY_CTRL_FLAG_FIN; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + + /* Push to non-existent stream is invalid */ + frame.syn_stream.stream_id = 8; + frame.syn_stream.assoc_stream_id = 3; + frame.syn_stream.hd.flags = SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_on_syn_reply_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; + spdylay_frame frame; + spdylay_stream *stream; + spdylay_outbound_item *item; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + CU_ASSERT(SPDYLAY_STREAM_OPENED == + ((spdylay_stream*)spdylay_map_find(&session->streams, 1))->state); + + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + CU_ASSERT(SPDYLAY_STREAM_CLOSING == + ((spdylay_stream*)spdylay_map_find(&session->streams, 1))->state); + + /* Check the situation when SYN_REPLY is received after peer sends + FIN */ + stream = spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); + frame.syn_reply.stream_id = 3; + + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_reply_free(&frame.syn_reply); + + /* Upper cased name/value pairs */ + spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(upcase_nv)); + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_reply_free(&frame.syn_reply); + + spdylay_session_del(session); + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, + &user_data); + + /* Multiple SYN_REPLY frames for the same active stream ID */ + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_PROTO_SPDY3, + SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv)); + user_data.invalid_ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(SPDYLAY_RST_STREAM == item->frame_type); + CU_ASSERT(SPDYLAY_STREAM_IN_USE == item->frame->rst_stream.status_code); + + spdylay_frame_syn_reply_free(&frame.syn_reply); + + spdylay_session_del(session); +} + +void test_spdylay_session_send_syn_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + null_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { NULL }; + spdylay_frame *frame = malloc(sizeof(spdylay_frame)); + spdylay_stream *stream; + spdylay_syn_stream_aux_data *aux_data = + malloc(sizeof(spdylay_syn_stream_aux_data)); + memset(aux_data, 0, sizeof(spdylay_syn_stream_aux_data)); + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + spdylay_frame_syn_stream_init(&frame->syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 0, 0, 3, dup_nv(nv)); + spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame, aux_data); + CU_ASSERT(0 == spdylay_session_send(session)); + stream = spdylay_session_get_stream(session, 1); + CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state); + + spdylay_session_del(session); +} + +void test_spdylay_session_send_syn_reply() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + null_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { NULL }; + spdylay_frame *frame = malloc(sizeof(spdylay_frame)); + spdylay_stream *stream; + + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, NULL)); + spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_syn_reply_init(&frame->syn_reply, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 2, dup_nv(nv)); + spdylay_session_add_frame(session, SPDYLAY_SYN_REPLY, frame, NULL); + CU_ASSERT(0 == spdylay_session_send(session)); + stream = spdylay_session_get_stream(session, 2); + CU_ASSERT(SPDYLAY_STREAM_OPENED == stream->state); + + spdylay_session_del(session); +} + +void test_spdylay_submit_response() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + null_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { "Content-Length", "1024", NULL }; + int32_t stream_id = 2; + spdylay_data_provider data_prd; + my_user_data ud; + spdylay_outbound_item *item; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64*1024; + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + spdylay_session_open_stream(session, stream_id, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + CU_ASSERT(0 == spdylay_submit_response(session, stream_id, nv, &data_prd)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("content-length", item->frame->syn_reply.nv[0])); + CU_ASSERT(0 == spdylay_session_send(session)); + spdylay_session_del(session); +} + +void test_spdylay_submit_response_with_null_data_read_callback() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + spdylay_data_provider data_prd = {{-1}, NULL}; + spdylay_outbound_item *item; + my_user_data ud; + spdylay_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_FIN, 3, + SPDYLAY_STREAM_OPENING, NULL); + CU_ASSERT(0 == spdylay_submit_response(session, 1, nv, &data_prd)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", item->frame->syn_reply.nv[0])); + CU_ASSERT(item->frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN); + + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == spdylay_frame_unpack_syn_reply(&frame.syn_reply, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.syn_reply.nv[0])); + spdylay_frame_syn_reply_free(&frame.syn_reply); + + spdylay_session_del(session); +} + +void test_spdylay_submit_request_with_data() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + null_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { "Version", "HTTP/1.1", NULL }; + spdylay_data_provider data_prd; + my_user_data ud; + spdylay_outbound_item *item; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 64*1024; + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + CU_ASSERT(0 == spdylay_submit_request(session, 3, nv, &data_prd, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + + spdylay_session_del(session); +} + +void test_spdylay_submit_request_with_null_data_read_callback() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + spdylay_data_provider data_prd = {{-1}, NULL}; + spdylay_outbound_item *item; + my_user_data ud; + spdylay_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + CU_ASSERT(0 == spdylay_submit_request(session, 3, nv, &data_prd, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", item->frame->syn_stream.nv[0])); + CU_ASSERT(item->frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN); + + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == spdylay_frame_unpack_syn_stream(&frame.syn_stream, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.syn_stream.nv[0])); + spdylay_frame_syn_stream_free(&frame.syn_stream); + + spdylay_session_del(session); +} + +void test_spdylay_submit_syn_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { "version", "HTTP/1.1", NULL }; + spdylay_outbound_item *item; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, NULL)); + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 3, + nv, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == item->frame->syn_stream.hd.flags); + /* See assoc-stream-ID is ignored */ + CU_ASSERT(0 == item->frame->syn_stream.assoc_stream_id); + CU_ASSERT(3 == item->frame->syn_stream.pri); + + spdylay_session_del(session); + + CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, NULL)); + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 3, + nv, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == item->frame->syn_stream.hd.flags); + CU_ASSERT(1 == item->frame->syn_stream.assoc_stream_id); + CU_ASSERT(3 == item->frame->syn_stream.pri); + + /* Invalid assoc-stream-ID */ + CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT == + spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 2, 3, + nv, NULL)); + + spdylay_session_del(session); +} + +void test_spdylay_submit_syn_reply() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { "version", "HTTP/1.1", NULL }; + my_user_data ud; + spdylay_outbound_item *item; + spdylay_stream *stream; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + + CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + CU_ASSERT(0 == spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, + nv)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_reply.nv[0])); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == item->frame->syn_reply.hd.flags); + + ud.ctrl_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == ud.ctrl_send_cb_called); + + stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + + CU_ASSERT(0 == spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, + nv)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == ud.ctrl_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR); + + spdylay_session_del(session); +} + +void test_spdylay_submit_headers() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; + my_user_data ud; + spdylay_outbound_item *item; + spdylay_stream *stream; + accumulator acc; + spdylay_frame frame; + + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + CU_ASSERT(0 == spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp(":version", item->frame->headers.nv[0])); + CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == item->frame->headers.hd.flags); + + ud.ctrl_send_cb_called = 0; + ud.sent_frame_type = 0; + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == ud.ctrl_send_cb_called); + + stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + + CU_ASSERT(0 == spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == ud.ctrl_send_cb_called); + CU_ASSERT(SPDYLAY_HEADERS == ud.sent_frame_type); + CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR); + + CU_ASSERT(0 == spdylay_frame_unpack_headers(&frame.headers, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.headers.nv[0])); + spdylay_frame_headers_free(&frame.headers); + + spdylay_session_del(session); +} + +void test_spdylay_session_reply_fail() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + fail_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { NULL }; + int32_t stream_id = 2; + spdylay_data_provider data_prd; + my_user_data ud; + + data_prd.read_callback = fixed_length_data_source_read_callback; + ud.data_source_length = 4*1024; + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &ud)); + CU_ASSERT(0 == spdylay_submit_response(session, stream_id, nv, &data_prd)); + CU_ASSERT(0 == spdylay_session_send(session)); + spdylay_session_del(session); +} + +void test_spdylay_session_on_headers_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; + spdylay_frame frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_stream_shutdown(spdylay_session_get_stream(session, 1), + SPDYLAY_SHUT_WR); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 1, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + CU_ASSERT(SPDYLAY_STREAM_OPENED == + spdylay_session_get_stream(session, 1)->state); + + frame.headers.hd.flags |= SPDYLAY_CTRL_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(2 == user_data.ctrl_recv_cb_called); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + /* Check to see when SPDYLAY_STREAM_CLOSING, incoming HEADERS is + discarded. */ + spdylay_session_open_stream(session, 3, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_CLOSING, NULL); + frame.headers.stream_id = 3; + frame.headers.hd.flags = SPDYLAY_CTRL_FLAG_NONE; + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(2 == user_data.ctrl_recv_cb_called); + CU_ASSERT(1 == user_data.invalid_ctrl_recv_cb_called); + + /* Server initiated stream */ + spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + + frame.headers.hd.flags = SPDYLAY_CTRL_FLAG_FIN; + frame.headers.stream_id = 2; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(3 == user_data.ctrl_recv_cb_called); + CU_ASSERT(SPDYLAY_STREAM_OPENING == + spdylay_session_get_stream(session, 2)->state); + CU_ASSERT(spdylay_session_get_stream(session, 2)->shut_flags & + SPDYLAY_SHUT_RD); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_headers_free(&frame.headers); + + /* Upper cased name/value pairs */ + spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(upcase_nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_headers_free(&frame.headers); + + spdylay_session_del(session); +} + +void test_spdylay_session_on_window_update_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_frame frame; + spdylay_stream *stream; + spdylay_outbound_item *data_item; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, + &user_data); + stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3, + 1, 16*1024); + + CU_ASSERT(0 == spdylay_session_on_window_update_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + CU_ASSERT(64*1024+16*1024 == stream->window_size); + + data_item = malloc(sizeof(spdylay_outbound_item)); + memset(data_item, 0, sizeof(spdylay_outbound_item)); + spdylay_stream_defer_data(stream, data_item, SPDYLAY_DEFERRED_FLOW_CONTROL); + + CU_ASSERT(0 == spdylay_session_on_window_update_received(session, &frame)); + CU_ASSERT(2 == user_data.ctrl_recv_cb_called); + CU_ASSERT(64*1024+16*1024*2 == stream->window_size); + CU_ASSERT(NULL == stream->deferred_data); + + spdylay_frame_window_update_free(&frame.window_update); + spdylay_session_del(session); +} + +void test_spdylay_session_on_ping_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + NULL, + NULL, + on_ctrl_recv_callback, + on_invalid_ctrl_recv_callback + }; + my_user_data user_data; + spdylay_frame frame; + spdylay_outbound_item *top; + uint32_t unique_id; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + unique_id = 2; + spdylay_frame_ping_init(&frame.ping, SPDYLAY_PROTO_SPDY2, unique_id); + + CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + top = spdylay_session_get_ob_pq_top(session); + CU_ASSERT(SPDYLAY_PING == top->frame_type); + CU_ASSERT(unique_id == top->frame->ping.unique_id); + + session->last_ping_unique_id = 1; + frame.ping.unique_id = 1; + + CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame)); + CU_ASSERT(2 == user_data.ctrl_recv_cb_called); + + spdylay_frame_ping_free(&frame.ping); + spdylay_session_del(session); +} + +void test_spdylay_session_on_goaway_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + NULL, + NULL, + on_ctrl_recv_callback, + on_invalid_ctrl_recv_callback, + }; + my_user_data user_data; + spdylay_frame frame; + int32_t stream_id = 1000000007; + user_data.ctrl_recv_cb_called = 0; + user_data.invalid_ctrl_recv_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY2, stream_id, + SPDYLAY_GOAWAY_OK); + + CU_ASSERT(0 == spdylay_session_on_goaway_received(session, &frame)); + CU_ASSERT(1 == user_data.ctrl_recv_cb_called); + CU_ASSERT(session->goaway_flags == SPDYLAY_GOAWAY_RECV); + + spdylay_frame_goaway_free(&frame.goaway); + spdylay_session_del(session); +} + +void test_spdylay_session_on_data_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + my_user_data user_data; + spdylay_outbound_item *top; + int32_t stream_id = 2; + spdylay_stream *stream; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + stream = spdylay_session_open_stream(session, stream_id, + SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + CU_ASSERT(0 == spdylay_session_on_data_received(session, + SPDYLAY_DATA_FLAG_NONE, + 4096, stream_id)); + CU_ASSERT(0 == stream->shut_flags); + + CU_ASSERT(0 == spdylay_session_on_data_received(session, + SPDYLAY_DATA_FLAG_FIN, + 4096, stream_id)); + CU_ASSERT(SPDYLAY_SHUT_RD == stream->shut_flags); + + /* If SPDYLAY_STREAM_CLOSING state, DATA frame is discarded. */ + stream_id = 4; + + spdylay_session_open_stream(session, stream_id, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_CLOSING, NULL); + CU_ASSERT(0 == spdylay_session_on_data_received(session, + SPDYLAY_DATA_FLAG_NONE, + 4096, stream_id)); + CU_ASSERT(NULL == spdylay_session_get_ob_pq_top(session)); + + /* Check INVALID_STREAM case: DATA frame with stream ID which does + not exist. */ + stream_id = 6; + + CU_ASSERT(0 == spdylay_session_on_data_received(session, + SPDYLAY_DATA_FLAG_NONE, + 4096, stream_id)); + top = spdylay_session_get_ob_pq_top(session); + CU_ASSERT(SPDYLAY_RST_STREAM == top->frame_type); + CU_ASSERT(SPDYLAY_INVALID_STREAM == top->frame->rst_stream.status_code); + + spdylay_session_del(session); +} + +void test_spdylay_session_is_my_stream_id() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0)); + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 1)); + CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 2)); + + spdylay_session_del(session); + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0)); + CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 1)); + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 2)); + + spdylay_session_del(session); +} + +void test_spdylay_session_on_rst_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + spdylay_frame frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + /* server push */ + spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + spdylay_stream_add_pushed_stream(stream, 2); + spdylay_session_open_stream(session, 4, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + spdylay_stream_add_pushed_stream(stream, 4); + + spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY2, 1, + SPDYLAY_CANCEL); + + CU_ASSERT(0 == spdylay_session_on_rst_stream_received(session, &frame)); + + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 2)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 4)); + + spdylay_frame_rst_stream_free(&frame.rst_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_send_rst_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + spdylay_frame *frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + /* server push */ + spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + spdylay_stream_add_pushed_stream(stream, 2); + spdylay_session_open_stream(session, 4, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + spdylay_stream_add_pushed_stream(stream, 4); + + frame = malloc(sizeof(spdylay_frame)); + spdylay_frame_rst_stream_init(&frame->rst_stream, SPDYLAY_PROTO_SPDY2, 1, + SPDYLAY_CANCEL); + spdylay_session_add_frame(session, SPDYLAY_RST_STREAM, frame, NULL); + CU_ASSERT(0 == spdylay_session_send(session)); + + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 2)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 4)); + + spdylay_session_del(session); +} + +void test_spdylay_session_get_next_ob_item() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; + + CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session)); + spdylay_submit_ping(session); + CU_ASSERT(SPDYLAY_PING == + spdylay_session_get_next_ob_item(session)->frame_type); + + spdylay_submit_request(session, 0, nv, NULL, NULL); + CU_ASSERT(SPDYLAY_PING == + spdylay_session_get_next_ob_item(session)->frame_type); + + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session)); + + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + + spdylay_submit_request(session, 0, nv, NULL, NULL); + CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session)); + + spdylay_submit_response(session, 1, nv, NULL); + CU_ASSERT(SPDYLAY_SYN_REPLY == + spdylay_session_get_next_ob_item(session)->frame_type); + + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 3; + + CU_ASSERT(SPDYLAY_SYN_STREAM == + spdylay_session_get_next_ob_item(session)->frame_type); + + spdylay_session_del(session); +} + +void test_spdylay_session_pop_next_ob_item() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + spdylay_outbound_item *item; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; + + CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session)); + spdylay_submit_ping(session); + spdylay_submit_request(session, 0, nv, NULL, NULL); + + item = spdylay_session_pop_next_ob_item(session); + CU_ASSERT(SPDYLAY_PING == item->frame_type); + spdylay_outbound_item_free(item); + free(item); + + item = spdylay_session_pop_next_ob_item(session); + CU_ASSERT(SPDYLAY_SYN_STREAM == item->frame_type); + spdylay_outbound_item_free(item); + free(item); + + CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session)); + + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + + spdylay_submit_request(session, 0, nv, NULL, NULL); + spdylay_submit_response(session, 1, nv, NULL); + + item = spdylay_session_pop_next_ob_item(session); + CU_ASSERT(SPDYLAY_SYN_REPLY == item->frame_type); + spdylay_outbound_item_free(item); + free(item); + + CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session)); + + spdylay_submit_response(session, 1, nv, NULL); + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; + + item = spdylay_session_pop_next_ob_item(session); + CU_ASSERT(SPDYLAY_SYN_STREAM == item->frame_type); + spdylay_outbound_item_free(item); + free(item); + + spdylay_session_del(session); +} + +void test_spdylay_session_on_request_recv_callback() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + spdylay_frame frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_request_recv_callback = on_request_recv_callback; + user_data.stream_id = 0; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + frame.syn_stream.stream_id = 3; + frame.syn_stream.hd.flags |= SPDYLAY_CTRL_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.stream_id); + + user_data.stream_id = 0; + + frame.syn_stream.stream_id = 0; + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + + user_data.stream_id = 0; + + spdylay_session_open_stream(session, 5, SPDYLAY_CTRL_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 5, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + frame.headers.hd.flags |= SPDYLAY_CTRL_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(5 == user_data.stream_id); + + user_data.stream_id = 0; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + spdylay_frame_headers_free(&frame.headers); + spdylay_session_del(session); +} + +static void stream_close_callback(spdylay_session *session, int32_t stream_id, + spdylay_status_code status_code, + void *user_data) +{ + my_user_data* my_data = (my_user_data*)user_data; + void *stream_data = spdylay_session_get_stream_user_data(session, stream_id); + ++my_data->stream_close_cb_called; + CU_ASSERT(stream_data != NULL); +} + +void test_spdylay_session_on_stream_close() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + int32_t stream_id = 1; + uint8_t pri = 3; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_stream_close_callback = stream_close_callback; + user_data.stream_close_cb_called = 0; + + CU_ASSERT(spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &user_data) == 0); + stream = spdylay_session_open_stream(session, stream_id, + SPDYLAY_CTRL_FLAG_NONE, + pri, SPDYLAY_STREAM_OPENED, &user_data); + CU_ASSERT(stream != NULL); + CU_ASSERT(spdylay_session_close_stream(session, stream_id, SPDYLAY_OK) == 0); + CU_ASSERT(user_data.stream_close_cb_called == 1); + spdylay_session_del(session); +} + +void test_spdylay_session_max_concurrent_streams() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + spdylay_frame frame; + const char *nv[] = { NULL }; + spdylay_outbound_item *item; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 3, 0, 3, dup_nv(nv)); + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + + item = spdylay_session_get_ob_pq_top(session); + CU_ASSERT(SPDYLAY_RST_STREAM == item->frame_type); + CU_ASSERT(SPDYLAY_REFUSED_STREAM == item->frame->rst_stream.status_code) + + spdylay_frame_syn_stream_free(&frame.syn_stream); + + spdylay_session_del(session); +} + +static ssize_t block_count_send_callback(spdylay_session* session, + const uint8_t *data, size_t len, + int flags, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + int r; + if(ud->block_count == 0) { + r = SPDYLAY_ERR_WOULDBLOCK; + } else { + --ud->block_count; + r = len; + } + return r; +} + +void test_spdylay_session_data_backoff_by_high_pri_frame() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + spdylay_data_provider data_prd; + spdylay_stream *stream; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = block_count_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.ctrl_send_cb_called = 0; + ud.data_source_length = 16*1024; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud); + spdylay_submit_request(session, 3, nv, &data_prd, NULL); + + ud.block_count = 2; + /* Sends SYN_STREAM + DATA[0] */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(SPDYLAY_SYN_STREAM == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 8*1024); + + spdylay_submit_ping(session); + ud.block_count = 2; + /* Sends DATA[1] + PING, PING is interleaved in DATA sequence */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(SPDYLAY_PING == ud.sent_frame_type); + /* data for DATA[2] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 4*1024); + + ud.block_count = 2; + /* Sends DATA[2..3] */ + CU_ASSERT(0 == spdylay_session_send(session)); + + stream = spdylay_session_get_stream(session, 1); + CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR); + + spdylay_session_del(session); +} + +void test_spdylay_session_stop_data_with_rst_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + spdylay_data_provider data_prd; + spdylay_frame frame; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.ctrl_send_cb_called = 0; + ud.data_source_length = 16*1024; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_submit_response(session, 1, nv, &data_prd); + + ud.block_count = 2; + /* Sends SYN_REPLY + DATA[0] */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type); + /* data for DATA[1] is read from data_prd but it is not sent */ + CU_ASSERT(ud.data_source_length == 8*1024); + + spdylay_frame_rst_stream_init(&frame.rst_stream, SPDYLAY_PROTO_SPDY2, 1, + SPDYLAY_CANCEL); + CU_ASSERT(0 == spdylay_session_on_rst_stream_received(session, &frame)); + spdylay_frame_rst_stream_free(&frame.rst_stream); + + /* Big enough number to send all DATA frames potentially. */ + ud.block_count = 100; + /* Nothing will be sent in the following call. */ + CU_ASSERT(0 == spdylay_session_send(session)); + /* With RST_STREAM, stream is canceled and further DATA on that + stream are not sent. */ + CU_ASSERT(ud.data_source_length == 8*1024); + + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + + spdylay_session_del(session); +} + +/* + * Check that on_stream_close_callback is called when server pushed + * SYN_STREAM have SPDYLAY_CTRL_FLAG_FIN. + */ +void test_spdylay_session_stream_close_on_syn_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + spdylay_frame frame; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_stream_close_callback = + no_stream_user_data_stream_close_callback; + ud.stream_close_cb_called = 0; + + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_FIN | + SPDYLAY_CTRL_FLAG_UNIDIRECTIONAL, + 2, 1, 3, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_recv_invalid_frame() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + scripted_data_feed df; + my_user_data user_data; + const char *nv[] = { + "url", "/", NULL + }; + uint8_t *framedata = NULL, *nvbuf = NULL; + size_t framedatalen = 0, nvbuflen = 0; + ssize_t framelen; + spdylay_frame frame; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.recv_callback = scripted_recv_callback; + callbacks.send_callback = null_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + + user_data.df = &df; + user_data.ctrl_send_cb_called = 0; + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, + &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, 1, 0, 3, dup_nv(nv)); + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, + &session->hd_deflater); + scripted_data_feed_init(&df, framedata, framelen); + + CU_ASSERT(0 == spdylay_session_recv(session)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == user_data.ctrl_send_cb_called); + + /* Receive exactly same bytes of SYN_STREAM causes error */ + scripted_data_feed_init(&df, framedata, framelen); + + CU_ASSERT(0 == spdylay_session_recv(session)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_send_cb_called); + CU_ASSERT(SPDYLAY_GOAWAY == user_data.sent_frame_type); + + free(framedata); + free(nvbuf); + spdylay_frame_syn_stream_free(&frame.syn_stream); + + spdylay_session_del(session); +} + +static ssize_t defer_data_source_read_callback +(spdylay_session *session, int32_t stream_id, + uint8_t *buf, size_t len, int *eof, + spdylay_data_source *source, void *user_data) +{ + return SPDYLAY_ERR_DEFERRED; +} + +void test_spdylay_session_defer_data() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + spdylay_data_provider data_prd; + spdylay_outbound_item *item; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + callbacks.send_callback = block_count_send_callback; + data_prd.read_callback = defer_data_source_read_callback; + + ud.ctrl_send_cb_called = 0; + ud.data_source_length = 16*1024; + + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud); + spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_submit_response(session, 1, nv, &data_prd); + + ud.block_count = 1; + /* Sends SYN_REPLY */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(SPDYLAY_SYN_REPLY == ud.sent_frame_type); + /* No data is read */ + CU_ASSERT(ud.data_source_length == 16*1024); + + ud.block_count = 1; + spdylay_submit_ping(session); + /* Sends PING */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(SPDYLAY_PING == ud.sent_frame_type); + + /* Resume deferred DATA */ + CU_ASSERT(0 == spdylay_session_resume_data(session, 1)); + item = spdylay_session_get_ob_pq_top(session); + item->frame->data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 4KiB blocks */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(ud.data_source_length == 8*1024); + + /* Deferred again */ + item->frame->data.data_prd.read_callback = defer_data_source_read_callback; + /* This is needed since 4KiB block is already read and waiting to be + sent. No read_callback invocation. */ + ud.block_count = 1; + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(ud.data_source_length == 8*1024); + + /* Resume deferred DATA */ + + CU_ASSERT(0 == spdylay_session_resume_data(session, 1)); + item = spdylay_session_get_ob_pq_top(session); + item->frame->data.data_prd.read_callback = + fixed_length_data_source_read_callback; + ud.block_count = 1; + /* Reads 2 4KiB blocks */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(ud.data_source_length == 0); + + spdylay_session_del(session); +} + +void test_spdylay_session_flow_control() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { NULL }; + my_user_data ud; + spdylay_data_provider data_prd; + spdylay_frame frame; + spdylay_stream *stream; + int32_t new_initial_window_size; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + data_prd.read_callback = fixed_length_data_source_read_callback; + + ud.ctrl_send_cb_called = 0; + ud.data_source_length = 128*1024; + + /* Initial window size is 64KiB */ + spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud); + spdylay_submit_request(session, 3, nv, &data_prd, NULL); + + /* Sends 64KiB data */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(64*1024 == ud.data_source_length); + + /* Back 32KiB */ + spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3, + 1, 32*1024); + spdylay_session_on_window_update_received(session, &frame); + + /* Sends another 32KiB data */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(32*1024 == ud.data_source_length); + + stream = spdylay_session_get_stream(session, 1); + /* Change initial window size to 16KiB. The window_size becomes + negative. */ + new_initial_window_size = 16*1024; + stream->window_size = new_initial_window_size- + (session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] + -stream->window_size); + CU_ASSERT(-48*1024 == stream->window_size); + + /* Back 48KiB */ + frame.window_update.delta_window_size = 48*1024; + spdylay_session_on_window_update_received(session, &frame); + + /* Nothing is sent because window_size is less than 0 */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(32*1024 == ud.data_source_length); + + /* Back 16KiB */ + frame.window_update.delta_window_size = 16*1024; + spdylay_session_on_window_update_received(session, &frame); + + /* Sends another 16KiB data */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(16*1024 == ud.data_source_length); + + /* Back 16KiB */ + frame.window_update.delta_window_size = 16*1024; + spdylay_session_on_window_update_received(session, &frame); + + /* Sends another 16KiB data */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == ud.data_source_length); + CU_ASSERT(spdylay_session_get_stream(session, 1)->shut_flags & + SPDYLAY_SHUT_WR); + + spdylay_frame_window_update_free(&frame.window_update); + spdylay_session_del(session); +} + +void test_spdylay_session_on_ctrl_not_send() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + const char *nv[] = { NULL }; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback; + callbacks.send_callback = null_send_callback; + user_data.ctrl_not_send_cb_called = 0; + user_data.not_sent_frame_type = 0; + user_data.not_sent_error = 0; + + CU_ASSERT(spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &user_data) == 0); + stream = spdylay_session_open_stream(session, 1, + SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENED, &user_data); + /* Check SYN_STREAM */ + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 3, 3, + nv, NULL)); + + user_data.ctrl_not_send_cb_called = 0; + /* Associated stream closed */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED == user_data.not_sent_error); + + /* Check SYN_REPLY */ + CU_ASSERT(0 == + spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + user_data.ctrl_not_send_cb_called = 0; + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error); + + stream->state = SPDYLAY_STREAM_OPENING; + user_data.ctrl_not_send_cb_called = 0; + /* Send bogus stream ID */ + CU_ASSERT(0 == + spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED == user_data.not_sent_error); + + user_data.ctrl_not_send_cb_called = 0; + /* Shutdown transmission */ + stream->shut_flags |= SPDYLAY_SHUT_WR; + CU_ASSERT(0 == + spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_SHUT_WR == user_data.not_sent_error); + + stream->shut_flags = SPDYLAY_SHUT_NONE; + user_data.ctrl_not_send_cb_called = 0; + /* Queue RST_STREAM */ + CU_ASSERT(0 == + spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + CU_ASSERT(0 == spdylay_submit_rst_stream(session, 1, SPDYLAY_INTERNAL_ERROR)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error); + + stream = spdylay_session_open_stream(session, 3, + SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENED, &user_data); + + /* Check HEADERS */ + user_data.ctrl_not_send_cb_called = 0; + stream->state = SPDYLAY_STREAM_OPENING; + CU_ASSERT(0 == + spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error); + + stream->state = SPDYLAY_STREAM_OPENED; + user_data.ctrl_not_send_cb_called = 0; + /* Queue RST_STREAM */ + CU_ASSERT(0 == + spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv)); + CU_ASSERT(0 == spdylay_submit_rst_stream(session, 3, SPDYLAY_INTERNAL_ERROR)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error); + + spdylay_session_del(session); + + /* Check SYN_STREAM */ + user_data.ctrl_not_send_cb_called = 0; + CU_ASSERT(spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, + &callbacks, &user_data) == 0); + /* Maximum Stream ID is reached */ + session->next_stream_id = (1u << 31)+1; + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0, + 3, nv, NULL)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE == user_data.not_sent_error); + + session->next_stream_id = 1; + user_data.ctrl_not_send_cb_called = 0; + /* Send GOAWAY */ + CU_ASSERT(0 == spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK)); + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0, + 3, nv, NULL)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED == user_data.not_sent_error); + + spdylay_session_del(session); +} + +void test_spdylay_session_on_settings_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream1, *stream2; + spdylay_frame frame; + const size_t niv = 5; + spdylay_settings_entry iv[niv]; + + iv[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 1000000009; + iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].value = 50; + iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].value = 64*1024; + iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + iv[3].settings_id = SPDYLAY_SETTINGS_CURRENT_CWND; + iv[3].value = 512; + iv[3].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + iv[4].settings_id = 999; + iv[4].value = 0; + iv[4].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, + &user_data); + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024; + + stream1 = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + stream2 = spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING, NULL); + stream1->window_size = 16*1024; + stream2->window_size = -48*1024; + + spdylay_frame_settings_init(&frame.settings, SPDYLAY_PROTO_SPDY3, + SPDYLAY_FLAG_SETTINGS_NONE, dup_iv(iv, niv), niv); + + CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame)); + + CU_ASSERT(1000000009 == + session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(64*1024 == + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]); + CU_ASSERT(512 == + session->remote_settings[SPDYLAY_SETTINGS_CURRENT_CWND]); + CU_ASSERT(64*1024 == stream1->window_size); + CU_ASSERT(0 == stream2->window_size); + + frame.settings.iv[2].value = 16*1024; + + CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame)); + + CU_ASSERT(16*1024 == stream1->window_size); + CU_ASSERT(-48*1024 == stream2->window_size); + + spdylay_frame_settings_free(&frame.settings); + + spdylay_session_del(session); +} + +void test_spdylay_submit_settings() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data ud; + spdylay_outbound_item *item; + spdylay_frame *frame; + spdylay_settings_entry iv[3]; + + iv[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[0].value = 50; + iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + iv[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv[1].value = 16*1024; + iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + /* This is duplicate entry */ + iv[2].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[2].value = 150; + iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_ctrl_send_callback = on_ctrl_send_callback; + spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud); + + CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT == + spdylay_submit_settings(session, SPDYLAY_FLAG_SETTINGS_NONE, + iv, 3)); + + /* Make sure that local settings are not changed */ + CU_ASSERT(SPDYLAY_INITIAL_MAX_CONCURRENT_STREAMS == + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(SPDYLAY_INITIAL_WINDOW_SIZE == + session->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]); + + CU_ASSERT(0 == spdylay_submit_settings(session, + SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS, + iv, 2)); + + /* Make sure that local settings are changed */ + CU_ASSERT(50 == + session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]); + CU_ASSERT(16*1024 == + session->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]); + + item = spdylay_session_get_next_ob_item(session); + + CU_ASSERT(SPDYLAY_SETTINGS == item->frame_type); + + frame = item->frame; + CU_ASSERT(2 == frame->settings.niv); + CU_ASSERT(SPDYLAY_FLAG_SETTINGS_CLEAR_SETTINGS == frame->settings.hd.flags); + + CU_ASSERT(50 == frame->settings.iv[0].value); + CU_ASSERT(SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS == + frame->settings.iv[0].settings_id); + CU_ASSERT(SPDYLAY_FLAG_SETTINGS_NONE == + frame->settings.iv[0].flags); + + CU_ASSERT(16*1024 == frame->settings.iv[1].value); + CU_ASSERT(SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE == + frame->settings.iv[1].settings_id); + CU_ASSERT(SPDYLAY_FLAG_SETTINGS_NONE == + frame->settings.iv[1].flags); + + ud.ctrl_send_cb_called = 0; + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == ud.ctrl_send_cb_called); + + spdylay_session_del(session); +} + +void test_spdylay_session_get_outbound_queue_size() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { "version", "HTTP/1.1", NULL }; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, + &callbacks, NULL)); + CU_ASSERT(0 == spdylay_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 1, 7, + nv, NULL)); + CU_ASSERT(1 == spdylay_session_get_outbound_queue_size(session)); + + CU_ASSERT(0 == spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK)); + CU_ASSERT(2 == spdylay_session_get_outbound_queue_size(session)); + + spdylay_session_del(session); +} diff --git a/deps/spdylay/tests/spdylay_session_test.h b/deps/spdylay/tests/spdylay_session_test.h new file mode 100644 index 0000000..d4636a2 --- /dev/null +++ b/deps/spdylay/tests/spdylay_session_test.h @@ -0,0 +1,68 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_SESSION_TEST_H +#define SPDYLAY_SESSION_TEST_H + +void test_spdylay_session_recv(); +void test_spdylay_session_recv_invalid_stream_id(); +void test_spdylay_session_add_frame(); +void test_spdylay_session_on_syn_stream_received(); +void test_spdylay_session_on_syn_stream_received_with_push(); +void test_spdylay_session_on_syn_reply_received(); +void test_spdylay_session_on_window_update_received(); +void test_spdylay_session_send_syn_stream(); +void test_spdylay_session_send_syn_reply(); +void test_spdylay_submit_response(); +void test_spdylay_submit_response_with_null_data_read_callback(); +void test_spdylay_submit_request_with_data(); +void test_spdylay_submit_request_with_null_data_read_callback(); +void test_spdylay_submit_syn_stream(); +void test_spdylay_submit_syn_reply(); +void test_spdylay_submit_headers(); +void test_spdylay_session_reply_fail(); +void test_spdylay_session_on_headers_received(); +void test_spdylay_session_on_ping_received(); +void test_spdylay_session_on_goaway_received(); +void test_spdylay_session_on_data_received(); +void test_spdylay_session_on_rst_received(); +void test_spdylay_session_is_my_stream_id(); +void test_spdylay_session_send_rst_stream(); +void test_spdylay_session_get_next_ob_item(); +void test_spdylay_session_pop_next_ob_item(); +void test_spdylay_session_on_request_recv_callback(); +void test_spdylay_session_on_stream_close(); +void test_spdylay_session_max_concurrent_streams(); +void test_spdylay_session_data_backoff_by_high_pri_frame(); +void test_spdylay_session_stop_data_with_rst_stream(); +void test_spdylay_session_stream_close_on_syn_stream(); +void test_spdylay_session_recv_invalid_frame(); +void test_spdylay_session_defer_data(); +void test_spdylay_session_flow_control(); +void test_spdylay_session_on_ctrl_not_send(); +void test_spdylay_session_on_settings_received(); +void test_spdylay_submit_settings(); +void test_spdylay_session_get_outbound_queue_size(); + +#endif // SPDYLAY_SESSION_TEST_H diff --git a/deps/spdylay/tests/spdylay_stream_test.c b/deps/spdylay/tests/spdylay_stream_test.c new file mode 100644 index 0000000..76b9a26 --- /dev/null +++ b/deps/spdylay/tests/spdylay_stream_test.c @@ -0,0 +1,46 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_stream_test.h" + +#include + +#include "spdylay_stream.h" + +void test_spdylay_stream_add_pushed_stream() +{ + spdylay_stream stream; + int i, n; + spdylay_stream_init(&stream, 1, SPDYLAY_CTRL_FLAG_NONE, 3, 65536, + SPDYLAY_STREAM_OPENING, NULL); + n = 26; + for(i = 2; i < n; i += 2) { + CU_ASSERT(0 == spdylay_stream_add_pushed_stream(&stream, i)); + CU_ASSERT(i/2 == stream.pushed_streams_length); + } + for(i = 2; i < n; i += 2) { + CU_ASSERT(i == stream.pushed_streams[i/2-1]); + } + spdylay_stream_free(&stream); +} diff --git a/deps/spdylay/tests/spdylay_stream_test.h b/deps/spdylay/tests/spdylay_stream_test.h new file mode 100644 index 0000000..1326063 --- /dev/null +++ b/deps/spdylay/tests/spdylay_stream_test.h @@ -0,0 +1,30 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_STREAM_TEST_H +#define SPDYLAY_STREAM_TEST_H + +void test_spdylay_stream_add_pushed_stream(); + +#endif /* SPDYLAY_STREAM_TEST_H */ diff --git a/deps/spdylay/tests/spdylay_zlib_test.c b/deps/spdylay/tests/spdylay_zlib_test.c new file mode 100644 index 0000000..5ed0231 --- /dev/null +++ b/deps/spdylay/tests/spdylay_zlib_test.c @@ -0,0 +1,81 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_zlib_test.h" + +#include + +#include + +#include "spdylay_zlib.h" + +void test_spdylay_zlib_with(uint16_t version) +{ + spdylay_zlib deflater, inflater; + const char msg[] = + "GET /chat HTTP/1.1\r\n" + "Host: server.example.com\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Origin: http://example.com\r\n" + "Sec-WebSocket-Protocol: chat, superchat\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + uint8_t inflatebuf[sizeof(msg)]; + spdylay_buffer buf; + uint8_t *deflatebuf; + size_t deflatebuf_max; + ssize_t deflatebuf_len; + spdylay_buffer_init(&buf, 4096); + + CU_ASSERT(0 == spdylay_zlib_deflate_hd_init(&deflater, version)); + CU_ASSERT(0 == spdylay_zlib_inflate_hd_init(&inflater, version)); + + deflatebuf_max = spdylay_zlib_deflate_hd_bound(&deflater, sizeof(msg)); + deflatebuf = malloc(deflatebuf_max); + + CU_ASSERT(0 < (deflatebuf_len = spdylay_zlib_deflate_hd + (&deflater, deflatebuf, deflatebuf_max, + (const uint8_t*)msg, sizeof(msg)))); + CU_ASSERT(sizeof(msg) == spdylay_zlib_inflate_hd + (&inflater, &buf, deflatebuf, deflatebuf_len)); + free(deflatebuf); + spdylay_buffer_serialize(&buf, inflatebuf); + + spdylay_zlib_deflate_free(&deflater); + spdylay_zlib_inflate_free(&inflater); + + spdylay_buffer_free(&buf); +} + +void test_spdylay_zlib_spdy2() +{ + test_spdylay_zlib_with(SPDYLAY_PROTO_SPDY2); +} + +void test_spdylay_zlib_spdy3() +{ + test_spdylay_zlib_with(SPDYLAY_PROTO_SPDY3); +} diff --git a/deps/spdylay/tests/spdylay_zlib_test.h b/deps/spdylay/tests/spdylay_zlib_test.h new file mode 100644 index 0000000..b610663 --- /dev/null +++ b/deps/spdylay/tests/spdylay_zlib_test.h @@ -0,0 +1,31 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_ZLIB_TEST_H +#define SPDYLAY_ZLIB_TEST_H + +void test_spdylay_zlib_spdy2(); +void test_spdylay_zlib_spdy3(); + +#endif // SPDYLAY_ZLIB_TEST_H diff --git a/deps/spdylay/tests/testdata/cacert.pem b/deps/spdylay/tests/testdata/cacert.pem new file mode 100644 index 0000000..e462790 --- /dev/null +++ b/deps/spdylay/tests/testdata/cacert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICKTCCAdOgAwIBAgIJAIsolheWrwMZMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJU3Bk +eSBUZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDnNwZHlA +bG9jYWxob3N0MB4XDTEyMDMwMTE5MTI0NVoXDTIzMDUxOTE5MTI0NVowcDELMAkG +A1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAlT +cGR5IFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJARYOc3Bk +eUBsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAw/2MgzAdlJDm29qH +ZlAibgs9mH+8keOtsRrb4B1PiCcZoHvN9eCVZ4WnzT+0zhHF+nO3YfwVFVC3w7TF +7fLB3QIDAQABo1AwTjAdBgNVHQ4EFgQUVP2Jw9RX6BB76aV5x2qk5qsrAIQwHwYD +VR0jBBgwFoAUVP2Jw9RX6BB76aV5x2qk5qsrAIQwDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQUFAANBAKd9M5FzQLEZW1KPe9/XNZlgxZ2g3EC5Krxo5I4Ul3MnIYS9 +u4K8t/iprhgOzjFH6+8LVk9v0Za+gU+K43CpUo4= +-----END CERTIFICATE----- diff --git a/deps/spdylay/tests/testdata/index.html b/deps/spdylay/tests/testdata/index.html new file mode 100644 index 0000000..cdd75fc --- /dev/null +++ b/deps/spdylay/tests/testdata/index.html @@ -0,0 +1 @@ +small diff --git a/deps/spdylay/tests/testdata/privkey.pem b/deps/spdylay/tests/testdata/privkey.pem new file mode 100644 index 0000000..0ab825b --- /dev/null +++ b/deps/spdylay/tests/testdata/privkey.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAMP9jIMwHZSQ5tvah2ZQIm4LPZh/vJHjrbEa2+AdT4gnGaB7zfXg +lWeFp80/tM4Rxfpzt2H8FRVQt8O0xe3ywd0CAwEAAQJBAIQ8PGP/QNYOdlT8OsLj +aneJCgQsm1Rro7ONBbFO1WxslvA6+uJsx4Rs8zLiS8cyqmJ/lmGa7zhwYSOvFQPa +XgECIQDgIcgM/2C67peTm1diKKIoGVVKFCfdRi+Dje6mTl2TQQIhAN/bcFWbG73j +cUVlIsr9Wk1dJzjPPWKeyirF1qd/WbOdAiEApTsCOeLCssxV3jF02B5QfPNAFx6I +zO2C9Z7awque/IECIGCHW3VOoTPMs7dc2Rf3D810cclJdArmtf6juOAZRjDxAiBS +AC+H685IBJ99N5nCbF9NWYIVSkuiKVQ8POYVZX+0Jg== +-----END RSA PRIVATE KEY-----