pax_global_header00006660000000000000000000000064136733310070014515gustar00rootroot0000000000000052 comment=c9a84aa6df8512584c77c8cd15be9536b89c35aa wslay-release-1.1.1/000077500000000000000000000000001367333100700142725ustar00rootroot00000000000000wslay-release-1.1.1/.gitignore000066400000000000000000000007701367333100700162660ustar00rootroot00000000000000*~ *.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/wslay/wslayver.h lib/libwslay.pc ltmain.sh stamp-h1 .deps/ INSTALL .DS_STORE tests/main tests/main.log tests/main.trs tests/test-suite.log /compile /test-driver /build.* doc/man doc/sphinx/_build /CMakeFiles/ /cmake_install.cmake /lib/cmake_install.cmake /lib/libwslay.a /wslay-config.cmake /wslay.cmake wslay-release-1.1.1/AUTHORS000066400000000000000000000001021367333100700153330ustar00rootroot00000000000000Tatsuhiro Tsujikawa wslay-release-1.1.1/CMakeLists.txt000066400000000000000000000021631367333100700170340ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) # This CMakeLists.txt file intended for: # - development of wslay library # - building wslay for install purposes # - building wslay with ExternalProject_Add() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) if(NOT DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type") endif() project(wslay) option(WSLAY_CONFIGURE_INSTALL "Generate install target" ON) option(WSLAY_STATIC "Build static version of the library" ON) option(WSLAY_SHARED "Build shared version of the library" OFF) option(WSLAY_EXAMPLES "Build examples" OFF) option(WSLAY_TESTS "Build tests" OFF) add_subdirectory(lib) if(WSLAY_EXAMPLES) add_subdirectory(examples) endif() if(WSLAY_TESTS) enable_testing() add_subdirectory(tests) endif() if (WSLAY_CONFIGURE_INSTALL) include(GNUInstallDirs) set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/wslay) install(EXPORT wslay DESTINATION ${INSTALL_CMAKE_DIR}) configure_file(wslay-config.cmake.in wslay-config.cmake @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/wslay-config.cmake DESTINATION ${INSTALL_CMAKE_DIR}) endif() wslay-release-1.1.1/COPYING000066400000000000000000000021041367333100700153220ustar00rootroot00000000000000The MIT License Copyright (c) 2011, 2012, 2015 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. wslay-release-1.1.1/ChangeLog000066400000000000000000000000001367333100700160320ustar00rootroot00000000000000wslay-release-1.1.1/Makefile.am000066400000000000000000000022521367333100700163270ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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 tests examples doc ACLOCAL_AMFLAGS = -I m4 wslay-release-1.1.1/NEWS000066400000000000000000000033661367333100700150010ustar00rootroot00000000000000wslay 1.1.1 =========== Release Note ------------ This release fixes the bug that eof is not evaluated after the invocation of read_callback. Changes ------- * Check for eof when read_callback returns 0 (GH-47) Patch from Robert Bragg wslay 1.1.0 =========== Release Note ------------ This release adds CMake build support and the ability to set and verify reserved bits. Build issue with nettle >= 3.4 was fixed. Changes ------- * Fix compilation of examples Since 3.4 nettle defines base64_encode_raw like this: void base64_encode_raw(char *dst, size_t length, const uint8_t *src); So examples have to be adjusted. More read at https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.4_release_20171119/NEWS#L49-53 Patch from Sergey Avseyev * check for 0 length before memcpy: .../wslay/lib/wslay_event.c:304:7: runtime error: null pointer passed as argument 2, which is declared to never be null Patch from Anders Bakken * Skip UTF-8 validation for PMCE fragments If the message was marked with rsv1 on the initial frame then we should skip utf-8 validation on subsequent continuation frames as well. Added test for this case. Found by autobahn wstest tool. Patch from Isaac Boukris * Allow RSV1 bit in event-based API for PMCE - RFC 7692 Add a new function wslay_event_set_allowed_rsv_bits which only accpet RSV1 for now (or 0 to disable). Skip UTF-8 validation on frames with RSV1 set as it is too early for that. Add extended versions of wslay_event_queue_msg functions which also take the reserved bits to set for this message. Patch from Isaac Boukris * fixed missing malloc guard Patch from Jakub Piskorz * Fix argc check. Patch from Anders Bakken * CMake support Patch from wonder-mice wslay-release-1.1.1/README000066400000000000000000000000171367333100700151500ustar00rootroot00000000000000See README.rst wslay-release-1.1.1/README.rst000066400000000000000000000032371367333100700157660ustar00rootroot00000000000000Wslay - The WebSocket library ============================= Project Web: https://tatsuhiro-t.github.io/wslay/ Wslay is a WebSocket library written in C. It implements the protocol version 13 described in `RFC 6455 `_. This library offers 2 levels of API: event-based API and frame-based low-level API. For event-based API, it is suitable for non-blocking reactor pattern style. You can set callbacks in various events. For frame-based API, you can send WebSocket frame directly. Wslay only supports data transfer part of WebSocket protocol and does not perform opening handshake in HTTP. Wslay supports: * Text/Binary messages. * Automatic ping reply. * Callback interface. * External event loop. Wslay does not perform any I/O operations for its own. Instead, it offers callbacks for them. This makes Wslay independent on any I/O frameworks, SSL, sockets, etc. This makes Wslay portable across various platforms and the application authors can choose freely I/O frameworks. See Autobahn test reports: `server `_ and `client `_. Requirements ------------ `Sphinx `_ is used to generate man pages. To build and run the unit test programs, the following packages are needed: * cunit >= 2.1 To build and run the example programs, the following packages are needed: * nettle >= 2.4 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 wslay-release-1.1.1/cmake/000077500000000000000000000000001367333100700153525ustar00rootroot00000000000000wslay-release-1.1.1/cmake/FindCUnit.cmake000066400000000000000000000012001367333100700201700ustar00rootroot00000000000000# CUNIT_INCLUDE_DIRS - where to find , etc. # CUNIT_LIBRARIES - List of libraries when using libcunit. # CUNIT_FOUND - True if libcunit found. if(CUNIT_INCLUDE_DIRS) # Already in cache, be silent set(CUNIT_FIND_QUIETLY YES) endif() find_path(CUNIT_INCLUDE_DIRS CUnit/CUnit.h) find_library(CUNIT_LIBRARY NAMES cunit libcunit) # handle the QUIETLY and REQUIRED arguments and set CUNIT_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CUNIT DEFAULT_MSG CUNIT_LIBRARY CUNIT_INCLUDE_DIRS) if(CUNIT_FOUND) set(CUNIT_LIBRARIES ${CUNIT_LIBRARY}) endif() wslay-release-1.1.1/cmake/FindNettle.cmake000066400000000000000000000012601367333100700204070ustar00rootroot00000000000000# NETTLE_INCLUDE_DIRS - where to find , etc. # NETTLE_LIBRARIES - List of libraries when using libnettle. # NETTLE_FOUND - True if libnettle found. if(NETTLE_INCLUDE_DIRS) # Already in cache, be silent set(NETTLE_FIND_QUIETLY YES) endif() find_path(NETTLE_INCLUDE_DIRS nettle/md5.h nettle/ripemd160.h nettle/sha.h) find_library(NETTLE_LIBRARY NAMES nettle libnettle) # handle the QUIETLY and REQUIRED arguments and set NETTLE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NETTLE DEFAULT_MSG NETTLE_LIBRARY NETTLE_INCLUDE_DIRS) if(NETTLE_FOUND) set(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) endif() wslay-release-1.1.1/configure.ac000066400000000000000000000072131367333100700165630ustar00rootroot00000000000000dnl Wslay - The WebSocket Library dnl Copyright (c) 2011, 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) AC_INIT([wslay], [1.1.1], [t-tujikawa@users.sourceforge.net]) LT_PREREQ([2.2.6]) LT_INIT() AC_CONFIG_AUX_DIR([.]) dnl See versioning rule: dnl http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html AC_SUBST(LT_CURRENT, 1) AC_SUBST(LT_REVISION, 1) AC_SUBST(LT_AGE, 1) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE() AC_CONFIG_HEADERS([config.h]) dnl Checks for programs AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET PKG_PROG_PKG_CONFIG([0.20]) AC_PATH_PROG([SPHINX_BUILD], [sphinx-build]) AC_SUBST([SPHINX_BUILD]) AM_CONDITIONAL([HAVE_SPHINX_BUILD], [ test "x$SPHINX_BUILD" != "x" ]) # Checks for libraries. # Nettle used in examples PKG_CHECK_MODULES([NETTLE], [nettle >= 2.4], [have_nettle=yes], [have_nettle=no]) AC_CHECK_LIB([cunit], [CU_initialize_registry], [have_cunit=yes], [have_cunit=no]) AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) case "$target" in *mingw*) # Required for ntoh*/hton* functions. LIBS="-lws2_32 $LIBS" ;; esac # Checks for header files. AC_CHECK_HEADERS([ \ arpa/inet.h \ netinet/in.h \ stddef.h \ stdint.h \ stdlib.h \ string.h \ unistd.h \ ]) # Need winsock2.h for ntoh*/hton* functions. case "$target" in *mingw*) AC_CHECK_HEADERS([winsock2.h]) ;; esac # 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_CHECK_FUNCS([ \ memmove \ memset \ ntohl \ ntohs \ htons ]) build_examples=no if test "x${have_nettle}" = "xyes"; then case $host_os in linux*) # the examples uses epoll build_examples=yes ;; esac fi AM_CONDITIONAL([ENABLE_EXAMPLES], [ test "x${build_examples}" = "xyes" ]) AC_CONFIG_FILES([ Makefile lib/Makefile lib/libwslay.pc lib/includes/Makefile lib/includes/wslay/wslayver.h tests/Makefile examples/Makefile doc/Makefile doc/sphinx/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} Nettle: ${have_nettle} Build examples: ${build_examples} ]) wslay-release-1.1.1/doc/000077500000000000000000000000001367333100700150375ustar00rootroot00000000000000wslay-release-1.1.1/doc/Makefile.am000066400000000000000000000051321367333100700170740ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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. SPHINX_DIR = $(srcdir)/sphinx MANPAGES_WITH_SRC = \ man/wslay_event_context_server_init.3 \ man/wslay_event_config_set_no_buffering.3 \ man/wslay_event_config_set_max_recv_msg_length.3 \ man/wslay_event_config_set_callbacks.3 \ man/wslay_event_queue_close.3 \ man/wslay_event_queue_fragmented_msg.3 \ man/wslay_event_queue_msg.3 \ man/wslay_event_recv.3 \ man/wslay_event_send.3 \ man/wslay_event_set_error.3 \ man/wslay_event_want_read.3 \ man/wslay_event_want_write.3 \ man/wslay_event_get_read_enabled.3 \ man/wslay_event_get_write_enabled.3 \ man/wslay_event_shutdown_read.3 \ man/wslay_event_shutdown_write.3 \ man/wslay_event_get_close_received.3 \ man/wslay_event_get_close_sent.3 \ man/wslay_event_get_status_code_received.3 \ man/wslay_event_get_status_code_sent.3 \ man/wslay_event_get_queued_msg_count.3 \ man/wslay_event_get_queued_msg_length.3 SPHINX_MANPAGES_SRC = $(MANPAGES_WITH_SRC:%.3=$(SPHINX_DIR)/%.rst) SPHINX_FILES = $(SPHINX_DIR)/Makefile \ $(SPHINX_DIR)/api_reference.rst \ $(SPHINX_DIR)/conf.py \ $(SPHINX_DIR)/index.rst \ $(SPHINX_DIR)/tutorial.rst \ $(SPHINX_MANPAGES_SRC) MANPAGES_DST = $(MANPAGES_SRC:${SPHINX_DIR}/%.rst=%.3) MANPAGES = $(MANPAGES_WITH_SRC) \ man/wslay_event_context_client_init.3 \ man/wslay_event_context_free.3 man_MANS = $(MANPAGES) EXTRA_DIST = $(MANPAGES) $(SPHINX_FILES) if HAVE_SPHINX_BUILD $(MANPAGES_WITH_SRC): $(SPHINX_MANPAGES_SRC) echo $(MANPAGES_DST) @SPHINX_BUILD@ -b man $(SPHINX_DIR) man endif # HAVE_SPHINX_BUILD wslay-release-1.1.1/doc/sphinx/000077500000000000000000000000001367333100700163505ustar00rootroot00000000000000wslay-release-1.1.1/doc/sphinx/Makefile000066400000000000000000000107551367333100700200200ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # 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)" clean: -rm -rf $(BUILDDIR)/* html: $(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/wslay.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/wslay.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/wslay" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/wslay" @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." wslay-release-1.1.1/doc/sphinx/_static/000077500000000000000000000000001367333100700177765ustar00rootroot00000000000000wslay-release-1.1.1/doc/sphinx/_static/default2.css000066400000000000000000000002461367333100700222200ustar00rootroot00000000000000@import url(//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic); pre, tt { font-family: monospace, sans-serif; } tt { font-size: 100%; } wslay-release-1.1.1/doc/sphinx/api_reference.rst000066400000000000000000000016061367333100700216740ustar00rootroot00000000000000API Reference ============= Contents: .. toctree:: :maxdepth: 1 man/wslay_event_context_server_init man/wslay_event_config_set_no_buffering man/wslay_event_config_set_max_recv_msg_length man/wslay_event_config_set_callbacks man/wslay_event_config_set_allowed_rsv_bits man/wslay_event_queue_close man/wslay_event_queue_fragmented_msg man/wslay_event_queue_msg man/wslay_event_recv man/wslay_event_send man/wslay_event_set_error man/wslay_event_want_read man/wslay_event_want_write man/wslay_event_get_read_enabled man/wslay_event_get_write_enabled man/wslay_event_shutdown_read man/wslay_event_shutdown_write man/wslay_event_get_close_received man/wslay_event_get_close_sent man/wslay_event_get_status_code_received man/wslay_event_get_status_code_sent man/wslay_event_get_queued_msg_count man/wslay_event_get_queued_msg_length wslay-release-1.1.1/doc/sphinx/conf.py.in000066400000000000000000000261601367333100700202610ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # wslay documentation build configuration file, created by # sphinx-quickstart on Sun Jan 8 22:37:36 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'wslay' copyright = u'2012, 2015, 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 = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # 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 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 = {'bodyfont':'roboto, sans-serif', 'headfont':'roboto, "Trebuchet MS", sans-serif'} # 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/default2.css'] # 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 = 'wslaydoc' # -- 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', 'wslay.tex', u'wslay 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 = [ ('man/wslay_event_context_server_init', 'wslay_event_context_server_init', u'Initialize an event-based API context', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_context_server_init', 'wslay_event_context_client_init', u'Initialize an event-based API context', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_context_server_init', 'wslay_event_context_free', u'Initialize an event-based API context', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_config_set_no_buffering', 'wslay_event_config_set_no_buffering', u'Enable or disable buffering of an entire message for non-control frames', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_config_set_max_recv_msg_length', 'wslay_event_config_set_max_recv_msg_length', u'Set maximum length of a message that can be received', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_config_set_callbacks', 'wslay_event_config_set_callbacks', u'Set callback functions', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_config_set_allowed_rsv_bits', 'wslay_event_config_set_allowed_rsv_bits', u'Set allowed reserved bits', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_queue_close', 'wslay_event_queue_close', u'Queue a close control frame', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_queue_fragmented_msg', 'wslay_event_queue_fragmented_msg', u'Queue a fragmented message for future transmission', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_queue_fragmented_msg', 'wslay_event_queue_fragmented_msg_ex', u'Queue a fragmented message for future transmission', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_queue_msg', 'wslay_event_queue_msg', u'Queue a message for future transmission', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_queue_msg', 'wslay_event_queue_msg_ex', u'Queue a message for future transmission', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_recv', 'wslay_event_recv', u'Receive messages', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_send', 'wslay_event_send', u'Send any pending messages', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_set_error', 'wslay_event_set_error', u'Set error code', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_want_read', 'wslay_event_want_read', u'Tell whether the library wants to read more data', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_want_write', 'wslay_event_want_write', u'Tell whether the library wants to send more data', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_read_enabled', 'wslay_event_get_read_enabled', u'Query whether the event-based API context allows read operation', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_write_enabled', 'wslay_event_get_write_enabled', u'Query whether the event-based API context allows write operation', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_shutdown_read', 'wslay_event_shutdown_read', u'Disable read operation', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_shutdown_write', 'wslay_event_shutdown_write', u'Disable write operation', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_close_received', 'wslay_event_get_close_received', u'Query whether a close control frame has been received', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_close_sent', 'wslay_event_get_close_sent', u'Query whether a close control frame has been sent', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_status_code_received', 'wslay_event_get_status_code_received', u'Return status code received in close control frame', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_status_code_sent', 'wslay_event_get_status_code_sent', u'Return status code sent in close control frame', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_queued_msg_count', 'wslay_event_get_queued_msg_count', u'Get the number of queued messages', [u'Tatsuhiro Tsujikawa'], 3), ('man/wslay_event_get_queued_msg_length', 'wslay_event_get_queued_msg_length', u'Get the sum of queued message length', [u'Tatsuhiro Tsujikawa'], 3), ] def setup(app): app.add_stylesheet('default2.css') wslay-release-1.1.1/doc/sphinx/index.rst000066400000000000000000000024101367333100700202060ustar00rootroot00000000000000Wslay - The WebSocket library ============================= Wslay is a WebSocket library written in C. It implements the protocol version 13 described in `RFC 6455 `_. This library offers 2 levels of API: event-based API and frame-based low-level API. For event-based API, it is suitable for non-blocking reactor pattern style. You can set callbacks in various events. For frame-based API, you can send WebSocket frame directly. Wslay only supports data transfer part of WebSocket protocol and does not perform opening handshake in HTTP. Wslay supports: * Text/Binary messages. * Automatic ping reply. * Callback interface. * External event loop. Wslay does not perform any I/O operations for its own. Instead, it offers callbacks for them. This makes Wslay independent on any I/O frameworks, SSL, sockets, etc. This makes Wslay portable across various platforms and the application authors can choose freely I/O frameworks. See Autobahn test reports: `server `_ and `client `_. This project is hosted at https://github.com/tatsuhiro-t/wslay Contents: .. toctree:: :maxdepth: 2 tutorial api_reference wslay-release-1.1.1/doc/sphinx/make.bat000066400000000000000000000106351367333100700177620ustar00rootroot00000000000000@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\wslay.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\wslay.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 wslay-release-1.1.1/doc/sphinx/man/000077500000000000000000000000001367333100700171235ustar00rootroot00000000000000wslay-release-1.1.1/doc/sphinx/man/wslay_event_config_set_allowed_rsv_bits.rst000066400000000000000000000016171367333100700300440ustar00rootroot00000000000000.. highlight:: c wslay_event_config_set_allowed_rsv_bits ======================================= SYNOPSIS -------- #include .. c:function:: void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv) DESCRIPTION ----------- :c:func:`wslay_event_config_set_allowed_rsv_bits` sets a bit mask of allowed reserved bits (RSVs). Currently only permitted values are ``WSLAY_RSV1_BIT`` to allow PMCE extension (see :rfc:`7692`) or ``WSLAY_RSV_NONE`` to disable. When bits other than ``WSLAY_RSV1_BIT`` is set in *rsv*, those bits are simply ignored. The set value is used to check the validity of reserved bits for both transmission and reception of WebSocket frames. RETURN VALUE ------------ This function always succeeds, and there is no return value. SEE ALSO -------- :c:func:`wslay_event_queue_fragmented_msg`, :c:func:`wslay_event_queue_fragmented_msg_ex` wslay-release-1.1.1/doc/sphinx/man/wslay_event_config_set_callbacks.rst000066400000000000000000000011401367333100700264100ustar00rootroot00000000000000wslay_event_config_set_callbacks ================================ SYNOPSIS -------- #include .. c:function:: void wslay_event_config_set_callbacks(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks) DESCRIPTION ----------- :c:func:`wslay_event_config_set_callbacks` sets callbacks to *ctx*. The callbacks previously set by this function or :c:func:`wslay_event_context_server_init` or :c:func:`wslay_event_context_client_init` are replaced with *callbacks*. SEE ALSO -------- :c:func:`wslay_event_context_server_init`, :c:func:`wslay_event_context_client_init` wslay-release-1.1.1/doc/sphinx/man/wslay_event_config_set_max_recv_msg_length.rst000066400000000000000000000016341367333100700305140ustar00rootroot00000000000000wslay_event_config_set_max_recv_msg_length ========================================== SYNOPSIS -------- #include .. c:function:: void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx, uint64_t val) DESCRIPTION ----------- :c:func:`wslay_event_config_set_max_recv_msg_length` sets maximum length of a message that can be received. The length of message is checked by :c:func:`wslay_event_recv` function. If the length of a message is larger than this value, reading operation is disabled (same effect with :c:func:`wslay_event_shutdown_read` call) and close control frame with ``WSLAY_CODE_MESSAGE_TOO_BIG`` is queued. If buffering for non-control frames is disabled, the library checks each frame payload length and does not check length of entire message. The default value is ``(1u << 31)-1``. SEE ALSO -------- :c:func:`wslay_event_recv`, :c:func:`wslay_event_shutdown_read` wslay-release-1.1.1/doc/sphinx/man/wslay_event_config_set_no_buffering.rst000066400000000000000000000014551367333100700271450ustar00rootroot00000000000000wslay_event_config_set_no_buffering =================================== SYNOPSIS -------- #include .. c:function:: void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val) DESCRIPTION ----------- :c:func:`wslay_event_config_set_no_buffering` enables or disables buffering of an entire message for non-control frames. If *val* is 0, buffering is enabled. Otherwise, buffering is disabled. If :c:type:`wslay_event_on_msg_recv_callback` is invoked when buffering is disabled, the *msg_length* member of *struct wslay_event_on_msg_recv_arg* is set to 0. The control frames are always buffered regardless of this function call. This function must not be used after the first invocation of :c:func:`wslay_event_recv` function. SEE ALSO -------- :c:func:`wslay_event_recv` wslay-release-1.1.1/doc/sphinx/man/wslay_event_context_server_init.rst000066400000000000000000000167211367333100700264010ustar00rootroot00000000000000.. highlight:: c wslay_event_context_server_init, wslay_event_context_client_init, wslay_event_context_free ========================================================================================== SYNOPSIS -------- #include .. c:function:: int wslay_event_context_server_init(wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) .. c:function:: int wslay_event_context_client_init(wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) .. c:function:: void wslay_event_context_free(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_context_server_init` function initializes an :c:event-based API context for WebSocket server use. :c:func:`wslay_event_context_client_init` function initializes an :c:event-based API context for WebSocket client use. If they return :c:successfully, `ctx` will point to a structure which holds any :c:necessary resources needed to process WebSocket protocol transfers. *callbacks* is a pointer to :c:type:`struct wslay_event_callbacks`, which is defined as follows:: struct wslay_event_callbacks { wslay_event_recv_callback recv_callback; wslay_event_send_callback send_callback; wslay_event_genmask_callback genmask_callback; wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback; wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback; wslay_event_on_frame_recv_end_callback on_frame_recv_end_callback; wslay_event_on_msg_recv_callback on_msg_recv_callback; }; **recv_callback** .. c:type:: typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data) *recv_callback* is invoked by :c:func:`wslay_event_recv` when it wants to receive more data from peer. The implementation of this callback function must read data at most *len* bytes from peer and store them in *buf* and return the number of bytes read. *flags* is always 0 in this version. If there is an error, return -1 and set error code ``WSLAY_ERR_CALLBACK_FAILURE`` using :c:func:`wslay_event_set_error`. Wslay event-based API on the whole assumes non-blocking I/O. If the cause of error is ``EAGAIN`` or ``EWOULDBLOCK``, set ``WSLAY_ERR_WOULDBLOCK`` instead. This is important because it tells :c:func:`wslay_event_recv` to stop receiving further data and return. **send_callback** .. c:type:: typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) *send_callback* is invoked by :c:func:`wslay_event_send` when it wants to send more data to peer. The implementation of this callback function must send data at most *len* bytes to peer and return the number of bytes sent. *flags* is the bitwise OR of zero or more of the following flag: ``WSLAY_MSG_MORE`` There is more data to send It provides some hints to tune performance and behaviour. If there is an error, return -1 and set error code ``WSLAY_ERR_CALLBACK_FAILURE`` using :c:func:`wslay_event_set_error`. Wslay event-based API on the whole assumes non-blocking I/O. If the cause of error is ``EAGAIN`` or ``EWOULDBLOCK``, set ``WSLAY_ERR_WOULDBLOCK`` instead. This is important because it tells :c:func:`wslay_event_send` to stop sending data and return. **genmask_callback** .. c:type:: typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) *genmask_callback* is invoked by :c:func:`wslay_event_send` when it wants new mask key. As described in RFC6455, only the traffic from WebSocket client is masked, so this callback function is only needed if an event-based API is initialized for WebSocket client use. The implementation of this callback function must fill exactly *len* bytes of data in *buf* and return 0 on success. If there is an error, return -1 and set error code ``WSLAY_ERR_CALLBACK_FAILURE`` using :c:func:`wslay_event_set_error`. **on_frame_recv_start_callback** .. c:type:: typedef void (*wslay_event_on_frame_recv_start_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data) *on_frame_recv_start_callback* is invoked by :c:func:`wslay_event_recv` when a new frame starts to be received. This callback function is only invoked once for each frame. :c:type:`struct wslay_event_on_frame_recv_start_arg` is defined as follows:: struct wslay_event_on_frame_recv_start_arg { uint8_t fin; uint8_t rsv; uint8_t opcode; uint64_t payload_length; }; *fin*, *rsv* and *opcode* is fin bit and reserved bits and opcode of a frame. *payload_length* is a payload length of a frame. **on_frame_recv_chunk_callback** .. c:type:: typedef void (*wslay_event_on_frame_recv_chunk_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data) *on_frame_recv_chunk_callback* is invoked by :c:func:`wslay_event_recv` when a chunk of frame payload is received. :c:type:`struct wslay_event_on_frame_recv_chunk_arg` is defined as follows:: struct wslay_event_on_frame_recv_chunk_arg { const uint8_t *data; size_t data_length; }; *data* points to a chunk of payload data. *data_length* is the length of a chunk. **on_frame_recv_end_callback** .. c:type:: typedef void (*wslay_event_on_frame_recv_end_callback)(wslay_event_context_ptr ctx, void *user_data) *on_frame_recv_end_callback* is invoked by :c:func:`wslay_event_recv` when a frame is completely received. **on_msg_recv_callback** .. c:type:: typedef void (*wslay_event_on_msg_recv_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) *on_msg_recv_callback* is invoked by :c:func:`wslay_event_recv` when a message is completely received. :c:type:`struct wslay_event_on_msg_recv_arg` is defined as follows:: struct wslay_event_on_msg_recv_arg { uint8_t rsv; uint8_t opcode; const uint8_t *msg; size_t msg_length; uint16_t status_code; }; The *rsv* member and the *opcode* member are reserved bits and opcode of received message respectively. The *rsv* member is constructed as follows:: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 The *msg* member points to the message of the received message. The *msg_length* member is the length of message. If a message is close control frame, in other words, ``opcode == WSLAY_CONNECTION_CLOSE``, *status_code* is set to the status code in the close control frame. If no status code is included in the close control frame, *status_code* set to 0. *user_data* is an arbitrary pointer, which is directly passed to each callback functions as *user_data* argument. When initialized event-based API context *ctx* is no longer used, use :c:func:`wslay_event_context_free` to free any resources allocated for *ctx*. RETURN VALUE ------------ :c:func:`wslay_event_context_server_init` and :c:func:`wslay_event_context_client_init` returns 0 if it succeeds, or one of the following negative error codes: .. describe:: WSLAY_ERR_NOMEM Out of memory. SEE ALSO -------- :c:func:`wslay_event_send`, :c:func:`wslay_event_recv`, :c:func:`wslay_event_set_error` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_close_received.rst000066400000000000000000000006061367333100700261110ustar00rootroot00000000000000wslay_event_get_close_received ============================== SYNOPSIS -------- #include .. c:function:: int wslay_event_get_close_received(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_close_received` returns 1 if a close control frame has been received from peer, or returns 0. SEE ALSO -------- :c:func:`wslay_event_get_close_sent` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_close_sent.rst000066400000000000000000000005601367333100700252730ustar00rootroot00000000000000wslay_event_get_close_sent ========================== SYNOPSIS -------- #include .. c:function:: int wslay_event_get_close_sent(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_close_sent` returns 1 if a close control frame has been sent to peer, or returns 0. SEE ALSO -------- :c:func:`wslay_event_get_close_sent` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_queued_msg_count.rst000066400000000000000000000004611367333100700265030ustar00rootroot00000000000000wslay_event_get_queued_msg_count ================================ SYNOPSIS -------- #include .. c:function:: size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_queued_msg_count` returns the number of queued messages. wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_queued_msg_length.rst000066400000000000000000000006551367333100700266410ustar00rootroot00000000000000wslay_event_get_queued_msg_length ================================= SYNOPSIS -------- #include .. c:function:: size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_queued_msg_length` returns the sum of queued message length. It only counts the message length queued using :c:func:`wslay_event_queue_msg` or :c:func:`wslay_event_queue_close`. wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_read_enabled.rst000066400000000000000000000007441367333100700255260ustar00rootroot00000000000000wslay_event_get_read_enabled ============================ SYNOPSIS -------- #include .. c:function:: int wslay_event_get_read_enabled(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_read_enabled` returns 1 if the event-based API context allows read operation, or return 0. After :c:func:`wslay_event_shutdown_read` is called, :c:func:`wslay_event_get_read_enabled` returns 0. SEE ALSO -------- :c:func:`wslay_event_shutdown_read` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_status_code_received.rst000066400000000000000000000011051367333100700273140ustar00rootroot00000000000000wslay_event_get_status_code_received ==================================== SYNOPSIS -------- #include .. c:function:: uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_status_code_received` returns status code received in close control frame. If no close control frame has not been received, returns ``WSLAY_CODE_ABNORMAL_CLOSURE``. If received close control frame has no status code, returns ``WSLAY_CODE_NO_STATUS_RCVD``. SEE ALSO -------- :c:func:`wslay_event_get_status_code_sent` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_status_code_sent.rst000066400000000000000000000010551367333100700265030ustar00rootroot00000000000000wslay_event_get_status_code_sent ================================ SYNOPSIS -------- #include .. c:function:: uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_status_code_sent` returns status code sent in close control frame. If no close control frame has not been sent, returns ``WSLAY_CODE_ABNORMAL_CLOSURE``. If sent close control frame has no status code, returns ``WSLAY_CODE_NO_STATUS_RCVD``. SEE ALSO -------- :c:func:`wslay_event_get_status_code_received` wslay-release-1.1.1/doc/sphinx/man/wslay_event_get_write_enabled.rst000066400000000000000000000007541367333100700257460ustar00rootroot00000000000000wslay_event_get_write_enabled ============================= SYNOPSIS -------- #include .. c:function:: int wslay_event_get_write_enabled(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_get_write_enabled` returns 1 if the event-based API context allows write operation, or return 0. After :c:func:`wslay_event_shutdown_write` is called, :c:func:`wslay_event_get_write_enabled` returns 0. SEE ALSO -------- :c:func:`wslay_event_shutdown_write` wslay-release-1.1.1/doc/sphinx/man/wslay_event_queue_close.rst000066400000000000000000000026201367333100700246060ustar00rootroot00000000000000wslay_event_queue_close ======================= SYNOPSIS -------- #include .. c:function:: int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code, const uint8_t *reason, size_t reason_length) DESCRIPTION ----------- :c:func:`wslay_event_queue_close` queues close control frame. This function is provided just for convenience. :c:func:`wslay_event_queue_msg` can queue a close control frame as well. *status_code* is the status code of close control frame. *reason* is the close reason encoded in UTF-8. *reason_length* is the length of *reason* in bytes. *reason_length* must be less than 123 bytes. If *status_code* is 0, *reason* and *reason_length* is not used and close control frame with zero-length payload will be queued. This function just queues a message and does not send it. :c:func:`wslay_event_send` function call sends these queued messages. RETURN VALUE ------------ :c:func:`wslay_event_queue_close` returns 0 if it succeeds, or returns the following negative error codes: **WSLAY_ERR_NO_MORE_MSG** Could not queue given message. The one of possible reason is that close control frame has been queued/sent and no further queueing message is not allowed. **WSLAY_ERR_INVALID_ARGUMENT** The given message is invalid. **WSLAY_ERR_NOMEM** Out of memory. SEE ALSO -------- :c:func:`wslay_event_queue_msg`, :c:func:`wslay_event_queue_fragmented_msg` wslay-release-1.1.1/doc/sphinx/man/wslay_event_queue_fragmented_msg.rst000066400000000000000000000060361367333100700264700ustar00rootroot00000000000000.. highlight:: c wslay_event_queue_fragmented_msg, wslay_event_queue_fragmented_msg_ex ===================================================================== SYNOPSIS -------- #include .. c:function:: int wslay_event_queue_fragmented_msg(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg) .. c:function:: int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg, uint8_t rsv) DESCRIPTION ----------- :c:func:`wslay_event_queue_fragmented_msg` and :c:func:`wslay_event_queue_fragmented_msg_ex` queue a fragmented message specified in *arg*. The *struct wslay_event_fragmented_msg* is defined as:: union wslay_event_msg_source { int fd; void *data; }; struct wslay_event_fragmented_msg { uint8_t opcode; union wslay_event_msg_source source; wslay_event_fragmented_msg_callback read_callback; }; .. c:type:: typedef ssize_t (*wslay_event_fragmented_msg_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, const union wslay_event_msg_source *source, int *eof, void *user_data) The *opcode* member is the opcode of the message. The *source* member is an union and normally it contains a "source" to generate message data. The *read_callback* is a callback function called by :c:func:`wslay_event_send` to read message data from *source*. The implementation of :c:type:`wslay_event_fragmented_msg_callback` must store at most *len* bytes of data to *buf* and return the number of stored bytes. If all data is read (i.e., EOF), set *\*eof* to 1. If no data can be generated at the moment, return 0. If there is an error, return -1 and set error code ``WSLAY_ERR_CALLBACK_FAILURE`` using :c:func:`wslay_event_set_error`. This function supports non-control messages only. For control frames, use :c:func:`wslay_event_queue_msg` or :c:func:`wslay_event_queue_close`. This function just queues a message and does not send it. :c:func:`wslay_event_send` function call sends these queued messages. :c:func:`wslay_event_queue_fragmented_msg_ex` additionally accepts *rsv* parameter, which is a reserved bits to send. To set reserved bits, use macro ``WSLAY_RSV1_BIT``, ``WSLAY_RSV2_BIT``, and ``WSLAY_RSV3_BIT``. See :c:func:`wslay_event_config_set_allowed_rsv_bits` to see the allowed reserved bits to set. RETURN VALUE ------------ :c:func:`wslay_event_queue_fragmented_msg` and :c:func:`wslay_event_queue_fragmented_msg_ex` return 0 if it succeeds, or returns the following negative error codes: **WSLAY_ERR_NO_MORE_MSG** Could not queue given message. The one of possible reason is that close control frame has been queued/sent and no further queueing message is not allowed. **WSLAY_ERR_INVALID_ARGUMENT** The given message is invalid; or bit is set in *rsv* which is not allowed (see :c:func:`wslay_event_config_set_allowed_rsv_bits`). **WSLAY_ERR_NOMEM** Out of memory. SEE ALSO -------- :c:func:`wslay_event_queue_msg`, :c:func:`wslay_event_queue_msg_ex`, :c:func:`wslay_event_queue_close` wslay-release-1.1.1/doc/sphinx/man/wslay_event_queue_msg.rst000066400000000000000000000043531367333100700242740ustar00rootroot00000000000000.. highlight:: c wslay_event_queue_msg, wslay_event_queue_msg_ex =============================================== SYNOPSIS -------- #include .. c:function:: int wslay_event_queue_msg(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg) .. c:function:: int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg, uint8_t rsv) DESCRIPTION ----------- :c:func:`wslay_event_queue_msg` and :c:func:`wslay_event_queue_msg_ex` queue message specified in *arg*. The *struct wslay_event_msg* is defined as:: struct wslay_event_msg { uint8_t opcode; const uint8_t *msg; size_t msg_length; }; The *opcode* member is opcode of the message. The *msg* member is the pointer to the message data. The *msg_length* member is the length of message data. This function supports both control and non-control messages and the given message is sent without fragmentation. If fragmentation is needed, use :c:func:`wslay_event_queue_fragmented_msg` function instead. This function makes a copy of *msg* of length *msg_length*. This function just queues a message and does not send it. :c:func:`wslay_event_send` function call sends these queued messages. :c:func:`wslay_event_queue_msg_ex` additionally accepts *rsv* parameter, which is a reserved bits to send. To set reserved bits, use macro ``WSLAY_RSV1_BIT``, ``WSLAY_RSV2_BIT``, and ``WSLAY_RSV3_BIT``. See :c:func:`wslay_event_config_set_allowed_rsv_bits` to see the allowed reserved bits to set. RETURN VALUE ------------ :c:func:`wslay_event_queue_msg` and :c:func:`wslay_event_queue_msg_ex` return 0 if it succeeds, or returns the following negative error codes: **WSLAY_ERR_NO_MORE_MSG** Could not queue given message. The one of possible reason is that close control frame has been queued/sent and no further queueing message is not allowed. **WSLAY_ERR_INVALID_ARGUMENT** The given message is invalid; or RSV1 is set for control frame; or bit is set in *rsv* which is not allowed (see :c:func:`wslay_event_config_set_allowed_rsv_bits`). **WSLAY_ERR_NOMEM** Out of memory. SEE ALSO -------- :c:func:`wslay_event_queue_fragmented_msg`, :c:func:`wslay_event_queue_fragmented_msg_ex`, :c:func:`wslay_event_queue_close` wslay-release-1.1.1/doc/sphinx/man/wslay_event_recv.rst000066400000000000000000000040731367333100700232400ustar00rootroot00000000000000wslay_event_recv ================ SYNOPSIS -------- #include .. c:function:: int wslay_event_recv(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_recv` receives messages from peer. When receiving messages, it uses :c:type:`wslay_event_recv_callback` function. Single call of this function receives multiple messages until :c:type:`wslay_event_recv_callback` function sets error code ``WSLAY_ERR_WOULDBLOCK``. :c:func:`wslay_event_recv` calls following callback functions for their own event (See :c:func:`wslay_event_context_server_init` or :c:func:`wslay_event_context_client_init` for the details of each callbacks). :c:type:`wslay_event_on_frame_recv_start_callback` Called when a new frame starts to be received. :c:type:`wslay_event_on_frame_recv_start_callback` Called when a new frame starts to be received. :c:type:`wslay_event_on_frame_recv_chunk_callback` Called when a chunk of frame payload is received. :c:type:`wslay_event_on_frame_recv_end_callback` Called when a frame is completely received. :c:type:`wslay_event_on_msg_recv_callback` Called when a message is completely received. When close control frame is received, this function automatically queues close control frame. Also this function calls :c:func:`wslay_event_set_read_enabled` with second argument 0 to disable further read from peer. When ping control frame is received, this function automatically queues pong control frame. In case of a fatal error which leads to negative return code, this function calls :c:func:`wslay_event_set_read_enabled` with second argument 0 to disable further read from peer. RETURN VALUE ------------ :c:func:`wslay_event_recv` returns 0 if it succeeds, or one of the following negative error codes: **WSLAY_ERR_CALLBACK_FAILURE** User defined callback function is failed. **WSLAY_ERR_NOMEM** Out of memory. When negative error code is returned, application must not make any further call of :c:func:`wslay_event_recv` and must close WebSocket connection. SEE ALSO -------- :c:func:`wslay_event_set_read_enabled` wslay-release-1.1.1/doc/sphinx/man/wslay_event_send.rst000066400000000000000000000033661367333100700232360ustar00rootroot00000000000000wslay_event_send ================ SYNOPSIS -------- #include .. c:function:: int wslay_event_send(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_send` sends queued messages to peer. When sending a message, it uses :c:type:`wslay_event_send_callback` function. Single call of :c:func:`wslay_event_send` sends multiple messages until :c:type:`wslay_event_send_callback` sets error code ``WSLAY_ERR_WOULDBLOCK``. If *ctx* is initialized for WebSocket client use, :c:func:`wslay_event_send` uses :c:type:`wslay_event_genmask_callback` to get new mask key. When a message queued using :c:func:`wslay_event_queue_fragmented_msg` is sent, :c:func:`wslay_event_send` invokes :c:type:`wslay_event_fragmented_msg_callback` for that message. After close control frame is sent, this function calls :c:func:`wslay_event_set_write_enabled` with second argument 0 to disable further transmission to peer. If there are any pending messages, :c:func:`wslay_event_want_write` returns 1, otherwise returns 0. In case of a fatal error which leads to negative return code, this function calls :c:func:`wslay_event_set_write_enabled` with second argument 0 to disable further transmission to peer. RETURN VALUE ------------ :c:func:`wslay_event_send` returns 0 if it succeeds, or one of the following negative error codes: .. describe:: WSLAY_ERR_CALLBACK_FAILURE User defined callback function is failed. .. describe:: WSLAY_ERR_NOMEM Out of memory. When negative error code is returned, application must not make any further call of :c:func:`wslay_event_send` and must close WebSocket connection. SEE ALSO -------- :c:func:`wslay_event_queue_fragmented_msg`, :c:func:`wslay_event_set_write_enabled`, :c:func:`wslay_event_want_write` wslay-release-1.1.1/doc/sphinx/man/wslay_event_set_error.rst000066400000000000000000000010051367333100700242750ustar00rootroot00000000000000wslay_event_set_error ===================== SYNOPSIS -------- #include .. c:function:: void wslay_event_set_error(wslay_event_context_ptr ctx, int val) DESCRIPTION ----------- :c:func:`wslay_event_set_error` sets error code to tell the library there is an error. This function is typically used in user defined callback functions. See the description of callback function to know which error code should be used. RETURN VALUE ------------ :c:func:`wslay_event_set_error` does not return value. wslay-release-1.1.1/doc/sphinx/man/wslay_event_shutdown_read.rst000066400000000000000000000010251367333100700251410ustar00rootroot00000000000000wslay_event_shutdown_read ========================= SYNOPSIS -------- #include .. c:function:: void wslay_event_shutdown_read(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_shutdown_read` prevents the event-based API context from reading any further data from peer. This function may be used with :c:func:`wslay_event_queue_close` if the application detects error in the data received and wants to fail WebSocket connection. SEE ALSO -------- :c:func:`wslay_event_get_read_enabled` wslay-release-1.1.1/doc/sphinx/man/wslay_event_shutdown_write.rst000066400000000000000000000005701367333100700253640ustar00rootroot00000000000000wslay_event_shutdown_write ========================== SYNOPSIS -------- #include .. c:function:: void wslay_event_shutdown_write(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_shutdown_write` prevents the event-based API context from sending any further data to peer. SEE ALSO -------- :c:func:`wslay_event_get_write_enabled` wslay-release-1.1.1/doc/sphinx/man/wslay_event_want_read.rst000066400000000000000000000012721367333100700242430ustar00rootroot00000000000000wslay_event_want_read ===================== SYNOPSIS -------- #include .. c:function:: int wslay_event_want_read(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_want_read` is used to know whether the library want to read more data from peer. This is useful to decide whether to wait for read event in I/O event notification functions such as :manpage:`select(2)` and :manpage:`poll(2)`. RETURN VALUE ------------ :c:func:`wslay_event_want_read` returns 1 if the library want to read more data from peer, or returns 0. SEE ALSO -------- :c:func:`wslay_event_want_write`, :c:func:`wslay_event_shutdown_read`, :c:func:`wslay_event_get_read_enabled` wslay-release-1.1.1/doc/sphinx/man/wslay_event_want_write.rst000066400000000000000000000012751367333100700244650ustar00rootroot00000000000000wslay_event_want_write ====================== SYNOPSIS -------- #include .. c:function:: int wslay_event_want_write(wslay_event_context_ptr ctx) DESCRIPTION ----------- :c:func:`wslay_event_want_write` is used to know whether the library want to send more data to peer. This is useful to decide whether to wait for write event in I/O event notification functions such as :manpage:`select(2)` and :manpage:`poll(2)`. RETURN VALUE ------------ :c:func:`wslay_event_want_write` returns 1 if the library want to send more data to peer, or returns 0. SEE ALSO -------- :c:func:`wslay_event_want_read`, :c:func:`wslay_event_shutdown_write`, :c:func:`wslay_event_get_write_enabled` wslay-release-1.1.1/doc/sphinx/tutorial.rst000066400000000000000000000145331367333100700207530ustar00rootroot00000000000000.. highlight:: c Tutorial ======== WebSocket Echo Server --------------------- This section describes briefly how to make WebSocket echo server with Wslay library in C. The complete source code is located at :file:`examples/fork-echoserv.c`. This WebSocket echo server listens on port given in the command-line. When the incoming connection from the client is accepted, it forks another process. The parent process goes back to the event loop and can accept another client. The child process communicates with the client (WebSocket HTTP handshake and then WebSocket data transfer). For the purpose of this tutorial, we focus on the use of Wslay library. The primary function to communicate with WebSocket client is :c:func:`communicate` function. .. c:function:: int communicate(int fd) This function performs HTTP handshake and WebSocket data transfer until close handshake is done or an error occurs. *fd* is the file descriptor of the connection to the client. This function returns 0 if it succeeds, or returns 0. Let's look into this function. First we perform HTTP handshake. It will be done with :c:func:`http_handshake` function. When it succeeds, we make the file descriptor of the connection non-block. You may set other socket options like ``TCP_NODELAY``. At this point, we can start WebSocket data transfer. Now establish callbacks for wslay event-based API. We use 3 callbacks in this example:: struct wslay_event_callbacks callbacks = { recv_callback, send_callback, NULL, NULL, NULL, NULL, on_msg_recv_callback }; ``recv_callback`` is invoked by :c:func:`wslay_event_recv` when it wants to read more data from the client. It looks like this:: ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) { struct Session *session = (struct Session*)user_data; ssize_t r; while((r = recv(session->fd, buf, len, 0)) == -1 && errno == EINTR); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } else if(r == 0) { /* Unexpected EOF is also treated as an error */ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); r = -1; } return r; } If :c:func:`recv` failed with ``EAGAIN`` or ``EWOULDBLOCK`` (notice that we made socket non-block), we set ``WSLAY_ERR_WOULDBLOCK`` using :c:func:`wslay_event_set_error` to tell the wslay library to stop reading from the socket. If it failed with other reasons, we set ``WSLAY_ERR_CALLBACK_FAILED``, and it will make :c:func:`wslay_event_recv` fail. Notice that reading EOF here is unexpected: so it is also treated as an error. ``send_callback`` is invoked by :c:func:`wslay_event_send` when it wants to send data to the client. It looks like this:: ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, void *user_data) { struct Session *session = (struct Session*)user_data; ssize_t r; int sflags = 0; #ifdef MSG_MORE if(flags & WSLAY_MSG_MORE) { sflags |= MSG_MORE; } #endif // MSG_MORE while((r = send(session->fd, data, len, sflags)) == -1 && errno == EINTR); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } return r; } Similar to ``recv_callback``, we set error code using :c:func:`wslay_event_set_error` depending on the ``errno`` value. ``on_msg_recv_callback`` is invoked by :c:func:`wslay_event_recv` when it have received a message completely. It looks like this:: void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { /* Echo back non-control message */ if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = { arg->opcode, arg->msg, arg->msg_length }; wslay_event_queue_msg(ctx, &msgarg); } } Here, since we are building echo server, we just echo back non-control frames to the client. ``arg->opcode`` is a opcode of the received message. ``arg->msg`` contains received message data with length ``arg->msg_length``. :c:func:`wslay_event_queue_msg` queues message to the client. Then initialize wslay event-based API context:: wslay_event_context_server_init(&ctx, &callbacks, &session); At this point, we finished initialization of Wslay library and all we have to do is run event-loop and communicate with the client. For event-loop we need event notification mechanism, here we use standard :c:func:`poll`. Since we don't have any message to send client, first we query read event only. The event loop looks like this:: /* * Event loop: basically loop until both wslay_event_want_read(ctx) * and wslay_event_want_write(ctx) return 0. */ while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) { int r; while((r = poll(&event, 1, -1)) == -1 && errno == EINTR); if(r == -1) { perror("poll"); res = -1; break; } if(((event.revents & POLLIN) && wslay_event_recv(ctx) != 0) || ((event.revents & POLLOUT) && wslay_event_send(ctx) != 0) || (event.revents & (POLLERR | POLLHUP | POLLNVAL))) { /* * If either wslay_event_recv() or wslay_event_send() return * non-zero value, it means serious error which prevents wslay * library from processing further data, so WebSocket connection * must be closed. */ res = -1; break; } event.events = 0; if(wslay_event_want_read(ctx)) { event.events |= POLLIN; } if(wslay_event_want_write(ctx)) { event.events |= POLLOUT; } } return res; Basically, we just loop until both :c:func:`wslay_event_want_read` and :c:func:`wslay_event_want_write` return 0. Also if either :c:func:`wslay_event_recv` or :c:func:`wslay_event_send` return non-zero value, we exit the loop. If there is data to read, call :c:func:`wslay_event_recv`. If there is data to write and writing will not block, call :c:func:`wslay_event_send`. After exiting the event loop, we just close the connection, most likely, using ``shutdown(fd, SHUT_WR)`` and ``close(fd)``. wslay-release-1.1.1/examples/000077500000000000000000000000001367333100700161105ustar00rootroot00000000000000wslay-release-1.1.1/examples/.gitignore000066400000000000000000000000421367333100700200740ustar00rootroot00000000000000fork-echoserv echoserv testclient wslay-release-1.1.1/examples/CMakeLists.txt000066400000000000000000000015241367333100700206520ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) include(CheckIncludeFiles) check_include_files("sys/epoll.h" HAVE_EPOLL) if(NOT HAVE_EPOLL) message(FATAL_ERROR "epoll is required") endif() find_package(Nettle REQUIRED) if(WSLAY_STATIC) set(WSLAY_TARGET "wslay") else() set(WSLAY_TARGET "wslay_shared") endif() add_executable(echoserv echoserv.cc) target_include_directories(echoserv PRIVATE ${NETTLE_INCLUDE_DIRS}) target_link_libraries(echoserv ${WSLAY_TARGET} ${NETTLE_LIBRARIES}) add_executable(fork-echoserv fork-echoserv.c) target_include_directories(fork-echoserv PRIVATE ${NETTLE_INCLUDE_DIRS}) target_link_libraries(fork-echoserv ${WSLAY_TARGET} ${NETTLE_LIBRARIES}) add_executable(testclient testclient.cc) target_include_directories(testclient PRIVATE ${NETTLE_INCLUDE_DIRS}) target_link_libraries(testclient ${WSLAY_TARGET} ${NETTLE_LIBRARIES}) wslay-release-1.1.1/examples/Makefile.am000066400000000000000000000027501367333100700201500ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 2013 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 ENABLE_EXAMPLES AM_CPPFLAGS = -Wall -I$(top_srcdir)/lib/includes \ -I$(top_builddir)/lib/includes @NETTLE_CFLAGS@ @DEFS@ AM_LDFLAGS = @NETTLE_LIBS@ LDADD = $(top_builddir)/lib/libwslay.la bin_PROGRAMS = fork-echoserv echoserv testclient fork_echoserv_SOURCES = fork-echoserv.c echoserv_SOURCES = echoserv.cc testclient_SOURCES = testclient.cc endif # ENABLE_EXAMPLES wslay-release-1.1.1/examples/echoserv.cc000066400000000000000000000351771367333100700202520ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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. */ // WebSocket Echo Server // This is suitable for Autobahn server test. // g++ -Wall -O2 -g -o echoserv echoserv.cc -L../lib/.libs -I../lib/includes -lwslay -lnettle // $ export LD_LIBRARY_PATH=../lib/.libs // $ ./a.out 9000 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int create_listen_socket(const char *service) { struct addrinfo hints; int sfd = -1; int r; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; struct addrinfo *res; r = getaddrinfo(0, 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) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(sfd == -1) { continue; } int val = 1; if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { continue; } if(bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) { break; } close(sfd); } freeaddrinfo(res); if(listen(sfd, 16) == -1) { perror("listen"); close(sfd); return -1; } return sfd; } 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; } std::string sha1(const std::string& src) { sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, src.size(), reinterpret_cast(src.c_str())); uint8_t temp[SHA1_DIGEST_SIZE]; sha1_digest(&ctx, SHA1_DIGEST_SIZE, temp); std::string res(&temp[0], &temp[SHA1_DIGEST_SIZE]); return res; } std::string base64(const std::string& src) { base64_encode_ctx ctx; base64_encode_init(&ctx); int dstlen = BASE64_ENCODE_RAW_LENGTH(src.size()); char *dst = new char[dstlen]; base64_encode_raw(dst, src.size(), reinterpret_cast(src.c_str())); std::string res(&dst[0], &dst[dstlen]); delete [] dst; return res; } std::string create_acceptkey(const std::string& clientkey) { std::string s = clientkey+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; return base64(sha1(s)); } class EventHandler { public: virtual ~EventHandler() {} virtual int on_read_event() = 0; virtual int on_write_event() = 0; virtual bool want_read() = 0; virtual bool want_write() = 0; virtual int fd() const = 0; virtual bool finish() = 0; virtual EventHandler* next() = 0; }; ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data); ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data); void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data); class EchoWebSocketHandler : public EventHandler { public: EchoWebSocketHandler(int fd) : fd_(fd) { struct wslay_event_callbacks callbacks = { recv_callback, send_callback, NULL, /* genmask_callback */ NULL, /* on_frame_recv_start_callback */ NULL, /* on_frame_recv_callback */ NULL, /* on_frame_recv_end_callback */ on_msg_recv_callback }; wslay_event_context_server_init(&ctx_, &callbacks, this); } virtual ~EchoWebSocketHandler() { wslay_event_context_free(ctx_); shutdown(fd_, SHUT_WR); close(fd_); } virtual int on_read_event() { if(wslay_event_recv(ctx_) == 0) { return 0; } else { return -1; } } virtual int on_write_event() { if(wslay_event_send(ctx_) == 0) { return 0; } else { return -1; } } ssize_t send_data(const uint8_t *data, size_t len, int flags) { ssize_t r; int sflags = 0; #ifdef MSG_MORE if(flags & WSLAY_MSG_MORE) { sflags |= MSG_MORE; } #endif // MSG_MORE while((r = send(fd_, data, len, sflags)) == -1 && errno == EINTR); return r; } ssize_t recv_data(uint8_t *data, size_t len, int flags) { ssize_t r; while((r = recv(fd_, data, len, 0)) == -1 && errno == EINTR); return r; } virtual bool want_read() { return wslay_event_want_read(ctx_); } virtual bool want_write() { return wslay_event_want_write(ctx_); } virtual int fd() const { return fd_; } virtual bool finish() { return !want_read() && !want_write(); } virtual EventHandler* next() { return 0; } private: int fd_; wslay_event_context_ptr ctx_; }; ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) { EchoWebSocketHandler *sv = (EchoWebSocketHandler*)user_data; ssize_t r = sv->send_data(data, len, flags); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } return r; } ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) { EchoWebSocketHandler *sv = (EchoWebSocketHandler*)user_data; ssize_t r = sv->recv_data(data, len, flags); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } else if(r == 0) { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); r = -1; } return r; } void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = { arg->opcode, arg->msg, arg->msg_length }; wslay_event_queue_msg(ctx, &msgarg); } } class HttpHandshakeSendHandler : public EventHandler { public: HttpHandshakeSendHandler(int fd, const std::string& accept_key) : fd_(fd), resheaders_("HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: "+accept_key+"\r\n" "\r\n"), off_(0) {} virtual ~HttpHandshakeSendHandler() { if(fd_ != -1) { shutdown(fd_, SHUT_WR); close(fd_); } } virtual int on_read_event() { return 0; } virtual int on_write_event() { while(1) { size_t len = resheaders_.size()-off_; if(len == 0) { break; } ssize_t r; while((r = write(fd_, resheaders_.c_str()+off_, len)) == -1 && errno == EINTR); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { break; } else { perror("write"); return -1; } } else { off_ += r; } } return 0; } virtual bool want_read() { return false; } virtual bool want_write() { return true; } virtual int fd() const { return fd_; } virtual bool finish() { return off_ == resheaders_.size(); } virtual EventHandler* next() { if(finish()) { int fd = fd_; fd_ = -1; return new EchoWebSocketHandler(fd); } else { return 0; } } private: int fd_; std::string headers_; std::string resheaders_; size_t off_; }; class HttpHandshakeRecvHandler : public EventHandler { public: HttpHandshakeRecvHandler(int fd) : fd_(fd) {} virtual ~HttpHandshakeRecvHandler() { if(fd_ != -1) { close(fd_); } } virtual int on_read_event() { char buf[4096]; ssize_t r; std::string client_key; while(1) { while((r = read(fd_, buf, sizeof(buf))) == -1 && errno == EINTR); if(r == -1) { if(errno == EWOULDBLOCK || errno == EAGAIN) { break; } else { perror("read"); return -1; } } else if(r == 0) { std::cerr << "http_upgrade: Got EOF" << std::endl; return -1; } else { headers_.append(buf, buf+r); if(headers_.size() > 8192) { std::cerr << "Too large http header" << std::endl; return -1; } } } if(headers_.find("\r\n\r\n") != std::string::npos) { std::string::size_type keyhdstart; if(headers_.find("Upgrade: websocket\r\n") == std::string::npos || headers_.find("Connection: Upgrade\r\n") == std::string::npos || (keyhdstart = headers_.find("Sec-WebSocket-Key: ")) == std::string::npos) { std::cerr << "http_upgrade: missing required headers" << std::endl; return -1; } keyhdstart += 19; std::string::size_type keyhdend = headers_.find("\r\n", keyhdstart); client_key = headers_.substr(keyhdstart, keyhdend-keyhdstart); accept_key_ = create_acceptkey(client_key); } return 0; } virtual int on_write_event() { return 0; } virtual bool want_read() { return true; } virtual bool want_write() { return false; } virtual int fd() const { return fd_; } virtual bool finish() { return !accept_key_.empty(); } virtual EventHandler* next() { if(finish()) { int fd = fd_; fd_ = -1; return new HttpHandshakeSendHandler(fd, accept_key_); } else { return 0; } } private: int fd_; std::string headers_; std::string accept_key_; }; class ListenEventHandler : public EventHandler { public: ListenEventHandler(int fd) : fd_(fd), cfd_(-1) {} virtual ~ListenEventHandler() { close(fd_); close(cfd_); } virtual int on_read_event() { if(cfd_ != -1) { close(cfd_); } while((cfd_ = accept(fd_, 0, 0)) == -1 && errno == EINTR); if(cfd_ == -1) { perror("accept"); } return 0; } virtual int on_write_event() { 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; } virtual EventHandler* next() { if(cfd_ != -1) { int val = 1; int fd = cfd_; cfd_ = -1; if(make_non_block(fd) == -1 || setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { close(fd); return 0; } return new HttpHandshakeRecvHandler(fd); } else { return 0; } } private: int fd_; int cfd_; }; int ctl_epollev(int epollfd, int op, EventHandler *handler) { epoll_event ev; memset(&ev, 0, sizeof(ev)); int events = 0; if(handler->want_read()) { events |= EPOLLIN; } if(handler->want_write()) { events |= EPOLLOUT; } ev.events = events; ev.data.ptr = handler; return epoll_ctl(epollfd, op, handler->fd(), &ev); } void reactor(int sfd) { std::set handlers; ListenEventHandler* listen_handler = new ListenEventHandler(sfd); handlers.insert(listen_handler); int epollfd = epoll_create(16); if(epollfd == -1) { perror("epoll_create"); exit(EXIT_FAILURE); } if(ctl_epollev(epollfd, EPOLL_CTL_ADD, listen_handler) == -1) { perror("epoll_ctl"); exit(EXIT_FAILURE); } static const size_t MAX_EVENTS = 64; epoll_event events[MAX_EVENTS]; while(1) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if(nfds == -1) { perror("epoll_wait"); return; } for(int n = 0; n < nfds; ++n) { EventHandler* eh = (EventHandler*)events[n].data.ptr; if(((events[n].events & EPOLLIN) && eh->on_read_event() == -1) || ((events[n].events & EPOLLOUT) && eh->on_write_event() == -1) || (events[n].events & (EPOLLERR | EPOLLHUP))) { handlers.erase(eh); delete eh; } else { EventHandler* next = eh->next(); if(next) { handlers.insert(next); if(ctl_epollev(epollfd, EPOLL_CTL_ADD, next) == -1) { if(errno == EEXIST) { if(ctl_epollev(epollfd, EPOLL_CTL_MOD, next) == -1) { perror("epoll_ctl"); delete next; } } else { perror("epoll_ctl"); delete next; } } } if(eh->finish()) { handlers.erase(eh); delete eh; } else { if(ctl_epollev(epollfd, EPOLL_CTL_MOD, eh) == -1) { perror("epoll_ctl"); } } } } } } int main(int argc, char **argv) { if(argc < 2) { std::cerr << "Usage: " << argv[0] << " PORT" << std::endl; exit(EXIT_FAILURE); } struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, 0); int sfd = create_listen_socket(argv[1]); if(sfd == -1) { std::cerr << "Failed to create server socket" << std::endl; exit(EXIT_FAILURE); } std::cout << "WebSocket echo server, listening on " << argv[1] << std::endl; reactor(sfd); } wslay-release-1.1.1/examples/fork-echoserv.c000066400000000000000000000335321367333100700210370ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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. */ /* * WebSocket Echo Server * This is suitable for Autobahn server test. * * Dependency: nettle-dev * * To compile: * $ gcc -Wall -O2 -g -o fork-echoserv fork-echoserv.c -L../lib/.libs -I../lib/includes -lwslay -lnettle * * To run: * $ export LD_LIBRARY_PATH=../lib/.libs * $ ./a.out 9000 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Create server socket, listen on *service*. This function returns * file descriptor of server socket if it succeeds, or returns -1. */ int create_listen_socket(const char *service) { struct addrinfo hints, *res, *rp; int sfd = -1; int r; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; r = getaddrinfo(0, service, &hints, &res); if(r != 0) { fprintf(stderr, "getaddrinfo: %s", gai_strerror(r)); return -1; } for(rp = res; rp; rp = rp->ai_next) { int val = 1; sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(sfd == -1) { continue; } if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &val, (socklen_t)sizeof(val)) == -1) { continue; } if(bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) { break; } close(sfd); } freeaddrinfo(res); if(listen(sfd, 16) == -1) { perror("listen"); close(sfd); return -1; } return sfd; } /* * Makes file descriptor *fd* non-blocking mode. * This function returns 0, or returns -1. */ int make_non_block(int fd) { int flags, r; while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); if(flags == -1) { perror("fcntl"); return -1; } while((r = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); if(r == -1) { perror("fcntl"); return -1; } return 0; } /* * Calculates SHA-1 hash of *src*. The size of *src* is *src_length* bytes. * *dst* must be at least SHA1_DIGEST_SIZE. */ void sha1(uint8_t *dst, const uint8_t *src, size_t src_length) { struct sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, src_length, src); sha1_digest(&ctx, SHA1_DIGEST_SIZE, dst); } /* * Base64-encode *src* and stores it in *dst*. * The size of *src* is *src_length*. * *dst* must be at least BASE64_ENCODE_RAW_LENGTH(src_length). */ void base64(uint8_t *dst, const uint8_t *src, size_t src_length) { struct base64_encode_ctx ctx; base64_encode_init(&ctx); base64_encode_raw((char *)dst, src_length, src); } #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" /* * Create Server's accept key in *dst*. * *client_key* is the value of |Sec-WebSocket-Key| header field in * client's handshake and it must be length of 24. * *dst* must be at least BASE64_ENCODE_RAW_LENGTH(20)+1. */ void create_accept_key(char *dst, const char *client_key) { uint8_t sha1buf[20], key_src[60]; memcpy(key_src, client_key, 24); memcpy(key_src+24, WS_GUID, 36); sha1(sha1buf, key_src, sizeof(key_src)); base64((uint8_t*)dst, sha1buf, 20); dst[BASE64_ENCODE_RAW_LENGTH(20)] = '\0'; } /* We parse HTTP header lines of the format * \r\nfield_name: value1, value2, ... \r\n * * If the caller is looking for a specific value, we return a pointer to the * start of that value, else we simply return the start of values list. */ static char* http_header_find_field_value(char *header, char *field_name, char *value) { char *header_end, *field_start, *field_end, *next_crlf, *value_start; int field_name_len; /* Pointer to the last character in the header */ header_end = header + strlen(header) - 1; field_name_len = strlen(field_name); field_start = header; do{ field_start = strstr(field_start+1, field_name); field_end = field_start + field_name_len - 1; if(field_start != NULL && field_start - header >= 2 && field_start[-2] == '\r' && field_start[-1] == '\n' && header_end - field_end >= 1 && field_end[1] == ':') { break; /* Found the field */ } else { continue; /* This is not the one; keep looking. */ } } while(field_start != NULL); if(field_start == NULL) return NULL; /* Find the field terminator */ next_crlf = strstr(field_start, "\r\n"); /* A field is expected to end with \r\n */ if(next_crlf == NULL) return NULL; /* Malformed HTTP header! */ /* If not looking for a value, then return a pointer to the start of values string */ if(value == NULL) return field_end+2; value_start = strstr(field_start, value); /* Value not found */ if(value_start == NULL) return NULL; /* Found the value we're looking for */ if(value_start > next_crlf) return NULL; /* ... but after the CRLF terminator of the field. */ /* The value we found should be properly delineated from the other tokens */ if(isalnum(value_start[-1]) || isalnum(value_start[strlen(value)])) return NULL; return value_start; } /* * Performs HTTP handshake. *fd* is the file descriptor of the * connection to the client. This function returns 0 if it succeeds, * or returns -1. */ int http_handshake(int fd) { /* * Note: The implementation of HTTP handshake in this function is * written for just a example of how to use of wslay library and is * not meant to be used in production code. In practice, you need * to do more strict verification of the client's handshake. */ char header[16384], accept_key[29], *keyhdstart, *keyhdend, res_header[256]; size_t header_length = 0, res_header_sent = 0, res_header_length; ssize_t r; while(1) { while((r = read(fd, header+header_length, sizeof(header)-header_length)) == -1 && errno == EINTR); if(r == -1) { perror("read"); return -1; } else if(r == 0) { fprintf(stderr, "HTTP Handshake: Got EOF"); return -1; } else { header_length += r; if(header_length >= 4 && memcmp(header+header_length-4, "\r\n\r\n", 4) == 0) { break; } else if(header_length == sizeof(header)) { fprintf(stderr, "HTTP Handshake: Too large HTTP headers"); return -1; } } } if(http_header_find_field_value(header, "Upgrade", "websocket") == NULL || http_header_find_field_value(header, "Connection", "Upgrade") == NULL || (keyhdstart = http_header_find_field_value(header, "Sec-WebSocket-Key", NULL)) == NULL) { fprintf(stderr, "HTTP Handshake: Missing required header fields"); return -1; } for(; *keyhdstart == ' '; ++keyhdstart); keyhdend = keyhdstart; for(; *keyhdend != '\r' && *keyhdend != ' '; ++keyhdend); if(keyhdend-keyhdstart != 24) { printf("%s\n", keyhdstart); fprintf(stderr, "HTTP Handshake: Invalid value in Sec-WebSocket-Key"); return -1; } create_accept_key(accept_key, keyhdstart); snprintf(res_header, sizeof(res_header), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "\r\n", accept_key); res_header_length = strlen(res_header); while(res_header_sent < res_header_length) { while((r = write(fd, res_header+res_header_sent, res_header_length-res_header_sent)) == -1 && errno == EINTR); if(r == -1) { perror("write"); return -1; } else { res_header_sent += r; } } return 0; } /* * This struct is passed as *user_data* in callback function. The * *fd* member is the file descriptor of the connection to the client. */ struct Session { int fd; }; ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) { struct Session *session = (struct Session*)user_data; ssize_t r; int sflags = 0; #ifdef MSG_MORE if(flags & WSLAY_MSG_MORE) { sflags |= MSG_MORE; } #endif // MSG_MORE while((r = send(session->fd, data, len, sflags)) == -1 && errno == EINTR); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } return r; } ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data) { struct Session *session = (struct Session*)user_data; ssize_t r; while((r = recv(session->fd, buf, len, 0)) == -1 && errno == EINTR); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } else if(r == 0) { /* Unexpected EOF is also treated as an error */ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); r = -1; } return r; } void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { /* Echo back non-control message */ if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = { arg->opcode, arg->msg, arg->msg_length }; wslay_event_queue_msg(ctx, &msgarg); } } /* * Communicate with the client. This function performs HTTP handshake * and WebSocket data transfer until close handshake is done or an * error occurs. *fd* is the file descriptor of the connection to the * client. This function returns 0 if it succeeds, or returns 0. */ int communicate(int fd) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks = { recv_callback, send_callback, NULL, NULL, NULL, NULL, on_msg_recv_callback }; struct Session session = { fd }; int val = 1; struct pollfd event; int res = 0; if(http_handshake(fd) == -1) { return -1; } if(make_non_block(fd) == -1) { return -1; } if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { perror("setsockopt: TCP_NODELAY"); return -1; } memset(&event, 0, sizeof(struct pollfd)); event.fd = fd; event.events = POLLIN; wslay_event_context_server_init(&ctx, &callbacks, &session); /* * Event loop: basically loop until both wslay_event_want_read(ctx) * and wslay_event_want_write(ctx) return 0. */ while(wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) { int r; while((r = poll(&event, 1, -1)) == -1 && errno == EINTR); if(r == -1) { perror("poll"); res = -1; break; } if(((event.revents & POLLIN) && wslay_event_recv(ctx) != 0) || ((event.revents & POLLOUT) && wslay_event_send(ctx) != 0) || (event.revents & (POLLERR | POLLHUP | POLLNVAL))) { /* * If either wslay_event_recv() or wslay_event_send() return * non-zero value, it means serious error which prevents wslay * library from processing further data, so WebSocket connection * must be closed. */ res = -1; break; } event.events = 0; if(wslay_event_want_read(ctx)) { event.events |= POLLIN; } if(wslay_event_want_write(ctx)) { event.events |= POLLOUT; } } return res; } /* * Serves echo back service forever. *sfd* is the file descriptor of * the server socket. when the incoming connection from the client is * accepted, this function forks another process and the forked * process communicates with client. The parent process goes back to * the loop and can accept another client. */ void serve(int sfd) { while(1) { int fd; while((fd = accept(sfd, NULL, NULL)) == -1 && errno == EINTR); if(fd == -1) { perror("accept"); } else { int r = fork(); if(r == -1) { perror("fork"); close(fd); } else if(r == 0) { int r = communicate(fd); shutdown(fd, SHUT_WR); close(fd); if(r == 0) { exit(EXIT_SUCCESS); } else { exit(EXIT_FAILURE); } } } } } int main(int argc, char **argv) { struct sigaction act; int sfd; if(argc < 2) { fprintf(stderr, "Usage: %s PORT\n", argv[0]); exit(EXIT_FAILURE); } memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGCHLD, &act, NULL); sfd = create_listen_socket(argv[1]); if(sfd == -1) { fprintf(stderr, "Failed to create server socket\n"); exit(EXIT_FAILURE); } printf("WebSocket echo server, listening on %s\n", argv[1]); serve(sfd); return EXIT_SUCCESS; } wslay-release-1.1.1/examples/testclient.cc000066400000000000000000000332631367333100700206040ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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. */ // WebSocket Test Client for Autobahn client test // $ g++ -Wall -O2 -g -o testclient testclient.cc -L../lib/.libs -I../lib/includes -lwslay -lnettle // $ export LD_LIBRARY_PATH=../lib/.libs // $ ./a.out localhost 9001 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int connect_to(const char *host, const char *service) { struct addrinfo hints; int fd = -1; int r; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; struct addrinfo *res; r = getaddrinfo(host, 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_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; } std::string sha1(const std::string& src) { sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, src.size(), reinterpret_cast(src.c_str())); uint8_t temp[SHA1_DIGEST_SIZE]; sha1_digest(&ctx, SHA1_DIGEST_SIZE, temp); std::string res(&temp[0], &temp[SHA1_DIGEST_SIZE]); return res; } std::string base64(const std::string& src) { base64_encode_ctx ctx; base64_encode_init(&ctx); int dstlen = BASE64_ENCODE_RAW_LENGTH(src.size()); char *dst = new char[dstlen]; base64_encode_raw(dst, src.size(), reinterpret_cast(src.c_str())); std::string res(&dst[0], &dst[dstlen]); delete [] dst; return res; } std::string create_acceptkey(const std::string& clientkey) { std::string s = clientkey+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; return base64(sha1(s)); } class WebSocketClient { public: WebSocketClient(int fd, struct wslay_event_callbacks *callbacks, const std::string& body) : fd_(fd), body_(body), body_off_(0), dev_urand_("/dev/urandom") { wslay_event_context_client_init(&ctx_, callbacks, this); } ~WebSocketClient() { wslay_event_context_free(ctx_); shutdown(fd_, SHUT_WR); close(fd_); } int on_read_event() { return wslay_event_recv(ctx_); } int on_write_event() { return wslay_event_send(ctx_); } ssize_t send_data(const uint8_t *data, size_t len, int flags) { ssize_t r; int sflags = 0; #ifdef MSG_MORE if(flags & WSLAY_MSG_MORE) { sflags |= MSG_MORE; } #endif // MSG_MORE while((r = send(fd_, data, len, sflags)) == -1 && errno == EINTR); return r; } ssize_t feed_body(uint8_t *data, size_t len) { if(body_off_ < body_.size()) { size_t wlen = std::min(len, body_.size()-body_off_); memcpy(data, body_.c_str(), wlen); body_off_ += wlen; return wlen; } else { return 0; } } ssize_t recv_data(uint8_t *data, size_t len, int flags) { ssize_t r; while((r = recv(fd_, data, len, 0)) == -1 && errno == EINTR); return r; } bool want_read() { return wslay_event_want_read(ctx_); } bool want_write() { return wslay_event_want_write(ctx_); } int fd() const { return fd_; } void get_random(uint8_t *buf, size_t len) { dev_urand_.read((char*)buf, len); } void set_callbacks(const struct wslay_event_callbacks *callbacks) { wslay_event_config_set_callbacks(ctx_, callbacks); } private: int fd_; wslay_event_context_ptr ctx_; std::string body_; size_t body_off_; std::fstream dev_urand_; }; ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) { WebSocketClient *ws = (WebSocketClient*)user_data; ssize_t r = ws->send_data(data, len, flags); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } return r; } ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) { WebSocketClient *ws = (WebSocketClient*)user_data; ssize_t r = ws->recv_data(data, len, flags); if(r == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK); } else { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); } } else if(r == 0) { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); r = -1; } return r; } ssize_t feed_body_callback (wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) { WebSocketClient *ws = (WebSocketClient*)user_data; return ws->feed_body(data, len); } int genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) { WebSocketClient *ws = (WebSocketClient*)user_data; ws->get_random(buf, len); return 0; } void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { if(!wslay_is_ctrl_frame(arg->opcode)) { struct wslay_event_msg msgarg = { arg->opcode, arg->msg, arg->msg_length }; wslay_event_queue_msg(ctx, &msgarg); } } std::string casecntjson; void get_casecnt_on_msg_recv_callback (wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { if(arg->opcode == WSLAY_TEXT_FRAME) { casecntjson.assign(arg->msg, arg->msg+arg->msg_length); } } int send_http_handshake(int fd, const std::string& reqheader) { size_t off = 0; while(off < reqheader.size()) { ssize_t r; size_t len = reqheader.size()-off; while((r = write(fd, reqheader.c_str()+off, len)) == -1 && errno == EINTR); if(r == -1) { perror("write"); return -1; } off += r; } return 0; } int recv_http_handshake(int fd, std::string& resheader) { char buf[4096]; while(1) { ssize_t r; while((r = read(fd, buf, sizeof(buf))) == -1 && errno == EINTR); if(r <= 0) { return -1; } resheader.append(buf, buf+r); if(resheader.find("\r\n\r\n") != std::string::npos) { break; } if(resheader.size() > 8192) { std::cerr << "Too big response header" << std::endl; return -1; } } return 0; } std::string get_random16() { char buf[16]; std::fstream f("/dev/urandom"); f.read(buf, 16); return std::string(buf, buf+16); } int http_handshake(int fd, const char *host, const char *service, const char *path, std::string& body) { char buf[4096]; std::string client_key = base64(get_random16()); snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\n" "Host: %s:%s\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Key: %s\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n", path, host, service, client_key.c_str()); std::string reqheader = buf; if(send_http_handshake(fd, reqheader) == -1) { return -1; } std::string resheader; if(recv_http_handshake(fd, resheader) == -1) { return -1; } std::string::size_type keyhdstart; if((keyhdstart = resheader.find("Sec-WebSocket-Accept: ")) == std::string::npos) { std::cerr << "http_upgrade: missing required headers" << std::endl; return -1; } keyhdstart += 22; std::string::size_type keyhdend = resheader.find("\r\n", keyhdstart); std::string accept_key = resheader.substr(keyhdstart, keyhdend-keyhdstart); if(accept_key == create_acceptkey(client_key)) { body = resheader.substr(resheader.find("\r\n\r\n")+4); return 0; } else { return -1; } } void ctl_epollev(int epollfd, int op, WebSocketClient& ws) { epoll_event ev; memset(&ev, 0, sizeof(ev)); if(ws.want_read()) { ev.events |= EPOLLIN; } if(ws.want_write()) { ev.events |= EPOLLOUT; } if(epoll_ctl(epollfd, op, ws.fd(), &ev) == -1) { perror("epoll_ctl"); exit(EXIT_FAILURE); } } int communicate(const char *host, const char *service, const char *path, const struct wslay_event_callbacks *callbacks) { struct wslay_event_callbacks cb = *callbacks; cb.recv_callback = feed_body_callback; int fd = connect_to(host, service); if(fd == -1) { std::cerr << "Could not connect to the host" << std::endl; return -1; } std::string body; if(http_handshake(fd, host, service, path, body) == -1) { std::cerr << "Failed handshake" << std::endl; close(fd); return -1; } make_non_block(fd); int val = 1; if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)) == -1) { perror("setsockopt: TCP_NODELAY"); return -1; } WebSocketClient ws(fd, &cb, body); if(ws.on_read_event() == -1) { return -1; } cb.recv_callback = callbacks->recv_callback; ws.set_callbacks(&cb); int epollfd = epoll_create(1); if(epollfd == -1) { perror("epoll_create"); return -1; } ctl_epollev(epollfd, EPOLL_CTL_ADD, ws); static const size_t MAX_EVENTS = 1; epoll_event events[MAX_EVENTS]; bool ok = true; while(ws.want_read() || ws.want_write()) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if(nfds == -1) { perror("epoll_wait"); return -1; } for(int n = 0; n < nfds; ++n) { if(((events[n].events & EPOLLIN) && ws.on_read_event() != 0) || ((events[n].events & EPOLLOUT) && ws.on_write_event() != 0)) { ok = false; break; } } if(!ok) { break; } ctl_epollev(epollfd, EPOLL_CTL_MOD, ws); } return ok ? 0 : -1; } int get_casecnt(const char *host, const char *service) { struct wslay_event_callbacks callbacks = { recv_callback, send_callback, genmask_callback, NULL, /* on_frame_recv_start_callback */ NULL, /* on_frame_recv_callback */ NULL, /* on_frame_recv_end_callback */ get_casecnt_on_msg_recv_callback }; if(communicate(host, service, "/getCaseCount", &callbacks) == -1) { return -1; } errno = 0; int casecnt = strtol(casecntjson.c_str(), 0, 10); if(errno == ERANGE) { return -1; } else { return casecnt; } } int run_testcase(const char *host, const char *service, int casenum) { struct wslay_event_callbacks callbacks = { recv_callback, send_callback, genmask_callback, NULL, /* on_frame_recv_start_callback */ NULL, /* on_frame_recv_callback */ NULL, /* on_frame_recv_end_callback */ on_msg_recv_callback }; char buf[1024]; snprintf(buf, sizeof(buf), "/runCase?case=%d&agent=wslay", casenum); return communicate(host, service, buf, &callbacks); } int update_reports(const char *host, const char *service) { struct wslay_event_callbacks callbacks = { recv_callback, send_callback, genmask_callback, NULL, /* on_frame_recv_start_callback */ NULL, /* on_frame_recv_callback */ NULL, /* on_frame_recv_end_callback */ NULL, /* on_msg_recv_callback */ }; return communicate(host, service, "/updateReports?&agent=wslay", &callbacks); } int main(int argc, char **argv) { if(argc < 3) { std::cerr << "Usage: " << argv[0] << " HOST SERV" << std::endl; exit(EXIT_FAILURE); } struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, 0); const char *host = argv[1]; const char *service = argv[2]; int casecnt = get_casecnt(host, service); if(casecnt == -1) { std::cerr << "Failed to get case count." << std::endl; exit(EXIT_FAILURE); } for(int i = 1; i <= casecnt; ++i) { std::cout << "Running test case " << i << std::endl; if(run_testcase(host, service, i) == -1) { std::cout << "Detected error during test" << std::endl; } } if(update_reports(host, service) == -1) { std::cerr << "Failed to update reports." << std::endl; exit(EXIT_FAILURE); } } wslay-release-1.1.1/lib/000077500000000000000000000000001367333100700150405ustar00rootroot00000000000000wslay-release-1.1.1/lib/CMakeLists.txt000066400000000000000000000037151367333100700176060ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) # This CMakeLists.txt file intended for: # - include by master ../CMakeLists.txt # - embedding wslay library into other projects set(PACKAGE_VERSION "1.0.1-DEV") set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/includes) set(GEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/includes) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic-errors -Wno-long-long") include(CheckIncludeFile) include(TestBigEndian) check_include_file("arpa/inet.h" HAVE_ARPA_INET_H) check_include_file("netinet/in.h" HAVE_NETINET_IN_H) check_include_file("winsock2.h" HAVE_WINSOCK2_H) test_big_endian(WORDS_BIGENDIAN) configure_file(${INCLUDE_DIR}/wslay/wslayver.h.in ${GEN_INCLUDE_DIR}/wslay/wslayver.h @ONLY) configure_file(config.h.in config.h @ONLY) set(HEADERS wslay_event.h wslay_frame.h wslay_net.h wslay_queue.h wslay_stack.h ${INCLUDE_DIR}/wslay/wslay.h ${GEN_INCLUDE_DIR}/wslay/wslayver.h) set(SOURCES wslay_event.c wslay_frame.c wslay_net.c wslay_queue.c wslay_stack.c) set(WSLAY_TARGETS) if(WSLAY_STATIC) add_library(wslay STATIC ${SOURCES} ${HEADERS}) list(APPEND WSLAY_TARGETS wslay) endif() if(WSLAY_SHARED) add_library(wslay_shared SHARED ${SOURCES} ${HEADERS}) list(APPEND WSLAY_TARGETS wslay_shared) endif() foreach(target ${WSLAY_TARGETS}) set_property(TARGET ${target} PROPERTY C_STANDARD 99) target_include_directories(${target} PUBLIC $ $ $) target_compile_definitions(${target} PRIVATE "HAVE_CONFIG_H") endforeach() # install if(WSLAY_CONFIGURE_INSTALL) include(GNUInstallDirs) install(TARGETS ${WSLAY_TARGETS} EXPORT wslay INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(DIRECTORY ${INCLUDE_DIR}/ ${GEN_INCLUDE_DIR}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h") endif() wslay-release-1.1.1/lib/Makefile.am000066400000000000000000000031471367333100700171010ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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 = @DEFS@ -I$(srcdir)/includes -I$(builddir)/includes pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libwslay.pc DISTCLEANFILES = $(pkgconfig_DATA) lib_LTLIBRARIES = libwslay.la OBJECTS = wslay_frame.c wslay_event.c\ wslay_queue.c wslay_net.c HFILES = wslay_frame.h wslay_event.h\ wslay_queue.h wslay_net.h libwslay_la_SOURCES = $(HFILES) $(OBJECTS) libwslay_la_LDFLAGS = -no-undefined \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) wslay-release-1.1.1/lib/config.h.in000066400000000000000000000003631367333100700170650ustar00rootroot00000000000000/* This configuration file is used only by CMake build system. */ #ifndef CONFIG_H #define CONFIG_H #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_WINSOCK2_H #cmakedefine WORDS_BIGENDIAN #endif /* CONFIG_H */ wslay-release-1.1.1/lib/includes/000077500000000000000000000000001367333100700166465ustar00rootroot00000000000000wslay-release-1.1.1/lib/includes/Makefile.am000066400000000000000000000022501367333100700207010ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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 = wslay/wslay.h wslay/wslayver.h wslay-release-1.1.1/lib/includes/wslay/000077500000000000000000000000001367333100700200055ustar00rootroot00000000000000wslay-release-1.1.1/lib/includes/wslay/wslay.h000066400000000000000000000653701367333100700213300ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_H #define WSLAY_H #ifdef __cplusplus extern "C" { #endif #include #include #include /* * wslay/wslayver.h is generated from wslay/wslayver.h.in by * configure. The projects which do not use autotools can set * WSLAY_VERSION macro from outside to avoid to generating wslayver.h */ #ifndef WSLAY_VERSION # include #endif /* WSLAY_VERSION */ enum wslay_error { WSLAY_ERR_WANT_READ = -100, WSLAY_ERR_WANT_WRITE = -101, WSLAY_ERR_PROTO = -200, WSLAY_ERR_INVALID_ARGUMENT = -300, WSLAY_ERR_INVALID_CALLBACK = -301, WSLAY_ERR_NO_MORE_MSG = -302, WSLAY_ERR_CALLBACK_FAILURE = -400, WSLAY_ERR_WOULDBLOCK = -401, WSLAY_ERR_NOMEM = -500 }; /* * Status codes defined in RFC6455 */ enum wslay_status_code { WSLAY_CODE_NORMAL_CLOSURE = 1000, WSLAY_CODE_GOING_AWAY = 1001, WSLAY_CODE_PROTOCOL_ERROR = 1002, WSLAY_CODE_UNSUPPORTED_DATA = 1003, WSLAY_CODE_NO_STATUS_RCVD = 1005, WSLAY_CODE_ABNORMAL_CLOSURE = 1006, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA = 1007, WSLAY_CODE_POLICY_VIOLATION = 1008, WSLAY_CODE_MESSAGE_TOO_BIG = 1009, WSLAY_CODE_MANDATORY_EXT = 1010, WSLAY_CODE_INTERNAL_SERVER_ERROR = 1011, WSLAY_CODE_TLS_HANDSHAKE = 1015 }; enum wslay_io_flags { /* * There is more data to send. */ WSLAY_MSG_MORE = 1 }; /* * Callback function used by wslay_frame_send() function when it needs * to send data. The implementation of this function must send at most * len bytes of data in data. flags is the bitwise OR of zero or more * of the following flag: * * WSLAY_MSG_MORE * There is more data to send * * It provides some hints to tune performance and behaviour. user_data * is one given in wslay_frame_context_init() function. The * implementation of this function must return the number of bytes * sent. If there is an error, return -1. The return value 0 is also * treated an error by the library. */ typedef ssize_t (*wslay_frame_send_callback)(const uint8_t *data, size_t len, int flags, void *user_data); /* * Callback function used by wslay_frame_recv() function when it needs * more data. The implementation of this function must fill at most * len bytes of data into buf. The memory area of buf is allocated by * library and not be freed by the application code. flags is always 0 * in this version. user_data is one given in * wslay_frame_context_init() function. The implementation of this * function must return the number of bytes filled. If there is an * error, return -1. The return value 0 is also treated an error by * the library. */ typedef ssize_t (*wslay_frame_recv_callback)(uint8_t *buf, size_t len, int flags, void *user_data); /* * Callback function used by wslay_frame_send() function when it needs * new mask key. The implementation of this function must write * exactly len bytes of mask key to buf. user_data is one given in * wslay_frame_context_init() function. The implementation of this * function return 0 on success. If there is an error, return -1. */ typedef int (*wslay_frame_genmask_callback)(uint8_t *buf, size_t len, void *user_data); struct wslay_frame_callbacks { wslay_frame_send_callback send_callback; wslay_frame_recv_callback recv_callback; wslay_frame_genmask_callback genmask_callback; }; /* * The opcode defined in RFC6455. */ enum wslay_opcode { WSLAY_CONTINUATION_FRAME = 0x0u, WSLAY_TEXT_FRAME = 0x1u, WSLAY_BINARY_FRAME = 0x2u, WSLAY_CONNECTION_CLOSE = 0x8u, WSLAY_PING = 0x9u, WSLAY_PONG = 0xau }; /* * Macro that returns 1 if opcode is control frame opcode, otherwise * returns 0. */ #define wslay_is_ctrl_frame(opcode) ((opcode >> 3) & 1) /* * Macros that represent and return reserved bits: RSV1, RSV2, RSV3. * These macros assume that rsv is constructed by ((RSV1 << 2) | * (RSV2 << 1) | RSV3) */ #define WSLAY_RSV_NONE ((uint8_t) 0) #define WSLAY_RSV1_BIT (((uint8_t) 1) << 2) #define WSLAY_RSV2_BIT (((uint8_t) 1) << 1) #define WSLAY_RSV3_BIT (((uint8_t) 1) << 0) #define wslay_get_rsv1(rsv) ((rsv >> 2) & 1) #define wslay_get_rsv2(rsv) ((rsv >> 1) & 1) #define wslay_get_rsv3(rsv) (rsv & 1) struct wslay_frame_iocb { /* 1 for fragmented final frame, 0 for otherwise */ uint8_t fin; /* * reserved 3 bits. rsv = ((RSV1 << 2) | (RSV << 1) | RSV3). * RFC6455 requires 0 unless extensions are negotiated. */ uint8_t rsv; /* 4 bit opcode */ uint8_t opcode; /* payload length [0, 2**63-1] */ uint64_t payload_length; /* 1 for masked frame, 0 for unmasked */ uint8_t mask; /* part of payload data */ const uint8_t *data; /* bytes of data defined above */ size_t data_length; }; struct wslay_frame_context; typedef struct wslay_frame_context *wslay_frame_context_ptr; /* * Initializes ctx using given callbacks and user_data. This function * allocates memory for struct wslay_frame_context and stores the * result to *ctx. The callback functions specified in callbacks are * copied to ctx. user_data is stored in ctx and it will be passed to * callback functions. When the user code finished using ctx, it must * call wslay_frame_context_free to deallocate memory. */ int wslay_frame_context_init(wslay_frame_context_ptr *ctx, const struct wslay_frame_callbacks *callbacks, void *user_data); /* * Deallocates memory pointed by ctx. */ void wslay_frame_context_free(wslay_frame_context_ptr ctx); /* * Send WebSocket frame specified in iocb. ctx must be initialized * using wslay_frame_context_init() function. iocb->fin must be 1 if * this is a fin frame, otherwise 0. iocb->rsv is reserved bits. * iocb->opcode must be the opcode of this frame. iocb->mask must be * 1 if this is masked frame, otherwise 0. iocb->payload_length is * the payload_length of this frame. iocb->data must point to the * payload data to be sent. iocb->data_length must be the length of * the data. This function calls send_callback function if it needs * to send bytes. This function calls gen_mask_callback function if * it needs new mask key. This function returns the number of payload * bytes sent. Please note that it does not include any number of * header bytes. If it cannot send any single bytes of payload, it * returns WSLAY_ERR_WANT_WRITE. If the library detects error in iocb, * this function returns WSLAY_ERR_INVALID_ARGUMENT. If callback * functions report a failure, this function returns * WSLAY_ERR_INVALID_CALLBACK. This function does not always send all * given data in iocb. If there are remaining data to be sent, adjust * data and data_length in iocb accordingly and call this function * again. */ ssize_t wslay_frame_send(wslay_frame_context_ptr ctx, struct wslay_frame_iocb *iocb); /* * Receives WebSocket frame and stores it in iocb. This function * returns the number of payload bytes received. This does not * include header bytes. In this case, iocb will be populated as * follows: iocb->fin is 1 if received frame is fin frame, otherwise * 0. iocb->rsv is reserved bits of received frame. iocb->opcode is * opcode of received frame. iocb->mask is 1 if received frame is * masked, otherwise 0. iocb->payload_length is the payload length of * received frame. iocb->data is pointed to the buffer containing * received payload data. This buffer is allocated by the library and * must be read-only. iocb->data_length is the number of payload * bytes recieved. This function calls recv_callback if it needs to * receive additional bytes. If it cannot receive any single bytes of * payload, it returns WSLAY_ERR_WANT_READ. If the library detects * protocol violation in a received frame, this function returns * WSLAY_ERR_PROTO. If callback functions report a failure, this * function returns WSLAY_ERR_INVALID_CALLBACK. This function does * not always receive whole frame in a single call. If there are * remaining data to be received, call this function again. This * function ensures frame alignment. */ ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx, struct wslay_frame_iocb *iocb); struct wslay_event_context; /* Pointer to the event-based API context */ typedef struct wslay_event_context *wslay_event_context_ptr; struct wslay_event_on_msg_recv_arg { /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */ uint8_t rsv; /* opcode */ uint8_t opcode; /* received message */ const uint8_t *msg; /* message length */ size_t msg_length; /* * Status code iff opcode == WSLAY_CONNECTION_CLOSE. If no status * code is included in the close control frame, it is set to 0. */ uint16_t status_code; }; /* * Callback function invoked by wslay_event_recv() when a message is * completely received. */ typedef void (*wslay_event_on_msg_recv_callback) (wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data); struct wslay_event_on_frame_recv_start_arg { /* fin bit; 1 for final frame, or 0. */ uint8_t fin; /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */ uint8_t rsv; /* opcode of the frame */ uint8_t opcode; /* payload length of ths frame */ uint64_t payload_length; }; /* * Callback function invoked by wslay_event_recv() when a new frame * starts to be received. This callback function is only invoked once * for each frame. */ typedef void (*wslay_event_on_frame_recv_start_callback) (wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data); struct wslay_event_on_frame_recv_chunk_arg { /* chunk of payload data */ const uint8_t *data; /* length of data */ size_t data_length; }; /* * Callback function invoked by wslay_event_recv() when a chunk of * frame payload is received. */ typedef void (*wslay_event_on_frame_recv_chunk_callback) (wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data); /* * Callback function invoked by wslay_event_recv() when a frame is * completely received. */ typedef void (*wslay_event_on_frame_recv_end_callback) (wslay_event_context_ptr ctx, void *user_data); /* * Callback function invoked by wslay_event_recv() when it wants to * receive more data from peer. The implementation of this callback * function must read data at most len bytes from peer and store them * in buf and return the number of bytes read. flags is always 0 in * this version. * * If there is an error, return -1 and set error code * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay * event-based API on the whole assumes non-blocking I/O. If the cause * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK * instead. This is important because it tells wslay_event_recv() to * stop receiving further data and return. */ typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data); /* * Callback function invoked by wslay_event_send() when it wants to * send more data to peer. The implementation of this callback * function must send data at most len bytes to peer and return the * number of bytes sent. flags is the bitwise OR of zero or more of * the following flag: * * WSLAY_MSG_MORE * There is more data to send * * It provides some hints to tune performance and behaviour. * * If there is an error, return -1 and set error code * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay * event-based API on the whole assumes non-blocking I/O. If the cause * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK * instead. This is important because it tells wslay_event_send() to * stop sending data and return. */ typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data); /* * Callback function invoked by wslay_event_send() when it wants new * mask key. As described in RFC6455, only the traffic from WebSocket * client is masked, so this callback function is only needed if an * event-based API is initialized for WebSocket client use. */ typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data); struct wslay_event_callbacks { wslay_event_recv_callback recv_callback; wslay_event_send_callback send_callback; wslay_event_genmask_callback genmask_callback; wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback; wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback; wslay_event_on_frame_recv_end_callback on_frame_recv_end_callback; wslay_event_on_msg_recv_callback on_msg_recv_callback; }; /* * Initializes ctx as WebSocket Server. user_data is an arbitrary * pointer, which is directly passed to each callback functions as * user_data argument. * * On success, returns 0. On error, returns one of following negative * values: * * WSLAY_ERR_NOMEM * Out of memory. */ int wslay_event_context_server_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data); /* * Initializes ctx as WebSocket client. user_data is an arbitrary * pointer, which is directly passed to each callback functions as * user_data argument. * * On success, returns 0. On error, returns one of following negative * values: * * WSLAY_ERR_NOMEM * Out of memory. */ int wslay_event_context_client_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data); /* * Releases allocated resources for ctx. */ void wslay_event_context_free(wslay_event_context_ptr ctx); /* * Sets a bit mask of allowed reserved bits. * Currently only permitted values are WSLAY_RSV1_BIT to allow PMCE * extension (see RFC-7692) or WSLAY_RSV_NONE to disable. * * Default: WSLAY_RSV_NONE */ void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv); /* * Enables or disables buffering of an entire message for non-control * frames. If val is 0, buffering is enabled. Otherwise, buffering is * disabled. If wslay_event_on_msg_recv_callback is invoked when * buffering is disabled, the msg_length member of struct * wslay_event_on_msg_recv_arg is set to 0. * * The control frames are always buffered regardless of this function call. * * This function must not be used after the first invocation of * wslay_event_recv() function. */ void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val); /* * Sets maximum length of a message that can be received. The length * of message is checked by wslay_event_recv() function. If the length * of a message is larger than this value, reading operation is * disabled (same effect with wslay_event_shutdown_read() call) and * close control frame with WSLAY_CODE_MESSAGE_TOO_BIG is queued. If * buffering for non-control frames is disabled, the library checks * each frame payload length and does not check length of entire * message. * * The default value is (1u << 31)-1. */ void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx, uint64_t val); /* * Sets callbacks to ctx. The callbacks previouly set by this function * or wslay_event_context_server_init() or * wslay_event_context_client_init() are replaced with callbacks. */ void wslay_event_config_set_callbacks (wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks); /* * Receives messages from peer. When receiving * messages, it uses wslay_event_recv_callback function. Single call * of this function receives multiple messages until * wslay_event_recv_callback function sets error code * WSLAY_ERR_WOULDBLOCK. * * When close control frame is received, this function automatically * queues close control frame. Also this function calls * wslay_event_set_read_enabled() with second argument 0 to disable * further read from peer. * * When ping control frame is received, this function automatically * queues pong control frame. * * In case of a fatal errror which leads to negative return code, this * function calls wslay_event_set_read_enabled() with second argument * 0 to disable further read from peer. * * wslay_event_recv() returns 0 if it succeeds, or one of the * following negative error codes: * * WSLAY_ERR_CALLBACK_FAILURE * User defined callback function is failed. * * WSLAY_ERR_NOMEM * Out of memory. * * When negative error code is returned, application must not make any * further call of wslay_event_recv() and must close WebSocket * connection. */ int wslay_event_recv(wslay_event_context_ptr ctx); /* * Sends queued messages to peer. When sending a * message, it uses wslay_event_send_callback function. Single call of * wslay_event_send() sends multiple messages until * wslay_event_send_callback sets error code WSLAY_ERR_WOULDBLOCK. * * If ctx is initialized for WebSocket client use, wslay_event_send() * uses wslay_event_genmask_callback to get new mask key. * * When a message queued using wslay_event_queue_fragmented_msg() is * sent, wslay_event_send() invokes * wslay_event_fragmented_msg_callback for that message. * * After close control frame is sent, this function calls * wslay_event_set_write_enabled() with second argument 0 to disable * further transmission to peer. * * If there are any pending messages, wslay_event_want_write() returns * 1, otherwise returns 0. * * In case of a fatal errror which leads to negative return code, this * function calls wslay_event_set_write_enabled() with second argument * 0 to disable further transmission to peer. * * wslay_event_send() returns 0 if it succeeds, or one of the * following negative error codes: * * WSLAY_ERR_CALLBACK_FAILURE * User defined callback function is failed. * * WSLAY_ERR_NOMEM * Out of memory. * * When negative error code is returned, application must not make any * further call of wslay_event_send() and must close WebSocket * connection. */ int wslay_event_send(wslay_event_context_ptr ctx); struct wslay_event_msg { uint8_t opcode; const uint8_t *msg; size_t msg_length; }; /* * Queues message specified in arg. * * This function supports both control and non-control messages and * the given message is sent without fragmentation. If fragmentation * is needed, use wslay_event_queue_fragmented_msg() function instead. * * This function just queues a message and does not send * it. wslay_event_send() function call sends these queued messages. * * wslay_event_queue_msg() returns 0 if it succeeds, or returns the * following negative error codes: * * WSLAY_ERR_NO_MORE_MSG * Could not queue given message. The one of possible reason is that * close control frame has been queued/sent and no further queueing * message is not allowed. * * WSLAY_ERR_INVALID_ARGUMENT * The given message is invalid. * * WSLAY_ERR_NOMEM * Out of memory. */ int wslay_event_queue_msg(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg); /* * Extended version of wslay_event_queue_msg which allows to set reserved bits. */ int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg, uint8_t rsv); /* * Specify "source" to generate message. */ union wslay_event_msg_source { int fd; void *data; }; /* * Callback function called by wslay_event_send() to read message data * from source. The implementation of * wslay_event_fragmented_msg_callback must store at most len bytes of * data to buf and return the number of stored bytes. If all data is * read (i.e., EOF), set *eof to 1. If no data can be generated at the * moment, return 0. If there is an error, return -1 and set error * code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). */ typedef ssize_t (*wslay_event_fragmented_msg_callback) (wslay_event_context_ptr ctx, uint8_t *buf, size_t len, const union wslay_event_msg_source *source, int *eof, void *user_data); struct wslay_event_fragmented_msg { /* opcode */ uint8_t opcode; /* "source" to generate message data */ union wslay_event_msg_source source; /* Callback function to read message data from source. */ wslay_event_fragmented_msg_callback read_callback; }; /* * Queues a fragmented message specified in arg. * * This function supports non-control messages only. For control frames, * use wslay_event_queue_msg() or wslay_event_queue_close(). * * This function just queues a message and does not send * it. wslay_event_send() function call sends these queued messages. * * wslay_event_queue_fragmented_msg() returns 0 if it succeeds, or * returns the following negative error codes: * * WSLAY_ERR_NO_MORE_MSG * Could not queue given message. The one of possible reason is that * close control frame has been queued/sent and no further queueing * message is not allowed. * * WSLAY_ERR_INVALID_ARGUMENT * The given message is invalid. * * WSLAY_ERR_NOMEM * Out of memory. */ int wslay_event_queue_fragmented_msg (wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg); /* * Extended version of wslay_event_queue_fragmented_msg which allows to set * reserved bits. */ int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg, uint8_t rsv); /* * Queues close control frame. This function is provided just for * convenience. wslay_event_queue_msg() can queue a close control * frame as well. status_code is the status code of close control * frame. reason is the close reason encoded in UTF-8. reason_length * is the length of reason in bytes. reason_length must be less than * 123 bytes. * * If status_code is 0, reason and reason_length is not used and close * control frame with zero-length payload will be queued. * * This function just queues a message and does not send * it. wslay_event_send() function call sends these queued messages. * * wslay_event_queue_close() returns 0 if it succeeds, or returns the * following negative error codes: * * WSLAY_ERR_NO_MORE_MSG * Could not queue given message. The one of possible reason is that * close control frame has been queued/sent and no further queueing * message is not allowed. * * WSLAY_ERR_INVALID_ARGUMENT * The given message is invalid. * * WSLAY_ERR_NOMEM * Out of memory. */ int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code, const uint8_t *reason, size_t reason_length); /* * Sets error code to tell the library there is an error. This * function is typically used in user defined callback functions. See * the description of callback function to know which error code * should be used. */ void wslay_event_set_error(wslay_event_context_ptr ctx, int val); /* * Query whehter the library want to read more data from peer. * * wslay_event_want_read() returns 1 if the library want to read more * data from peer, or returns 0. */ int wslay_event_want_read(wslay_event_context_ptr ctx); /* * Query whehter the library want to send more data to peer. * * wslay_event_want_write() returns 1 if the library want to send more * data to peer, or returns 0. */ int wslay_event_want_write(wslay_event_context_ptr ctx); /* * Prevents the event-based API context from reading any further data * from peer. * * This function may be used with wslay_event_queue_close() if the * application detects error in the data received and wants to fail * WebSocket connection. */ void wslay_event_shutdown_read(wslay_event_context_ptr ctx); /* * Prevents the event-based API context from sending any further data * to peer. */ void wslay_event_shutdown_write(wslay_event_context_ptr ctx); /* * Returns 1 if the event-based API context allows read operation, or * return 0. * * After wslay_event_shutdown_read() is called, * wslay_event_get_read_enabled() returns 0. */ int wslay_event_get_read_enabled(wslay_event_context_ptr ctx); /* * Returns 1 if the event-based API context allows write operation, or * return 0. * * After wslay_event_shutdown_write() is called, * wslay_event_get_write_enabled() returns 0. */ int wslay_event_get_write_enabled(wslay_event_context_ptr ctx); /* * Returns 1 if a close control frame has been received from peer, or * returns 0. */ int wslay_event_get_close_received(wslay_event_context_ptr ctx); /* * Returns 1 if a close control frame has been sent to peer, or * returns 0. */ int wslay_event_get_close_sent(wslay_event_context_ptr ctx); /* * Returns status code received in close control frame. If no close * control frame has not been received, returns * WSLAY_CODE_ABNORMAL_CLOSURE. If received close control frame has no * status code, returns WSLAY_CODE_NO_STATUS_RCVD. */ uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx); /* * Returns status code sent in close control frame. If no close * control frame has not been sent, returns * WSLAY_CODE_ABNORMAL_CLOSURE. If sent close control frame has no * status code, returns WSLAY_CODE_NO_STATUS_RCVD. */ uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx); /* * Returns the number of queued messages. */ size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx); /* * Returns the sum of queued message length. It only counts the * message length queued using wslay_event_queue_msg() or * wslay_event_queue_close(). */ size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx); #ifdef __cplusplus } #endif #endif /* WSLAY_H */ wslay-release-1.1.1/lib/includes/wslay/wslayver.h.in000066400000000000000000000024411367333100700224400ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAYVER_H #define WSLAYVER_H /* Version number of wslay release */ #define WSLAY_VERSION "@PACKAGE_VERSION@" #endif /* WSLAYVER_H */ wslay-release-1.1.1/lib/libwslay.pc.in000066400000000000000000000025411367333100700176210ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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: Wslay Description: Low-level WebSockets library URL: http://wslay.sourceforge.net/ Version: @VERSION@ Libs: -L${libdir} -lwslay Cflags: -I${includedir} wslay-release-1.1.1/lib/wslay_event.c000066400000000000000000000772541367333100700175630ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_event.h" #include #include #include #include "wslay_queue.h" #include "wslay_frame.h" #include "wslay_net.h" /* Start of utf8 dfa */ /* Copyright (c) 2008-2010 Bjoern Hoehrmann * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. * * Copyright (c) 2008-2009 Bjoern Hoehrmann * * 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. */ #define UTF8_ACCEPT 0 #define UTF8_REJECT 12 static const uint8_t utf8d[] = { /* * The first part of the table maps bytes to character classes that * to reduce the size of the transition table and create bitmasks. */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, /* * The second part is a transition table that maps a combination * of a state of the automaton and a character class to a state. */ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,12,12,12,12,12, }; static uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) { uint32_t type = utf8d[byte]; *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte); *state = utf8d[256 + *state + type]; return *state; } /* End of utf8 dfa */ static ssize_t wslay_event_frame_recv_callback(uint8_t *buf, size_t len, int flags, void *user_data) { struct wslay_event_frame_user_data *e = (struct wslay_event_frame_user_data*)user_data; return e->ctx->callbacks.recv_callback(e->ctx, buf, len, flags, e->user_data); } static ssize_t wslay_event_frame_send_callback(const uint8_t *data, size_t len, int flags, void *user_data) { struct wslay_event_frame_user_data *e = (struct wslay_event_frame_user_data*)user_data; return e->ctx->callbacks.send_callback(e->ctx, data, len, flags, e->user_data); } static int wslay_event_frame_genmask_callback(uint8_t *buf, size_t len, void *user_data) { struct wslay_event_frame_user_data *e = (struct wslay_event_frame_user_data*)user_data; return e->ctx->callbacks.genmask_callback(e->ctx, buf, len, e->user_data); } static int wslay_event_byte_chunk_init (struct wslay_event_byte_chunk **chunk, size_t len) { *chunk = (struct wslay_event_byte_chunk*)malloc (sizeof(struct wslay_event_byte_chunk)); if(*chunk == NULL) { return WSLAY_ERR_NOMEM; } memset(*chunk, 0, sizeof(struct wslay_event_byte_chunk)); if(len) { (*chunk)->data = (uint8_t*)malloc(len); if((*chunk)->data == NULL) { free(*chunk); return WSLAY_ERR_NOMEM; } (*chunk)->data_length = len; } return 0; } static void wslay_event_byte_chunk_free(struct wslay_event_byte_chunk *c) { if(!c) { return; } free(c->data); free(c); } static void wslay_event_byte_chunk_copy(struct wslay_event_byte_chunk *c, size_t off, const uint8_t *data, size_t data_length) { memcpy(c->data+off, data, data_length); } static void wslay_event_imsg_set(struct wslay_event_imsg *m, uint8_t fin, uint8_t rsv, uint8_t opcode) { m->fin = fin; m->rsv = rsv; m->opcode = opcode; m->msg_length = 0; } static void wslay_event_imsg_chunks_free(struct wslay_event_imsg *m) { if(!m->chunks) { return; } while(!wslay_queue_empty(m->chunks)) { wslay_event_byte_chunk_free(wslay_queue_top(m->chunks)); wslay_queue_pop(m->chunks); } } static void wslay_event_imsg_reset(struct wslay_event_imsg *m) { m->opcode = 0xffu; m->utf8state = UTF8_ACCEPT; wslay_event_imsg_chunks_free(m); } static int wslay_event_imsg_append_chunk(struct wslay_event_imsg *m, size_t len) { if(len == 0) { return 0; } else { int r; struct wslay_event_byte_chunk *chunk; if((r = wslay_event_byte_chunk_init(&chunk, len)) != 0) { return r; } if((r = wslay_queue_push(m->chunks, chunk)) != 0) { return r; } m->msg_length += len; return 0; } } static int wslay_event_omsg_non_fragmented_init (struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv, const uint8_t *msg, size_t msg_length) { *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg)); if(!*m) { return WSLAY_ERR_NOMEM; } memset(*m, 0, sizeof(struct wslay_event_omsg)); (*m)->fin = 1; (*m)->opcode = opcode; (*m)->rsv = rsv; (*m)->type = WSLAY_NON_FRAGMENTED; if(msg_length) { (*m)->data = (uint8_t*)malloc(msg_length); if(!(*m)->data) { free(*m); return WSLAY_ERR_NOMEM; } memcpy((*m)->data, msg, msg_length); (*m)->data_length = msg_length; } return 0; } static int wslay_event_omsg_fragmented_init (struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv, const union wslay_event_msg_source source, wslay_event_fragmented_msg_callback read_callback) { *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg)); if(!*m) { return WSLAY_ERR_NOMEM; } memset(*m, 0, sizeof(struct wslay_event_omsg)); (*m)->opcode = opcode; (*m)->rsv = rsv; (*m)->type = WSLAY_FRAGMENTED; (*m)->source = source; (*m)->read_callback = read_callback; return 0; } static void wslay_event_omsg_free(struct wslay_event_omsg *m) { if(!m) { return; } free(m->data); free(m); } static uint8_t* wslay_event_flatten_queue(struct wslay_queue *queue, size_t len) { if(len == 0) { return NULL; } else { size_t off = 0; uint8_t *buf = (uint8_t*)malloc(len); if(!buf) { return NULL; } while(!wslay_queue_empty(queue)) { struct wslay_event_byte_chunk *chunk = wslay_queue_top(queue); memcpy(buf+off, chunk->data, chunk->data_length); off += chunk->data_length; wslay_event_byte_chunk_free(chunk); wslay_queue_pop(queue); assert(off <= len); } assert(len == off); return buf; } } static int wslay_event_is_msg_queueable(wslay_event_context_ptr ctx) { return ctx->write_enabled && (ctx->close_status & WSLAY_CLOSE_QUEUED) == 0; } int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code, const uint8_t *reason, size_t reason_length) { if(!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } else if(reason_length > 123) { return WSLAY_ERR_INVALID_ARGUMENT; } else { uint8_t msg[128]; size_t msg_length; struct wslay_event_msg arg; uint16_t ncode; int r; if(status_code == 0) { msg_length = 0; } else { ncode = htons(status_code); memcpy(msg, &ncode, 2); if(reason_length) { memcpy(msg+2, reason, reason_length); } msg_length = reason_length+2; } arg.opcode = WSLAY_CONNECTION_CLOSE; arg.msg = msg; arg.msg_length = msg_length; r = wslay_event_queue_msg(ctx, &arg); if(r == 0) { ctx->close_status |= WSLAY_CLOSE_QUEUED; } return r; } } static int wslay_event_queue_close_wrapper (wslay_event_context_ptr ctx, uint16_t status_code, const uint8_t *reason, size_t reason_length) { int r; ctx->read_enabled = 0; if((r = wslay_event_queue_close(ctx, status_code, reason, reason_length)) && r != WSLAY_ERR_NO_MORE_MSG) { return r; } return 0; } static int wslay_event_verify_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv) { return ((rsv & ~ctx->allowed_rsv_bits) == 0); } int wslay_event_queue_msg(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg) { return wslay_event_queue_msg_ex(ctx, arg, WSLAY_RSV_NONE); } int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_msg *arg, uint8_t rsv) { int r; struct wslay_event_omsg *omsg; if(!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } /* RSV1 is not allowed for control frames */ if((wslay_is_ctrl_frame(arg->opcode) && (arg->msg_length > 125 || wslay_get_rsv1(rsv))) || !wslay_event_verify_rsv_bits(ctx, rsv)) { return WSLAY_ERR_INVALID_ARGUMENT; } if((r = wslay_event_omsg_non_fragmented_init (&omsg, arg->opcode, rsv, arg->msg, arg->msg_length)) != 0) { return r; } if(wslay_is_ctrl_frame(arg->opcode)) { if((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) { return r; } } else { if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) { return r; } } ++ctx->queued_msg_count; ctx->queued_msg_length += arg->msg_length; return 0; } int wslay_event_queue_fragmented_msg (wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg) { return wslay_event_queue_fragmented_msg_ex(ctx, arg, WSLAY_RSV_NONE); } int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg, uint8_t rsv) { int r; struct wslay_event_omsg *omsg; if(!wslay_event_is_msg_queueable(ctx)) { return WSLAY_ERR_NO_MORE_MSG; } if(wslay_is_ctrl_frame(arg->opcode) || !wslay_event_verify_rsv_bits(ctx, rsv)) { return WSLAY_ERR_INVALID_ARGUMENT; } if((r = wslay_event_omsg_fragmented_init (&omsg, arg->opcode, rsv, arg->source, arg->read_callback)) != 0) { return r; } if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) { return r; } ++ctx->queued_msg_count; return 0; } void wslay_event_config_set_callbacks (wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks) { ctx->callbacks = *callbacks; } static int wslay_event_context_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) { int i, r; struct wslay_frame_callbacks frame_callbacks = { wslay_event_frame_send_callback, wslay_event_frame_recv_callback, wslay_event_frame_genmask_callback }; *ctx = (wslay_event_context_ptr)malloc(sizeof(struct wslay_event_context)); if(!*ctx) { return WSLAY_ERR_NOMEM; } memset(*ctx, 0, sizeof(struct wslay_event_context)); wslay_event_config_set_callbacks(*ctx, callbacks); (*ctx)->user_data = user_data; (*ctx)->frame_user_data.ctx = *ctx; (*ctx)->frame_user_data.user_data = user_data; if((r = wslay_frame_context_init(&(*ctx)->frame_ctx, &frame_callbacks, &(*ctx)->frame_user_data)) != 0) { wslay_event_context_free(*ctx); return r; } (*ctx)->read_enabled = (*ctx)->write_enabled = 1; (*ctx)->send_queue = wslay_queue_new(); if(!(*ctx)->send_queue) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } (*ctx)->send_ctrl_queue = wslay_queue_new(); if(!(*ctx)->send_ctrl_queue) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } (*ctx)->queued_msg_count = 0; (*ctx)->queued_msg_length = 0; for(i = 0; i < 2; ++i) { wslay_event_imsg_reset(&(*ctx)->imsgs[i]); (*ctx)->imsgs[i].chunks = wslay_queue_new(); if(!(*ctx)->imsgs[i].chunks) { wslay_event_context_free(*ctx); return WSLAY_ERR_NOMEM; } } (*ctx)->imsg = &(*ctx)->imsgs[0]; (*ctx)->obufmark = (*ctx)->obuflimit = (*ctx)->obuf; (*ctx)->status_code_sent = WSLAY_CODE_ABNORMAL_CLOSURE; (*ctx)->status_code_recv = WSLAY_CODE_ABNORMAL_CLOSURE; (*ctx)->max_recv_msg_length = (1u << 31)-1; return 0; } int wslay_event_context_server_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) { int r; if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) { return r; } (*ctx)->server = 1; return 0; } int wslay_event_context_client_init (wslay_event_context_ptr *ctx, const struct wslay_event_callbacks *callbacks, void *user_data) { int r; if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) { return r; } (*ctx)->server = 0; return 0; } void wslay_event_context_free(wslay_event_context_ptr ctx) { int i; if(!ctx) { return; } for(i = 0; i < 2; ++i) { wslay_event_imsg_chunks_free(&ctx->imsgs[i]); wslay_queue_free(ctx->imsgs[i].chunks); } if(ctx->send_queue) { while(!wslay_queue_empty(ctx->send_queue)) { wslay_event_omsg_free(wslay_queue_top(ctx->send_queue)); wslay_queue_pop(ctx->send_queue); } wslay_queue_free(ctx->send_queue); } if(ctx->send_ctrl_queue) { while(!wslay_queue_empty(ctx->send_ctrl_queue)) { wslay_event_omsg_free(wslay_queue_top(ctx->send_ctrl_queue)); wslay_queue_pop(ctx->send_ctrl_queue); } wslay_queue_free(ctx->send_ctrl_queue); } wslay_frame_context_free(ctx->frame_ctx); wslay_event_omsg_free(ctx->omsg); free(ctx); } static void wslay_event_call_on_frame_recv_start_callback (wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb) { if(ctx->callbacks.on_frame_recv_start_callback) { struct wslay_event_on_frame_recv_start_arg arg; arg.fin = iocb->fin; arg.rsv = iocb->rsv; arg.opcode = iocb->opcode; arg.payload_length = iocb->payload_length; ctx->callbacks.on_frame_recv_start_callback(ctx, &arg, ctx->user_data); } } static void wslay_event_call_on_frame_recv_chunk_callback (wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb) { if(ctx->callbacks.on_frame_recv_chunk_callback) { struct wslay_event_on_frame_recv_chunk_arg arg; arg.data = iocb->data; arg.data_length = iocb->data_length; ctx->callbacks.on_frame_recv_chunk_callback(ctx, &arg, ctx->user_data); } } static void wslay_event_call_on_frame_recv_end_callback (wslay_event_context_ptr ctx) { if(ctx->callbacks.on_frame_recv_end_callback) { ctx->callbacks.on_frame_recv_end_callback(ctx, ctx->user_data); } } static int wslay_event_is_valid_status_code(uint16_t status_code) { return (1000 <= status_code && status_code <= 1011 && status_code != 1004 && status_code != 1005 && status_code != 1006) || (3000 <= status_code && status_code <= 4999); } static int wslay_event_config_get_no_buffering(wslay_event_context_ptr ctx) { return (ctx->config & WSLAY_CONFIG_NO_BUFFERING) > 0; } int wslay_event_recv(wslay_event_context_ptr ctx) { struct wslay_frame_iocb iocb; ssize_t r; while(ctx->read_enabled) { memset(&iocb, 0, sizeof(iocb)); r = wslay_frame_recv(ctx->frame_ctx, &iocb); if(r >= 0) { int new_frame = 0; /* RSV1 is not allowed on control and continuation frames */ if((!wslay_event_verify_rsv_bits(ctx, iocb.rsv)) || (wslay_get_rsv1(iocb.rsv) && (wslay_is_ctrl_frame(iocb.opcode) || iocb.opcode == WSLAY_CONTINUATION_FRAME)) || (ctx->server && !iocb.mask) || (!ctx->server && iocb.mask)) { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } if(ctx->imsg->opcode == 0xffu) { if(iocb.opcode == WSLAY_TEXT_FRAME || iocb.opcode == WSLAY_BINARY_FRAME || iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); new_frame = 1; } else { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } } else if(ctx->ipayloadlen == 0 && ctx->ipayloadoff == 0) { if(iocb.opcode == WSLAY_CONTINUATION_FRAME) { ctx->imsg->fin = iocb.fin; } else if(iocb.opcode == WSLAY_CONNECTION_CLOSE || iocb.opcode == WSLAY_PING || iocb.opcode == WSLAY_PONG) { ctx->imsg = &ctx->imsgs[1]; wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode); } else { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } new_frame = 1; } if(new_frame) { if(ctx->imsg->msg_length+iocb.payload_length > ctx->max_recv_msg_length) { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_MESSAGE_TOO_BIG, NULL, 0)) != 0) { return r; } break; } ctx->ipayloadlen = iocb.payload_length; wslay_event_call_on_frame_recv_start_callback(ctx, &iocb); if(!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { if((r = wslay_event_imsg_append_chunk(ctx->imsg, iocb.payload_length)) != 0) { ctx->read_enabled = 0; return r; } } } /* If RSV1 bit is set then it is too early for utf-8 validation */ if((!wslay_get_rsv1(ctx->imsg->rsv) && ctx->imsg->opcode == WSLAY_TEXT_FRAME) || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { size_t i; if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { i = 2; } else { i = 0; } for(; i < iocb.data_length; ++i) { uint32_t codep; if(decode(&ctx->imsg->utf8state, &codep, iocb.data[i]) == UTF8_REJECT) { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } } } if(ctx->imsg->utf8state == UTF8_REJECT) { break; } wslay_event_call_on_frame_recv_chunk_callback(ctx, &iocb); if(iocb.data_length > 0) { if(!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { struct wslay_event_byte_chunk *chunk; chunk = wslay_queue_tail(ctx->imsg->chunks); wslay_event_byte_chunk_copy(chunk, ctx->ipayloadoff, iocb.data, iocb.data_length); } ctx->ipayloadoff += iocb.data_length; } if(ctx->ipayloadoff == ctx->ipayloadlen) { if(ctx->imsg->fin && (ctx->imsg->opcode == WSLAY_TEXT_FRAME || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) && ctx->imsg->utf8state != UTF8_ACCEPT) { if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) { return r; } break; } wslay_event_call_on_frame_recv_end_callback(ctx); if(ctx->imsg->fin) { if(ctx->callbacks.on_msg_recv_callback || ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE || ctx->imsg->opcode == WSLAY_PING) { struct wslay_event_on_msg_recv_arg arg; uint16_t status_code = 0; uint8_t *msg = NULL; size_t msg_length = 0; if(!wslay_event_config_get_no_buffering(ctx) || wslay_is_ctrl_frame(iocb.opcode)) { msg = wslay_event_flatten_queue(ctx->imsg->chunks, ctx->imsg->msg_length); if(ctx->imsg->msg_length && !msg) { ctx->read_enabled = 0; return WSLAY_ERR_NOMEM; } msg_length = ctx->imsg->msg_length; } if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) { const uint8_t *reason; size_t reason_length; if(ctx->imsg->msg_length >= 2) { memcpy(&status_code, msg, 2); status_code = ntohs(status_code); if(!wslay_event_is_valid_status_code(status_code)) { free(msg); if((r = wslay_event_queue_close_wrapper (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) { return r; } break; } reason = msg+2; reason_length = ctx->imsg->msg_length-2; } else { reason = NULL; reason_length = 0; } ctx->close_status |= WSLAY_CLOSE_RECEIVED; ctx->status_code_recv = status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code; if((r = wslay_event_queue_close_wrapper (ctx, status_code, reason, reason_length)) != 0) { free(msg); return r; } } else if(ctx->imsg->opcode == WSLAY_PING) { struct wslay_event_msg pong_arg; pong_arg.opcode = WSLAY_PONG; pong_arg.msg = msg; pong_arg.msg_length = ctx->imsg->msg_length; if((r = wslay_event_queue_msg(ctx, &pong_arg)) && r != WSLAY_ERR_NO_MORE_MSG) { ctx->read_enabled = 0; free(msg); return r; } } if(ctx->callbacks.on_msg_recv_callback) { arg.rsv = ctx->imsg->rsv; arg.opcode = ctx->imsg->opcode; arg.msg = msg; arg.msg_length = msg_length; arg.status_code = status_code; ctx->error = 0; ctx->callbacks.on_msg_recv_callback(ctx, &arg, ctx->user_data); } free(msg); } wslay_event_imsg_reset(ctx->imsg); if(ctx->imsg == &ctx->imsgs[1]) { ctx->imsg = &ctx->imsgs[0]; } } ctx->ipayloadlen = ctx->ipayloadoff = 0; } } else { if(r != WSLAY_ERR_WANT_READ || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { if((r = wslay_event_queue_close_wrapper(ctx, 0, NULL, 0)) != 0) { return r; } return WSLAY_ERR_CALLBACK_FAILURE; } break; } } return 0; } static void wslay_event_on_non_fragmented_msg_popped (wslay_event_context_ptr ctx) { ctx->omsg->fin = 1; ctx->opayloadlen = ctx->omsg->data_length; ctx->opayloadoff = 0; } static struct wslay_event_omsg* wslay_event_send_ctrl_queue_pop (wslay_event_context_ptr ctx) { /* * If Close control frame is queued, we don't send any control frame * other than Close. */ if(ctx->close_status & WSLAY_CLOSE_QUEUED) { while(!wslay_queue_empty(ctx->send_ctrl_queue)) { struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue); wslay_queue_pop(ctx->send_ctrl_queue); if(msg->opcode == WSLAY_CONNECTION_CLOSE) { return msg; } else { wslay_event_omsg_free(msg); } } return NULL; } else { struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue); wslay_queue_pop(ctx->send_ctrl_queue); return msg; } } int wslay_event_send(wslay_event_context_ptr ctx) { struct wslay_frame_iocb iocb; ssize_t r; while(ctx->write_enabled && (!wslay_queue_empty(ctx->send_queue) || !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg)) { if(!ctx->omsg) { if(wslay_queue_empty(ctx->send_ctrl_queue)) { ctx->omsg = wslay_queue_top(ctx->send_queue); wslay_queue_pop(ctx->send_queue); } else { ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx); if(ctx->omsg == NULL) { break; } } if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) { wslay_event_on_non_fragmented_msg_popped(ctx); } } else if(!wslay_is_ctrl_frame(ctx->omsg->opcode) && ctx->frame_ctx->ostate == PREP_HEADER && !wslay_queue_empty(ctx->send_ctrl_queue)) { if((r = wslay_queue_push_front(ctx->send_queue, ctx->omsg)) != 0) { ctx->write_enabled = 0; return r; } ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx); if(ctx->omsg == NULL) { break; } /* ctrl message has WSLAY_NON_FRAGMENTED */ wslay_event_on_non_fragmented_msg_popped(ctx); } if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) { memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = ctx->omsg->opcode; iocb.rsv = ctx->omsg->rsv; iocb.mask = ctx->server^1; iocb.data = ctx->omsg->data+ctx->opayloadoff; iocb.data_length = ctx->opayloadlen-ctx->opayloadoff; iocb.payload_length = ctx->opayloadlen; r = wslay_frame_send(ctx->frame_ctx, &iocb); if(r >= 0) { ctx->opayloadoff += r; if(ctx->opayloadoff == ctx->opayloadlen) { --ctx->queued_msg_count; ctx->queued_msg_length -= ctx->omsg->data_length; if(ctx->omsg->opcode == WSLAY_CONNECTION_CLOSE) { uint16_t status_code = 0; ctx->write_enabled = 0; ctx->close_status |= WSLAY_CLOSE_SENT; if(ctx->omsg->data_length >= 2) { memcpy(&status_code, ctx->omsg->data, 2); status_code = ntohs(status_code); } ctx->status_code_sent = status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code; } wslay_event_omsg_free(ctx->omsg); ctx->omsg = NULL; } else { break; } } else { if(r != WSLAY_ERR_WANT_WRITE || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } break; } } else { if(ctx->omsg->fin == 0 && ctx->obuflimit == ctx->obufmark) { int eof = 0; r = ctx->omsg->read_callback(ctx, ctx->obuf, sizeof(ctx->obuf), &ctx->omsg->source, &eof, ctx->user_data); if(r == 0 && eof == 0) { break; } else if(r < 0) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } ctx->obuflimit = ctx->obuf+r; if(eof) { ctx->omsg->fin = 1; } ctx->opayloadlen = r; ctx->opayloadoff = 0; } memset(&iocb, 0, sizeof(iocb)); iocb.fin = ctx->omsg->fin; iocb.opcode = ctx->omsg->opcode; iocb.rsv = ctx->omsg->rsv; iocb.mask = ctx->server ? 0 : 1; iocb.data = ctx->obufmark; iocb.data_length = ctx->obuflimit-ctx->obufmark; iocb.payload_length = ctx->opayloadlen; r = wslay_frame_send(ctx->frame_ctx, &iocb); if(r >= 0) { ctx->obufmark += r; if(ctx->obufmark == ctx->obuflimit) { ctx->obufmark = ctx->obuflimit = ctx->obuf; if(ctx->omsg->fin) { --ctx->queued_msg_count; wslay_event_omsg_free(ctx->omsg); ctx->omsg = NULL; } else { ctx->omsg->opcode = WSLAY_CONTINUATION_FRAME; /* RSV1 is not set on continuation frames */ ctx->omsg->rsv = ctx->omsg->rsv & ~WSLAY_RSV1_BIT; } } else { break; } } else { if(r != WSLAY_ERR_WANT_WRITE || (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) { ctx->write_enabled = 0; return WSLAY_ERR_CALLBACK_FAILURE; } break; } } } return 0; } void wslay_event_set_error(wslay_event_context_ptr ctx, int val) { ctx->error = val; } int wslay_event_want_read(wslay_event_context_ptr ctx) { return ctx->read_enabled; } int wslay_event_want_write(wslay_event_context_ptr ctx) { return ctx->write_enabled && (!wslay_queue_empty(ctx->send_queue) || !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg); } void wslay_event_shutdown_read(wslay_event_context_ptr ctx) { ctx->read_enabled = 0; } void wslay_event_shutdown_write(wslay_event_context_ptr ctx) { ctx->write_enabled = 0; } int wslay_event_get_read_enabled(wslay_event_context_ptr ctx) { return ctx->read_enabled; } int wslay_event_get_write_enabled(wslay_event_context_ptr ctx) { return ctx->write_enabled; } int wslay_event_get_close_received(wslay_event_context_ptr ctx) { return (ctx->close_status & WSLAY_CLOSE_RECEIVED) > 0; } int wslay_event_get_close_sent(wslay_event_context_ptr ctx) { return (ctx->close_status & WSLAY_CLOSE_SENT) > 0; } void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv) { /* We currently only allow WSLAY_RSV1_BIT or WSLAY_RSV_NONE */ ctx->allowed_rsv_bits = rsv & WSLAY_RSV1_BIT; } void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val) { if(val) { ctx->config |= WSLAY_CONFIG_NO_BUFFERING; } else { ctx->config &= ~WSLAY_CONFIG_NO_BUFFERING; } } void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx, uint64_t val) { ctx->max_recv_msg_length = val; } uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx) { return ctx->status_code_recv; } uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx) { return ctx->status_code_sent; } size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx) { return ctx->queued_msg_count; } size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx) { return ctx->queued_msg_length; } wslay-release-1.1.1/lib/wslay_event.h000066400000000000000000000105761367333100700175620ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_EVENT_H #define WSLAY_EVENT_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include struct wslay_stack; struct wslay_queue; struct wslay_event_byte_chunk { uint8_t *data; size_t data_length; }; struct wslay_event_imsg { uint8_t fin; uint8_t rsv; uint8_t opcode; uint32_t utf8state; struct wslay_queue *chunks; size_t msg_length; }; enum wslay_event_msg_type { WSLAY_NON_FRAGMENTED, WSLAY_FRAGMENTED }; struct wslay_event_omsg { uint8_t fin; uint8_t opcode; uint8_t rsv; enum wslay_event_msg_type type; uint8_t *data; size_t data_length; union wslay_event_msg_source source; wslay_event_fragmented_msg_callback read_callback; }; struct wslay_event_frame_user_data { wslay_event_context_ptr ctx; void *user_data; }; enum wslay_event_close_status { WSLAY_CLOSE_RECEIVED = 1 << 0, WSLAY_CLOSE_QUEUED = 1 << 1, WSLAY_CLOSE_SENT = 1 << 2 }; enum wslay_event_config { WSLAY_CONFIG_NO_BUFFERING = 1 << 0 }; struct wslay_event_context { /* config status, bitwise OR of enum wslay_event_config values*/ uint32_t config; /* maximum message length that can be received */ uint64_t max_recv_msg_length; /* 1 if initialized for server, otherwise 0 */ uint8_t server; /* bitwise OR of enum wslay_event_close_status values */ uint8_t close_status; /* status code in received close control frame */ uint16_t status_code_recv; /* status code in sent close control frame */ uint16_t status_code_sent; wslay_frame_context_ptr frame_ctx; /* 1 if reading is enabled, otherwise 0. Upon receiving close control frame this value set to 0. If any errors in read operation will also set this value to 0. */ uint8_t read_enabled; /* 1 if writing is enabled, otherwise 0 Upon completing sending close control frame, this value set to 0. If any errors in write opration will also set this value to 0. */ uint8_t write_enabled; /* imsg buffer to allow interleaved control frame between non-control frames. */ struct wslay_event_imsg imsgs[2]; /* Pointer to imsgs to indicate current used buffer. */ struct wslay_event_imsg *imsg; /* payload length of frame currently being received. */ uint64_t ipayloadlen; /* next byte offset of payload currently being received. */ uint64_t ipayloadoff; /* error value set by user callback */ int error; /* Pointer to the message currently being sent. NULL if no message is currently sent. */ struct wslay_event_omsg *omsg; /* Queue for non-control frames */ struct wslay_queue/**/ *send_queue; /* Queue for control frames */ struct wslay_queue/**/ *send_ctrl_queue; /* Size of send_queue + size of send_ctrl_queue */ size_t queued_msg_count; /* The sum of message length in send_queue */ size_t queued_msg_length; /* Buffer used for fragmented messages */ uint8_t obuf[4096]; uint8_t *obuflimit; uint8_t *obufmark; /* payload length of frame currently being sent. */ uint64_t opayloadlen; /* next byte offset of payload currently being sent. */ uint64_t opayloadoff; struct wslay_event_callbacks callbacks; struct wslay_event_frame_user_data frame_user_data; void *user_data; uint8_t allowed_rsv_bits; }; #endif /* WSLAY_EVENT_H */ wslay-release-1.1.1/lib/wslay_frame.c000066400000000000000000000240501367333100700175160ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_frame.h" #include #include #include #include "wslay_net.h" #define wslay_min(A, B) (((A) < (B)) ? (A) : (B)) int wslay_frame_context_init(wslay_frame_context_ptr *ctx, const struct wslay_frame_callbacks *callbacks, void *user_data) { *ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context)); if(*ctx == NULL) { return -1; } memset(*ctx, 0, sizeof(struct wslay_frame_context)); (*ctx)->istate = RECV_HEADER1; (*ctx)->ireqread = 2; (*ctx)->ostate = PREP_HEADER; (*ctx)->user_data = user_data; (*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf; (*ctx)->callbacks = *callbacks; return 0; } void wslay_frame_context_free(wslay_frame_context_ptr ctx) { free(ctx); } ssize_t wslay_frame_send(wslay_frame_context_ptr ctx, struct wslay_frame_iocb *iocb) { if(iocb->data_length > iocb->payload_length) { return WSLAY_ERR_INVALID_ARGUMENT; } if(ctx->ostate == PREP_HEADER) { uint8_t *hdptr = ctx->oheader; memset(ctx->oheader, 0, sizeof(ctx->oheader)); *hdptr |= (iocb->fin << 7) & 0x80u; *hdptr |= (iocb->rsv << 4) & 0x70u; *hdptr |= iocb->opcode & 0xfu; ++hdptr; *hdptr |= (iocb->mask << 7) & 0x80u; if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) { return WSLAY_ERR_INVALID_ARGUMENT; } if(iocb->payload_length < 126) { *hdptr |= iocb->payload_length; ++hdptr; } else if(iocb->payload_length < (1 << 16)) { uint16_t len = htons(iocb->payload_length); *hdptr |= 126; ++hdptr; memcpy(hdptr, &len, 2); hdptr += 2; } else if(iocb->payload_length < (1ull << 63)) { uint64_t len = hton64(iocb->payload_length); *hdptr |= 127; ++hdptr; memcpy(hdptr, &len, 8); hdptr += 8; } else { /* Too large payload length */ return WSLAY_ERR_INVALID_ARGUMENT; } if(iocb->mask) { if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4, ctx->user_data) != 0) { return WSLAY_ERR_INVALID_CALLBACK; } else { ctx->omask = 1; memcpy(hdptr, ctx->omaskkey, 4); hdptr += 4; } } ctx->ostate = SEND_HEADER; ctx->oheadermark = ctx->oheader; ctx->oheaderlimit = hdptr; ctx->opayloadlen = iocb->payload_length; ctx->opayloadoff = 0; } if(ctx->ostate == SEND_HEADER) { ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark; ssize_t r; int flags = 0; if(iocb->data_length > 0) { flags |= WSLAY_MSG_MORE; }; r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags, ctx->user_data); if(r > 0) { if(r > len) { return WSLAY_ERR_INVALID_CALLBACK; } else { ctx->oheadermark += r; if(ctx->oheadermark == ctx->oheaderlimit) { ctx->ostate = SEND_PAYLOAD; } else { return WSLAY_ERR_WANT_WRITE; } } } else { return WSLAY_ERR_WANT_WRITE; } } if(ctx->ostate == SEND_PAYLOAD) { size_t totallen = 0; if(iocb->data_length > 0) { if(ctx->omask) { uint8_t temp[4096]; const uint8_t *datamark = iocb->data, *datalimit = iocb->data+iocb->data_length; while(datamark < datalimit) { size_t datalen = datalimit - datamark; const uint8_t *writelimit = datamark+ wslay_min(sizeof(temp), datalen); size_t writelen = writelimit-datamark; ssize_t r; size_t i; for(i = 0; i < writelen; ++i) { temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4]; } r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data); if(r > 0) { if((size_t)r > writelen) { return WSLAY_ERR_INVALID_CALLBACK; } else { datamark += r; ctx->opayloadoff += r; totallen += r; } } else { if(totallen > 0) { break; } else { return WSLAY_ERR_WANT_WRITE; } } } } else { ssize_t r; r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0, ctx->user_data); if(r > 0) { if((size_t)r > iocb->data_length) { return WSLAY_ERR_INVALID_CALLBACK; } else { ctx->opayloadoff += r; totallen = r; } } else { return WSLAY_ERR_WANT_WRITE; } } } if(ctx->opayloadoff == ctx->opayloadlen) { ctx->ostate = PREP_HEADER; } return totallen; } return WSLAY_ERR_INVALID_ARGUMENT; } static void wslay_shift_ibuf(wslay_frame_context_ptr ctx) { ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark; memmove(ctx->ibuf, ctx->ibufmark, len); ctx->ibuflimit = ctx->ibuf+len; ctx->ibufmark = ctx->ibuf; } static ssize_t wslay_recv(wslay_frame_context_ptr ctx) { ssize_t r; if(ctx->ibufmark != ctx->ibuf) { wslay_shift_ibuf(ctx); } r = ctx->callbacks.recv_callback (ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit, 0, ctx->user_data); if(r > 0) { ctx->ibuflimit += r; } else { r = WSLAY_ERR_WANT_READ; } return r; } #define WSLAY_AVAIL_IBUF(ctx) ((size_t)(ctx->ibuflimit - ctx->ibufmark)) ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx, struct wslay_frame_iocb *iocb) { ssize_t r; if(ctx->istate == RECV_HEADER1) { uint8_t fin, opcode, rsv, payloadlen; if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { if((r = wslay_recv(ctx)) <= 0) { return r; } } if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { return WSLAY_ERR_WANT_READ; } fin = (ctx->ibufmark[0] >> 7) & 1; rsv = (ctx->ibufmark[0] >> 4) & 7; opcode = ctx->ibufmark[0] & 0xfu; ctx->iom.opcode = opcode; ctx->iom.fin = fin; ctx->iom.rsv = rsv; ++ctx->ibufmark; ctx->imask = (ctx->ibufmark[0] >> 7) & 1; payloadlen = ctx->ibufmark[0] & 0x7fu; ++ctx->ibufmark; if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) { return WSLAY_ERR_PROTO; } if(payloadlen == 126) { ctx->istate = RECV_EXT_PAYLOADLEN; ctx->ireqread = 2; } else if(payloadlen == 127) { ctx->istate = RECV_EXT_PAYLOADLEN; ctx->ireqread = 8; } else { ctx->ipayloadlen = payloadlen; ctx->ipayloadoff = 0; if(ctx->imask) { ctx->istate = RECV_MASKKEY; ctx->ireqread = 4; } else { ctx->istate = RECV_PAYLOAD; } } } if(ctx->istate == RECV_EXT_PAYLOADLEN) { if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { if((r = wslay_recv(ctx)) <= 0) { return r; } if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { return WSLAY_ERR_WANT_READ; } } ctx->ipayloadlen = 0; ctx->ipayloadoff = 0; memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread), ctx->ibufmark, ctx->ireqread); ctx->ipayloadlen = ntoh64(ctx->ipayloadlen); ctx->ibufmark += ctx->ireqread; if(ctx->ireqread == 8) { if(ctx->ipayloadlen < (1 << 16) || ctx->ipayloadlen & (1ull << 63)) { return WSLAY_ERR_PROTO; } } else if(ctx->ipayloadlen < 126) { return WSLAY_ERR_PROTO; } if(ctx->imask) { ctx->istate = RECV_MASKKEY; ctx->ireqread = 4; } else { ctx->istate = RECV_PAYLOAD; } } if(ctx->istate == RECV_MASKKEY) { if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { if((r = wslay_recv(ctx)) <= 0) { return r; } if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) { return WSLAY_ERR_WANT_READ; } } memcpy(ctx->imaskkey, ctx->ibufmark, 4); ctx->ibufmark += 4; ctx->istate = RECV_PAYLOAD; } if(ctx->istate == RECV_PAYLOAD) { uint8_t *readlimit, *readmark; uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff; if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) { if((r = wslay_recv(ctx)) <= 0) { return r; } } readmark = ctx->ibufmark; readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ? ctx->ibuflimit : ctx->ibufmark+rempayloadlen; if(ctx->imask) { for(; ctx->ibufmark != readlimit; ++ctx->ibufmark, ++ctx->ipayloadoff) { ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4]; } } else { ctx->ibufmark = readlimit; ctx->ipayloadoff += readlimit-readmark; } iocb->fin = ctx->iom.fin; iocb->rsv = ctx->iom.rsv; iocb->opcode = ctx->iom.opcode; iocb->payload_length = ctx->ipayloadlen; iocb->mask = ctx->imask; iocb->data = readmark; iocb->data_length = ctx->ibufmark-readmark; if(ctx->ipayloadlen == ctx->ipayloadoff) { ctx->istate = RECV_HEADER1; ctx->ireqread = 2; } return iocb->data_length; } return WSLAY_ERR_INVALID_ARGUMENT; } wslay-release-1.1.1/lib/wslay_frame.h000066400000000000000000000041071367333100700175240ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_FRAME_H #define WSLAY_FRAME_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include enum wslay_frame_state { PREP_HEADER, SEND_HEADER, SEND_PAYLOAD, RECV_HEADER1, RECV_PAYLOADLEN, RECV_EXT_PAYLOADLEN, RECV_MASKKEY, RECV_PAYLOAD }; struct wslay_frame_opcode_memo { uint8_t fin; uint8_t opcode; uint8_t rsv; }; struct wslay_frame_context { uint8_t ibuf[4096]; uint8_t *ibufmark; uint8_t *ibuflimit; struct wslay_frame_opcode_memo iom; uint64_t ipayloadlen; uint64_t ipayloadoff; uint8_t imask; uint8_t imaskkey[4]; enum wslay_frame_state istate; size_t ireqread; uint8_t oheader[14]; uint8_t *oheadermark; uint8_t *oheaderlimit; uint64_t opayloadlen; uint64_t opayloadoff; uint8_t omask; uint8_t omaskkey[4]; enum wslay_frame_state ostate; struct wslay_frame_callbacks callbacks; void *user_data; }; #endif /* WSLAY_FRAME_H */ wslay-release-1.1.1/lib/wslay_net.c000066400000000000000000000025531367333100700172160ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_net.h" #ifndef WORDS_BIGENDIAN uint64_t wslay_byteswap64(uint64_t x) { uint64_t u = ntohl(x & 0xffffffffllu); uint64_t l = ntohl(x >> 32); return (u << 32) | l; } #endif /* !WORDS_BIGENDIAN */ wslay-release-1.1.1/lib/wslay_net.h000066400000000000000000000034621367333100700172230ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_NET_H #define WSLAY_NET_H #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #ifdef HAVE_ARPA_INET_H # include #endif /* HAVE_ARPA_INET_H */ #ifdef HAVE_NETINET_IN_H # include #endif /* HAVE_NETINET_IN_H */ /* For Mingw build */ #ifdef HAVE_WINSOCK2_H # include #endif /* HAVE_WINSOCK2_H */ #ifdef WORDS_BIGENDIAN # define ntoh64(x) (x) # define hton64(x) (x) #else /* !WORDS_BIGENDIAN */ uint64_t wslay_byteswap64(uint64_t x); # define ntoh64(x) wslay_byteswap64(x) # define hton64(x) wslay_byteswap64(x) #endif /* !WORDS_BIGENDIAN */ #endif /* WSLAY_NET_H */ wslay-release-1.1.1/lib/wslay_queue.c000066400000000000000000000057161367333100700175600ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_queue.h" #include #include struct wslay_queue* wslay_queue_new(void) { struct wslay_queue *queue = (struct wslay_queue*)malloc (sizeof(struct wslay_queue)); if(!queue) { return NULL; } queue->top = queue->tail = NULL; return queue; } void wslay_queue_free(struct wslay_queue *queue) { if(!queue) { return; } else { struct wslay_queue_cell *p = queue->top; while(p) { struct wslay_queue_cell *next = p->next; free(p); p = next; } free(queue); } } int wslay_queue_push(struct wslay_queue *queue, void *data) { struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc (sizeof(struct wslay_queue_cell)); if(!new_cell) { return WSLAY_ERR_NOMEM; } new_cell->data = data; new_cell->next = NULL; if(queue->tail) { queue->tail->next = new_cell; queue->tail = new_cell; } else { queue->top = queue->tail = new_cell; } return 0; } int wslay_queue_push_front(struct wslay_queue *queue, void *data) { struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc (sizeof(struct wslay_queue_cell)); if(!new_cell) { return WSLAY_ERR_NOMEM; } new_cell->data = data; new_cell->next = queue->top; queue->top = new_cell; if(!queue->tail) { queue->tail = queue->top; } return 0; } void wslay_queue_pop(struct wslay_queue *queue) { struct wslay_queue_cell *top = queue->top; assert(top); queue->top = top->next; if(top == queue->tail) { queue->tail = NULL; } free(top); } void* wslay_queue_top(struct wslay_queue *queue) { assert(queue->top); return queue->top->data; } void* wslay_queue_tail(struct wslay_queue *queue) { assert(queue->tail); return queue->tail->data; } int wslay_queue_empty(struct wslay_queue *queue) { return queue->top == NULL; } wslay-release-1.1.1/lib/wslay_queue.h000066400000000000000000000036071367333100700175620ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_QUEUE_H #define WSLAY_QUEUE_H #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include struct wslay_queue_cell { void *data; struct wslay_queue_cell *next; }; struct wslay_queue { struct wslay_queue_cell *top; struct wslay_queue_cell *tail; }; struct wslay_queue* wslay_queue_new(void); void wslay_queue_free(struct wslay_queue *queue); int wslay_queue_push(struct wslay_queue *queue, void *data); int wslay_queue_push_front(struct wslay_queue *queue, void *data); void wslay_queue_pop(struct wslay_queue *queue); void* wslay_queue_top(struct wslay_queue *queue); void* wslay_queue_tail(struct wslay_queue *queue); int wslay_queue_empty(struct wslay_queue *queue); #endif /* WSLAY_QUEUE_H */ wslay-release-1.1.1/lib/wslay_stack.c000066400000000000000000000044401367333100700175320ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_stack.h" #include #include struct wslay_stack* wslay_stack_new() { struct wslay_stack *stack = (struct wslay_stack*)malloc (sizeof(struct wslay_stack)); if(!stack) { return NULL; } stack->top = NULL; return stack; } void wslay_stack_free(struct wslay_stack *stack) { struct wslay_stack_cell *p; if(!stack) { return; } p = stack->top; while(p) { struct wslay_stack_cell *next = p->next; free(p); p = next; } free(stack); } int wslay_stack_push(struct wslay_stack *stack, void *data) { struct wslay_stack_cell *new_cell = (struct wslay_stack_cell*)malloc (sizeof(struct wslay_stack_cell)); if(!new_cell) { return WSLAY_ERR_NOMEM; } new_cell->data = data; new_cell->next = stack->top; stack->top = new_cell; return 0; } void wslay_stack_pop(struct wslay_stack *stack) { struct wslay_stack_cell *top = stack->top; assert(top); stack->top = top->next; free(top); } void* wslay_stack_top(struct wslay_stack *stack) { assert(stack->top); return stack->top->data; } int wslay_stack_empty(struct wslay_stack *stack) { return stack->top == NULL; } wslay-release-1.1.1/lib/wslay_stack.h000066400000000000000000000033541367333100700175420ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_STACK_H #define WSLAY_STACK_H #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ #include struct wslay_stack_cell { void *data; struct wslay_stack_cell *next; }; struct wslay_stack { struct wslay_stack_cell *top; }; struct wslay_stack* wslay_stack_new(); void wslay_stack_free(struct wslay_stack *stack); int wslay_stack_push(struct wslay_stack *stack, void *data); void wslay_stack_pop(struct wslay_stack *stack); void* wslay_stack_top(struct wslay_stack *stack); int wslay_stack_empty(struct wslay_stack *stack); #endif /* WSLAY_STACK_H */ wslay-release-1.1.1/m4/000077500000000000000000000000001367333100700146125ustar00rootroot00000000000000wslay-release-1.1.1/m4/README000066400000000000000000000000621367333100700154700ustar00rootroot00000000000000Empty m4 directory to make `autoreconf -i` happy. wslay-release-1.1.1/tests/000077500000000000000000000000001367333100700154345ustar00rootroot00000000000000wslay-release-1.1.1/tests/CMakeLists.txt000066400000000000000000000026361367333100700202030ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) find_package(CUnit REQUIRED) # Choose a wslay target to get build options from. # That way we can avoid duplicating them here. if(WSLAY_STATIC) set(WSLAY_TARGET "wslay") else() set(WSLAY_TARGET "wslay_shared") endif() get_target_property(WSLAY_INCLUDE_DIRS ${WSLAY_TARGET} INTERFACE_INCLUDE_DIRECTORIES) get_target_property(WSLAY_COMPILE_DEFINITIONS ${WSLAY_TARGET} COMPILE_DEFINITIONS) set(WSLAY_DIR ${PROJECT_SOURCE_DIR}/lib) set(WSLAY_HEADERS ${WSLAY_DIR}/wslay_event.h ${WSLAY_DIR}/wslay_frame.h ${WSLAY_DIR}/wslay_net.h ${WSLAY_DIR}/wslay_queue.h ${WSLAY_DIR}/wslay_stack.h) set(WSLAY_SOURCES ${WSLAY_DIR}/wslay_event.c ${WSLAY_DIR}/wslay_frame.c ${WSLAY_DIR}/wslay_net.c ${WSLAY_DIR}/wslay_queue.c ${WSLAY_DIR}/wslay_stack.c) set(HEADERS wslay_event_test.h wslay_frame_test.h wslay_queue_test.h wslay_session_test.h wslay_stack_test.h) set(SOURCES wslay_event_test.c wslay_frame_test.c wslay_queue_test.c wslay_session_test.c wslay_stack_test.c main.c) add_executable(wslay_tests ${SOURCES} ${HEADERS} ${WSLAY_SOURCES} ${WSLAY_HEADERS}) target_include_directories(wslay_tests PRIVATE ${WSLAY_DIR} ${WSLAY_INCLUDE_DIRS} ${CUNIT_INCLUDE_DIRS}) target_compile_definitions(wslay_tests PRIVATE ${WSLAY_COMPILE_DEFINITIONS}) target_link_libraries(wslay_tests ${CUNIT_LIBRARIES}) add_dependencies(wslay_tests ${WSLAY_TARGET}) add_test(NAME wslay_tests COMMAND wslay_tests) wslay-release-1.1.1/tests/Makefile.am000066400000000000000000000031201367333100700174640ustar00rootroot00000000000000# Wslay - The WebSocket Library # Copyright (c) 2011, 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 wslay_frame_test.c\ wslay_event_test.c\ wslay_queue_test.c HFILES = wslay_session_test.h wslay_frame_test.h\ wslay_event_test.h\ wslay_queue_test.h main_SOURCES = $(HFILES) $(OBJECTS) main_LDADD = ${top_builddir}/lib/libwslay.la -lcunit main_LDFLAGS = -static AM_CFLAGS = -Wall -g -O2 -I${top_srcdir}/lib -I${top_srcdir}/lib/includes\ @DEFS@ # DEFS += -D_ISOC99_SOURCE -D_GNU_SOURCE TESTS = main endif # HAVE_CUNIT wslay-release-1.1.1/tests/main.c000066400000000000000000000150221367333100700165240ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_frame_test.h" #include "wslay_event_test.h" #include "wslay_queue_test.h" static int init_suite1(void) { return 0; } static int clean_suite1(void) { return 0; } int main(void) { CU_pSuite pSuite = NULL; unsigned int num_tests_failed; /* 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("libwslay_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, "wslay_frame_context_init", test_wslay_frame_context_init) || !CU_add_test(pSuite, "wslay_frame_recv", test_wslay_frame_recv) || !CU_add_test(pSuite, "wslay_frame_recv_1byte", test_wslay_frame_recv_1byte) || !CU_add_test(pSuite, "wslay_frame_recv_fragmented", test_wslay_frame_recv_fragmented) || !CU_add_test(pSuite, "wslay_frame_recv_interleaved_ctrl_frame", test_wslay_frame_recv_interleaved_ctrl_frame) || !CU_add_test(pSuite, "wslay_frame_recv_zero_payloadlen", test_wslay_frame_recv_zero_payloadlen) || !CU_add_test(pSuite, "wslay_frame_recv_too_large_payload", test_wslay_frame_recv_too_large_payload) || !CU_add_test(pSuite, "wslay_frame_recv_ctrl_too_large_payload", test_wslay_frame_recv_ctrl_frame_too_large_payload) || !CU_add_test(pSuite, "wslay_frame_recv_minimum_ext_payload16", test_wslay_frame_recv_minimum_ext_payload16) || !CU_add_test(pSuite, "wslay_frame_recv_minimum_ext_payload64", test_wslay_frame_recv_minimum_ext_payload64) || !CU_add_test(pSuite, "wslay_frame_send", test_wslay_frame_send) || !CU_add_test(pSuite, "wslay_frame_send_fragmented", test_wslay_frame_send_fragmented) || !CU_add_test(pSuite, "wslay_frame_send_interleaved_ctrl_frame", test_wslay_frame_send_interleaved_ctrl_frame) || !CU_add_test(pSuite, "wslay_frame_send_1byte_masked", test_wslay_frame_send_1byte_masked) || !CU_add_test(pSuite, "wslay_frame_send_zero_payloadlen", test_wslay_frame_send_zero_payloadlen) || !CU_add_test(pSuite, "wslay_frame_send_too_large_payload", test_wslay_frame_send_too_large_payload) || !CU_add_test(pSuite, "wslay_frame_send_ctrl_frame_too_large_payload", test_wslay_frame_send_ctrl_frame_too_large_payload) || !CU_add_test(pSuite, "wslay_event_send_fragmented_msg", test_wslay_event_send_fragmented_msg) || !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_empty_data", test_wslay_event_send_fragmented_msg_empty_data) || !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_with_ctrl", test_wslay_event_send_fragmented_msg_with_ctrl) || !CU_add_test(pSuite, "wslay_event_send_fragmented_msg_with_rsv1", test_wslay_event_send_fragmented_msg_with_rsv1) || !CU_add_test(pSuite, "wslay_event_send_msg_with_rsv1", test_wslay_event_send_msg_with_rsv1) || !CU_add_test(pSuite, "wslay_event_send_ctrl_msg_first", test_wslay_event_send_ctrl_msg_first) || !CU_add_test(pSuite, "wslay_event_send_ctrl_msg_with_rsv1", test_wslay_event_send_ctrl_msg_with_rsv1) || !CU_add_test(pSuite, "wslay_event_queue_close", test_wslay_event_queue_close) || !CU_add_test(pSuite, "wslay_event_queue_close_without_code", test_wslay_event_queue_close_without_code) || !CU_add_test(pSuite, "wslay_event_recv_close_without_code", test_wslay_event_recv_close_without_code) || !CU_add_test(pSuite, "wslay_event_reply_close", test_wslay_event_reply_close) || !CU_add_test(pSuite, "wslay_event_no_more_msg", test_wslay_event_no_more_msg) || !CU_add_test(pSuite, "wslay_event_callback_failure", test_wslay_event_callback_failure) || !CU_add_test(pSuite, "wslay_event_no_buffering", test_wslay_event_no_buffering) || !CU_add_test(pSuite, "wslay_event_recv_text_frame_with_rsv1", test_wslay_event_recv_text_frame_with_rsv1) || !CU_add_test(pSuite, "wslay_event_frame_too_big", test_wslay_event_frame_too_big) || !CU_add_test(pSuite, "wslay_event_message_too_big", test_wslay_event_message_too_big) || !CU_add_test(pSuite, "wslay_event_config_set_allowed_rsv_bits", test_wslay_event_config_set_allowed_rsv_bits) || !CU_add_test(pSuite, "wslay_queue", test_wslay_queue)) { 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(); num_tests_failed = CU_get_number_of_tests_failed(); CU_cleanup_registry(); if (CU_get_error() == CUE_SUCCESS) { return (int)num_tests_failed; } else { printf("CUnit Error: %s\n", CU_get_error_msg()); return CU_get_error(); } } wslay-release-1.1.1/tests/wslay_event_test.c000066400000000000000000000602311367333100700212010ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_event_test.h" #include #include #include "wslay_event.h" struct scripted_data_feed { uint8_t data[8192]; uint8_t* datamark; uint8_t* datalimit; size_t feedseq[8192]; size_t seqidx; }; struct accumulator { uint8_t buf[4096]; size_t length; }; struct my_user_data { struct scripted_data_feed *df; struct accumulator *acc; }; static void scripted_data_feed_init(struct scripted_data_feed *df, const uint8_t *data, size_t data_length) { memset(df, 0, sizeof(struct scripted_data_feed)); if(data_length) { memcpy(df->data, data, data_length); } df->datamark = df->data; df->datalimit = df->data+data_length; df->feedseq[0] = data_length; } static ssize_t scripted_read_callback (wslay_event_context_ptr ctx, uint8_t *data, size_t len, const union wslay_event_msg_source *source, int *eof, void *user_data) { struct scripted_data_feed *df = (struct scripted_data_feed*)source->data; 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; } if(df->datamark == df->datalimit) { *eof = 1; } return wlen; } static ssize_t scripted_recv_callback(wslay_event_context_ptr ctx, uint8_t* data, size_t len, int flags, void *user_data) { struct scripted_data_feed *df = ((struct 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(wslay_event_context_ptr ctx, const uint8_t *buf, size_t len, int flags, void* user_data) { struct accumulator *acc = ((struct 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 ssize_t one_accumulator_send_callback(wslay_event_context_ptr ctx, const uint8_t *buf, size_t len, int flags, void* user_data) { struct accumulator *acc = ((struct my_user_data*)user_data)->acc; assert(len > 0); memcpy(acc->buf+acc->length, buf, 1); acc->length += 1; return 1; } static ssize_t fail_recv_callback(wslay_event_context_ptr ctx, uint8_t* data, size_t len, int flags, void *user_data) { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); return -1; } static ssize_t fail_send_callback(wslay_event_context_ptr ctx, const uint8_t *buf, size_t len, int flags, void* user_data) { wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE); return -1; } void test_wslay_event_send_fragmented_msg(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "Hello"; struct scripted_data_feed df; struct wslay_event_fragmented_msg arg; const uint8_t ans[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, 0x80, 0x02, 0x6c, 0x6f }; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1); df.feedseq[0] = 3; df.feedseq[1] = 2; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.source.data = &df; arg.read_callback = scripted_read_callback; CU_ASSERT(0 == wslay_event_queue_fragmented_msg(ctx, &arg)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT_EQUAL(9, acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_fragmented_msg_empty_data(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; struct scripted_data_feed df; struct wslay_event_fragmented_msg arg; const uint8_t ans[] = {0x81, 0x00}; scripted_data_feed_init(&df, NULL, 0); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.source.data = &df; arg.read_callback = scripted_read_callback; CU_ASSERT(0 == wslay_event_queue_fragmented_msg(ctx, &arg)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT_EQUAL(2, acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_fragmented_msg_with_ctrl(void) { int i; wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "Hello"; struct scripted_data_feed df; struct wslay_event_fragmented_msg arg; struct wslay_event_msg ctrl_arg; const uint8_t ans[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */ 0x89, 0x00, /* unmasked ping */ 0x80, 0x02, 0x6c, 0x6f /* "lo" */ }; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1); df.feedseq[0] = 3; df.feedseq[1] = 2; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = one_accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.source.data = &df; arg.read_callback = scripted_read_callback; CU_ASSERT(0 == wslay_event_queue_fragmented_msg(ctx, &arg)); CU_ASSERT(1 == wslay_event_get_queued_msg_count(ctx)); CU_ASSERT(0 == wslay_event_get_queued_msg_length(ctx)); CU_ASSERT(0 == wslay_event_send(ctx)); memset(&ctrl_arg, 0, sizeof(ctrl_arg)); ctrl_arg.opcode = WSLAY_PING; ctrl_arg.msg_length = 0; CU_ASSERT(0 == wslay_event_queue_msg(ctx, &ctrl_arg)); CU_ASSERT(2 == wslay_event_get_queued_msg_count(ctx)); for(i = 0; i < 10; ++i) { CU_ASSERT(0 == wslay_event_send(ctx)); } CU_ASSERT(0 == wslay_event_get_queued_msg_count(ctx)); CU_ASSERT(11 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_fragmented_msg_with_rsv1(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "Hello"; struct scripted_data_feed df; struct wslay_event_fragmented_msg arg; const uint8_t ans[] = { 0x41, 0x03, 0x48, 0x65, 0x6c, 0x80, 0x02, 0x6c, 0x6f }; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)-1); df.feedseq[0] = 3; df.feedseq[1] = 2; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.source.data = &df; arg.read_callback = scripted_read_callback; CU_ASSERT(0 == wslay_event_queue_fragmented_msg_ex(ctx, &arg, WSLAY_RSV1_BIT)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT_EQUAL(9, acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_msg_with_rsv1(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "Hello"; struct wslay_event_msg arg; const uint8_t ans[] = { 0xc1, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.msg = (const uint8_t*)msg; arg.msg_length = 5; CU_ASSERT(0 == wslay_event_queue_msg_ex(ctx, &arg, WSLAY_RSV1_BIT)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(7 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_ctrl_msg_first(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "Hello"; struct wslay_event_msg arg; const uint8_t ans[] = { 0x89, 0x00, /* unmasked ping */ 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_PING; arg.msg_length = 0; CU_ASSERT(0 == wslay_event_queue_msg(ctx, &arg)); arg.opcode = WSLAY_TEXT_FRAME; arg.msg = (const uint8_t*)msg; arg.msg_length = 5; CU_ASSERT(0 == wslay_event_queue_msg(ctx, &arg)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(9 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); wslay_event_context_free(ctx); } void test_wslay_event_send_ctrl_msg_with_rsv1(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct wslay_event_msg arg; memset(&callbacks, 0, sizeof(callbacks)); wslay_event_context_server_init(&ctx, &callbacks, NULL); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); memset(&arg, 0, sizeof(arg)); arg.opcode = WSLAY_PING; arg.msg_length = 0; CU_ASSERT(WSLAY_ERR_INVALID_ARGUMENT == wslay_event_queue_msg_ex(ctx, &arg, WSLAY_RSV1_BIT)); wslay_event_context_free(ctx); } void test_wslay_event_queue_close(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const char msg[] = "H"; const uint8_t ans[] = { 0x88, 0x03, 0x03, 0xf1, 0x48 /* "H" */ }; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); CU_ASSERT(0 == wslay_event_queue_close(ctx, WSLAY_CODE_MESSAGE_TOO_BIG, (const uint8_t*)msg, 1)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(5 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); CU_ASSERT(1 == wslay_event_get_close_sent(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_queue_close_without_code(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; const uint8_t ans[] = { 0x88, 0x00 }; struct wslay_event_msg ping = { WSLAY_PING, NULL, 0 }; memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; memset(&acc, 0, sizeof(acc)); ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); CU_ASSERT(0 == wslay_event_queue_msg(ctx, &ping)); /* See that ping is not sent because close frame is queued */ CU_ASSERT(0 == wslay_event_queue_close(ctx, 0, NULL, 0)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(2 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); CU_ASSERT(1 == wslay_event_get_close_sent(ctx)); CU_ASSERT(WSLAY_CODE_NO_STATUS_RCVD == wslay_event_get_status_code_sent(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_recv_close_without_code(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; const uint8_t msg[] = { 0x88u, 0x00 }; struct scripted_data_feed df; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); callbacks.recv_callback = scripted_recv_callback; ud.df = &df; wslay_event_context_client_init(&ctx, &callbacks, &ud); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(1 == wslay_event_get_close_received(ctx)); CU_ASSERT(WSLAY_CODE_NO_STATUS_RCVD == wslay_event_get_status_code_received(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_reply_close(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; /* Masked close frame with code = 1009, reason = "Hello" */ const uint8_t msg[] = { 0x88u, 0x87u, 0x00u, 0x00u, 0x00u, 0x00u, 0x03, 0xf1, /* 1009 */ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; const uint8_t ans[] = { 0x88u, 0x07u, 0x03, 0xf1, /* 1009 */ 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; struct scripted_data_feed df; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; callbacks.recv_callback = scripted_recv_callback; memset(&acc, 0, sizeof(acc)); ud.df = &df; ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(1 == wslay_event_get_queued_msg_count(ctx)); /* 7 bytes = 2 bytes status code + "Hello" */ CU_ASSERT(7 == wslay_event_get_queued_msg_length(ctx)); CU_ASSERT(1 == wslay_event_get_close_received(ctx)); CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG == wslay_event_get_status_code_received(ctx)); CU_ASSERT(WSLAY_CODE_ABNORMAL_CLOSURE == wslay_event_get_status_code_sent(ctx)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(0 == wslay_event_get_queued_msg_count(ctx)); CU_ASSERT(0 == wslay_event_get_queued_msg_length(ctx)); CU_ASSERT(9 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); CU_ASSERT(1 == wslay_event_get_close_sent(ctx)); CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG == wslay_event_get_status_code_received(ctx)); CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG == wslay_event_get_status_code_sent(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_no_more_msg(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); wslay_event_context_server_init(&ctx, &callbacks, NULL); CU_ASSERT(0 == wslay_event_queue_close(ctx, 0, NULL, 0)); CU_ASSERT(WSLAY_ERR_NO_MORE_MSG == wslay_event_queue_close(ctx, 0, NULL, 0)); wslay_event_context_free(ctx); } void test_wslay_event_callback_failure(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.recv_callback = fail_recv_callback; callbacks.send_callback = fail_send_callback; wslay_event_context_server_init(&ctx, &callbacks, NULL); CU_ASSERT(WSLAY_ERR_CALLBACK_FAILURE == wslay_event_recv(ctx)); /* close control frame is in queue */ CU_ASSERT(WSLAY_ERR_CALLBACK_FAILURE == wslay_event_send(ctx)); wslay_event_context_free(ctx); } static void no_buffering_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { if(arg->opcode == WSLAY_PING) { CU_ASSERT(3 == arg->msg_length); CU_ASSERT(0 == memcmp("Foo", arg->msg, arg->msg_length)); } else { CU_ASSERT(WSLAY_TEXT_FRAME == arg->opcode); CU_ASSERT(0 == arg->msg_length); } } void test_wslay_event_no_buffering(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; const uint8_t msg[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */ 0x89, 0x03, 0x46, 0x6f, 0x6f, /* ping with "Foo" */ 0x80, 0x02, 0x6c, 0x6f, /* "lo" */ }; struct scripted_data_feed df; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); ud.df = &df; callbacks.recv_callback = scripted_recv_callback; callbacks.on_msg_recv_callback = no_buffering_callback; wslay_event_context_client_init(&ctx, &callbacks, &ud); wslay_event_config_set_no_buffering(ctx, 1); CU_ASSERT(0 == wslay_event_recv(ctx)); /* pong must be queued */ CU_ASSERT(wslay_event_want_write(ctx)); wslay_event_context_free(ctx); } static void text_rsv1_on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) { CU_ASSERT(WSLAY_TEXT_FRAME == arg->opcode); CU_ASSERT(WSLAY_RSV1_BIT == arg->rsv); } void test_wslay_event_recv_text_frame_with_rsv1(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; const uint8_t msg[] = { 0xc1, 0x07, 0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00 // "Hello" pm-deflate }; const uint8_t fragmented[] = { 0x41, 0x03, 0xf2, 0x48, 0xcd, // First fragment 0x80, 0x04, 0xc9, 0xc9, 0x07, 0x00 // Second fragment, RSV1 bit off }; const uint8_t bad_fragment[] = { 0x41, 0x03, 0xf2, 0x48, 0xcd, 0xc0, 0x04, 0xc9, 0xc9, 0x07, 0x00 // RSV1 bit on }; const uint8_t pingmsg[] = { 0xc9, 0x03, 0x46, 0x6f, 0x6f /* ping with "Foo" */ }; struct scripted_data_feed df; /* Message marked with RSV1 should skip UTF-8 validation */ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); ud.df = &df; callbacks.recv_callback = scripted_recv_callback; callbacks.on_msg_recv_callback = text_rsv1_on_msg_recv_callback; wslay_event_context_client_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(0 == wslay_event_want_write(ctx)); wslay_event_context_free(ctx); /* UTF-8 validation is skipped for continuation frames if the * initial frame was marked with RSV1 bit */ scripted_data_feed_init(&df, (const uint8_t*)fragmented, sizeof(fragmented)); memset(&callbacks, 0, sizeof(callbacks)); ud.df = &df; callbacks.recv_callback = scripted_recv_callback; callbacks.on_msg_recv_callback = text_rsv1_on_msg_recv_callback; wslay_event_context_client_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(0 == wslay_event_want_write(ctx)); wslay_event_context_free(ctx); /* disallow RSV1 */ scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); wslay_event_context_client_init(&ctx, &callbacks, &ud); CU_ASSERT(0 == wslay_event_recv(ctx)); /* Close frame must be queued */ CU_ASSERT(wslay_event_want_write(ctx)); wslay_event_context_free(ctx); /* RSV1 is not allowed in continuation frame */ scripted_data_feed_init(&df, (const uint8_t*)bad_fragment, sizeof(bad_fragment)); wslay_event_context_client_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); CU_ASSERT(0 == wslay_event_recv(ctx)); /* Close frame must be queued */ CU_ASSERT(wslay_event_want_write(ctx)); wslay_event_context_free(ctx); /* RSV1 is not allowed in ping frame */ scripted_data_feed_init(&df, (const uint8_t*)pingmsg, sizeof(pingmsg)); wslay_event_context_client_init(&ctx, &callbacks, &ud); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); CU_ASSERT(0 == wslay_event_recv(ctx)); /* Close frame must be queued */ CU_ASSERT(wslay_event_want_write(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_frame_too_big(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; /* Masked text frame */ const uint8_t msg[] = { 0x81, 0x85, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; const uint8_t ans[] = { 0x88, 0x02, 0x03, 0xf1 /* 1009 */ }; struct scripted_data_feed df; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; callbacks.recv_callback = scripted_recv_callback; memset(&acc, 0, sizeof(acc)); ud.df = &df; ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); wslay_event_config_set_max_recv_msg_length(ctx, 4); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(4 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); CU_ASSERT(1 == wslay_event_get_close_sent(ctx)); CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG == wslay_event_get_status_code_sent(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_message_too_big(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; struct my_user_data ud; struct accumulator acc; /* Masked text 2 frames */ const uint8_t msg[] = { 0x01, 0x85, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, /* "Hello" */ 0x80, 0x85, 0x00, 0x00, 0x00, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f /* "Hello" */ }; const uint8_t ans[] = { 0x88, 0x02, 0x03, 0xf1 /* 1009 */ }; struct scripted_data_feed df; scripted_data_feed_init(&df, (const uint8_t*)msg, sizeof(msg)); memset(&callbacks, 0, sizeof(callbacks)); callbacks.send_callback = accumulator_send_callback; callbacks.recv_callback = scripted_recv_callback; memset(&acc, 0, sizeof(acc)); ud.df = &df; ud.acc = &acc; wslay_event_context_server_init(&ctx, &callbacks, &ud); wslay_event_config_set_max_recv_msg_length(ctx, 9); CU_ASSERT(0 == wslay_event_recv(ctx)); CU_ASSERT(0 == wslay_event_send(ctx)); CU_ASSERT(4 == acc.length); CU_ASSERT(0 == memcmp(ans, acc.buf, acc.length)); CU_ASSERT(1 == wslay_event_get_close_sent(ctx)); CU_ASSERT(WSLAY_CODE_MESSAGE_TOO_BIG == wslay_event_get_status_code_sent(ctx)); wslay_event_context_free(ctx); } void test_wslay_event_config_set_allowed_rsv_bits(void) { wslay_event_context_ptr ctx; struct wslay_event_callbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); CU_ASSERT(0 == wslay_event_context_server_init(&ctx, &callbacks, NULL)); CU_ASSERT(WSLAY_RSV_NONE == ctx->allowed_rsv_bits); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT); CU_ASSERT(WSLAY_RSV1_BIT == ctx->allowed_rsv_bits); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV1_BIT | WSLAY_RSV2_BIT | WSLAY_RSV3_BIT); CU_ASSERT(WSLAY_RSV1_BIT == ctx->allowed_rsv_bits); wslay_event_config_set_allowed_rsv_bits(ctx, WSLAY_RSV2_BIT | WSLAY_RSV3_BIT); CU_ASSERT(WSLAY_RSV_NONE == ctx->allowed_rsv_bits); wslay_event_context_free(ctx); } wslay-release-1.1.1/tests/wslay_event_test.h000066400000000000000000000041501367333100700212040ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_EVENT_TEST_H #define WSLAY_EVENT_TEST_H void test_wslay_event_send_fragmented_msg(void); void test_wslay_event_send_fragmented_msg_empty_data(void); void test_wslay_event_send_fragmented_msg_with_ctrl(void); void test_wslay_event_send_fragmented_msg_with_rsv1(void); void test_wslay_event_send_msg_with_rsv1(void); void test_wslay_event_send_ctrl_msg_first(void); void test_wslay_event_send_ctrl_msg_with_rsv1(void); void test_wslay_event_queue_close(void); void test_wslay_event_queue_close_without_code(void); void test_wslay_event_recv_close_without_code(void); void test_wslay_event_reply_close(void); void test_wslay_event_no_more_msg(void); void test_wslay_event_callback_failure(void); void test_wslay_event_no_buffering(void); void test_wslay_event_recv_text_frame_with_rsv1(void); void test_wslay_event_frame_too_big(void); void test_wslay_event_message_too_big(void); void test_wslay_event_config_set_allowed_rsv_bits(void); #endif /* WSLAY_EVENT_TEST_H */ wslay-release-1.1.1/tests/wslay_frame_test.c000066400000000000000000000446211367333100700211570ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_frame_test.h" #include #include #include "wslay_frame.h" void test_wslay_frame_context_init(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks; int user_data; CU_ASSERT_FALSE(wslay_frame_context_init(&ctx, &callbacks, &user_data)); wslay_frame_context_free(ctx); } struct scripted_data_feed { uint8_t data[8192]; uint8_t* datamark; uint8_t* datalimit; size_t feedseq[8192]; size_t seqidx; }; static void scripted_data_feed_init(struct scripted_data_feed *df, uint8_t *data, size_t data_length) { memset(df, 0, sizeof(struct 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 scripted_recv_callback(uint8_t* data, size_t len, int flags, void *user_data) { struct scripted_data_feed *df = (struct scripted_data_feed*)user_data; 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 scripted_send_callback(const uint8_t* data, size_t len, int flags, void *user_data) { struct scripted_data_feed *df = (struct scripted_data_feed*)user_data; size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; memcpy(df->datamark, data, wlen); df->datamark += wlen; if(wlen <= len) { ++df->seqidx; } else { df->feedseq[df->seqidx] -= wlen; } return wlen; } void test_wslay_frame_recv(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; /* Masked text frame containing "Hello" */ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu, 0x4du, 0x51u, 0x58u }; scripted_data_feed_init(&df, msg, sizeof(msg)); wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT(5 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(0x1, iocb.opcode); CU_ASSERT_EQUAL(5, iocb.payload_length); CU_ASSERT_EQUAL(1, iocb.mask); CU_ASSERT_EQUAL(5, iocb.data_length); CU_ASSERT(memcmp("Hello", iocb.data, iocb.data_length) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_1byte(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; size_t i; /* Masked text frame containing "Hello" */ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu, 0x4du, 0x51u, 0x58u }; scripted_data_feed_init(&df, msg, sizeof(msg)); for(i = 0; i < sizeof(msg); ++i) { df.feedseq[i] = 1; } wslay_frame_context_init(&ctx, &callbacks, &df); for(i = 0; i < 4; ++i) { CU_ASSERT(WSLAY_ERR_WANT_READ == wslay_frame_recv(ctx, &iocb)); } for(i = 0; i < 5; ++i) { CU_ASSERT(1 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(0x1, iocb.opcode); CU_ASSERT_EQUAL(5, iocb.payload_length); CU_ASSERT_EQUAL(1, iocb.mask); CU_ASSERT_EQUAL(1, iocb.data_length); CU_ASSERT_EQUAL(msg[6+i]^msg[2+i%4], iocb.data[0]); } CU_ASSERT(WSLAY_ERR_WANT_READ == wslay_frame_recv(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_fragmented(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; /* Unmasked message */ uint8_t msg[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */ 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */ scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = 5; df.feedseq[1] = 4; wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT(3 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(0, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode); CU_ASSERT_EQUAL(3, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(3, iocb.data_length); CU_ASSERT(memcmp("Hel", iocb.data, iocb.data_length) == 0); CU_ASSERT(2 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_CONTINUATION_FRAME, iocb.opcode); CU_ASSERT_EQUAL(2, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(2, iocb.data_length); CU_ASSERT(memcmp("lo", iocb.data, iocb.data_length) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_interleaved_ctrl_frame(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; /* Unmasked message */ uint8_t msg[] = { 0x01, 0x03, 0x48, 0x65, 0x6c, /* "Hel" */ /* ping with "Hello" */ 0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */ scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = 5; df.feedseq[1] = 7, df.feedseq[2] = 4; wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT(3 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(0, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode); CU_ASSERT_EQUAL(3, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(3, iocb.data_length); CU_ASSERT(memcmp("Hel", iocb.data, iocb.data_length) == 0); CU_ASSERT(5 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_PING, iocb.opcode); CU_ASSERT_EQUAL(5, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(5, iocb.data_length); CU_ASSERT(memcmp("Hello", iocb.data, iocb.data_length) == 0); CU_ASSERT(2 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_CONTINUATION_FRAME, iocb.opcode); CU_ASSERT_EQUAL(2, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(2, iocb.data_length); CU_ASSERT(memcmp("lo", iocb.data, iocb.data_length) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_zero_payloadlen(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; /* Unmasked message */ uint8_t msg[] = { 0x81, 0x00 }; /* "" */ scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = 2; wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT(0 == wslay_frame_recv(ctx, &iocb)); CU_ASSERT_EQUAL(1, iocb.fin); CU_ASSERT_EQUAL(0, iocb.rsv); CU_ASSERT_EQUAL(WSLAY_TEXT_FRAME, iocb.opcode); CU_ASSERT_EQUAL(0, iocb.payload_length); CU_ASSERT_EQUAL(0, iocb.mask); CU_ASSERT_EQUAL(0, iocb.data_length); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_too_large_payload(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; uint8_t msg[] = { 0x81, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = sizeof(msg); wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_ctrl_frame_too_large_payload(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; uint8_t msg[] = { 0x88, 0x7e }; scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = sizeof(msg); wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_minimum_ext_payload16(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; uint8_t msg[] = { 0x81, 0x7e, 0x00, 0x7d }; scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = sizeof(msg); wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_recv_minimum_ext_payload64(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { NULL, scripted_recv_callback, NULL }; struct scripted_data_feed df; struct wslay_frame_iocb iocb; uint8_t msg[] = { 0x81, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff}; scripted_data_feed_init(&df, msg, sizeof(msg)); df.feedseq[0] = sizeof(msg); wslay_frame_context_init(&ctx, &callbacks, &df); CU_ASSERT_EQUAL(WSLAY_ERR_PROTO, wslay_frame_recv(ctx, &iocb)); wslay_frame_context_free(ctx); } struct accumulator { uint8_t buf[4096]; size_t length; }; static ssize_t accumulator_send_callback(const uint8_t *buf, size_t len, int flags, void* user_data) { struct accumulator *acc = (struct accumulator*)user_data; assert(acc->length+len < sizeof(acc->buf)); memcpy(acc->buf+acc->length, buf, len); acc->length += len; return len; } static int static_genmask_callback(uint8_t *buf, size_t len, void* user_data) { static const uint8_t makskey[] = { 0x37u, 0xfau, 0x21u, 0x3du }; memcpy(buf, makskey, 4); return 0; } void test_wslay_frame_send(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { accumulator_send_callback, NULL, static_genmask_callback }; struct accumulator acc; struct wslay_frame_iocb iocb; /* Masked text frame containing "Hello" */ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu, 0x4du, 0x51u, 0x58u }; wslay_frame_context_init(&ctx, &callbacks, &acc); memset(&iocb, 0, sizeof(iocb)); acc.length = 0; iocb.fin = 1; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 1; iocb.payload_length = 5; iocb.data = (const uint8_t*)"Hello"; iocb.data_length = 5; CU_ASSERT(5 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg), acc.length); CU_ASSERT(memcmp(msg, acc.buf, sizeof(msg)) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_send_fragmented(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { accumulator_send_callback, NULL, static_genmask_callback }; struct accumulator acc; struct wslay_frame_iocb iocb; /* Unmasked message */ uint8_t msg1[] = { 0x01, 0x03, 0x48, 0x65, 0x6c }; /* "Hel" */ uint8_t msg2[] = { 0x80, 0x02, 0x6c, 0x6f }; /* "lo" */ wslay_frame_context_init(&ctx, &callbacks, &acc); memset(&iocb, 0, sizeof(iocb)); acc.length = 0; iocb.fin = 0; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 0; iocb.payload_length = 3; iocb.data = (const uint8_t*)"Hel"; iocb.data_length = 3; CU_ASSERT(3 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg1), acc.length); CU_ASSERT(memcmp(msg1, acc.buf, sizeof(msg1)) == 0); acc.length = 0; iocb.fin = 1; iocb.opcode = WSLAY_CONTINUATION_FRAME; iocb.payload_length = 2; iocb.data = (const uint8_t*)"lo"; iocb.data_length = 2; CU_ASSERT(2 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg2), acc.length); CU_ASSERT(memcmp(msg2, acc.buf, sizeof(msg2)) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_send_interleaved_ctrl_frame(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { accumulator_send_callback, NULL, static_genmask_callback }; struct accumulator acc; struct wslay_frame_iocb iocb; /* Unmasked message */ /* text with "Hel", with fin = 0 */ uint8_t msg1[] = { 0x01, 0x03, 0x48, 0x65, 0x6c }; /* ping with "Hello" */ uint8_t msg2[] = { 0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f }; /* text with "lo", continuation frame for msg1, with fin = 1 */ uint8_t msg3[] = { 0x80, 0x02, 0x6c, 0x6f }; wslay_frame_context_init(&ctx, &callbacks, &acc); memset(&iocb, 0, sizeof(iocb)); acc.length = 0; iocb.fin = 0; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 0; iocb.payload_length = 3; iocb.data = (const uint8_t*)"Hel"; iocb.data_length = 3; CU_ASSERT(3 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg1), acc.length); CU_ASSERT(memcmp(msg1, acc.buf, sizeof(msg1)) == 0); acc.length = 0; iocb.fin = 1; iocb.opcode = WSLAY_PING; iocb.payload_length = 5; iocb.data = (const uint8_t*)"Hello"; iocb.data_length = 5; CU_ASSERT(5 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg2), acc.length); CU_ASSERT(memcmp(msg2, acc.buf, sizeof(msg2)) == 0); acc.length = 0; iocb.fin = 1; iocb.opcode = WSLAY_CONTINUATION_FRAME; iocb.payload_length = 2; iocb.data = (const uint8_t*)"lo"; iocb.data_length = 2; CU_ASSERT(2 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg3), acc.length); CU_ASSERT(memcmp(msg3, acc.buf, sizeof(msg3)) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_send_1byte_masked(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { scripted_send_callback, NULL, static_genmask_callback }; struct wslay_frame_iocb iocb; /* Masked text frame containing "Hello" */ uint8_t msg[] = { 0x81u, 0x85u, 0x37u, 0xfau, 0x21u, 0x3du, 0x7fu, 0x9fu, 0x4du, 0x51u, 0x58u }; uint8_t hello[] = "Hello"; struct scripted_data_feed df; size_t i; scripted_data_feed_init(&df, NULL, 0); for(i = 0; i < sizeof(msg); ++i) { df.feedseq[i] = 1; } wslay_frame_context_init(&ctx, &callbacks, &df); memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 1; iocb.payload_length = 5; iocb.data = hello; iocb.data_length = sizeof(hello)-1; for(i = 0; i < 5; ++i) { CU_ASSERT_EQUAL(WSLAY_ERR_WANT_WRITE, wslay_frame_send(ctx, &iocb)); } CU_ASSERT_EQUAL(5, wslay_frame_send(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_send_zero_payloadlen(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks = { accumulator_send_callback, NULL, static_genmask_callback }; struct accumulator acc; struct wslay_frame_iocb iocb; /* Unmasked message */ uint8_t msg[] = { 0x81, 0x00 }; /* "" */ acc.length = 0; wslay_frame_context_init(&ctx, &callbacks, &acc); memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 0; iocb.payload_length = 0; iocb.data_length = 0; CU_ASSERT(0 == wslay_frame_send(ctx, &iocb)); CU_ASSERT_EQUAL(sizeof(msg), acc.length); CU_ASSERT(memcmp(msg, acc.buf, sizeof(msg)) == 0); wslay_frame_context_free(ctx); } void test_wslay_frame_send_too_large_payload(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks; struct wslay_frame_iocb iocb; wslay_frame_context_init(&ctx, &callbacks, NULL); memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = WSLAY_TEXT_FRAME; iocb.mask = 0; iocb.payload_length = UINT64_MAX; CU_ASSERT_EQUAL(WSLAY_ERR_INVALID_ARGUMENT, wslay_frame_send(ctx, &iocb)); wslay_frame_context_free(ctx); } void test_wslay_frame_send_ctrl_frame_too_large_payload(void) { wslay_frame_context_ptr ctx; struct wslay_frame_callbacks callbacks; struct wslay_frame_iocb iocb; wslay_frame_context_init(&ctx, &callbacks, NULL); memset(&iocb, 0, sizeof(iocb)); iocb.fin = 1; iocb.opcode = WSLAY_PING; iocb.mask = 0; iocb.payload_length = 1024; CU_ASSERT_EQUAL(WSLAY_ERR_INVALID_ARGUMENT, wslay_frame_send(ctx, &iocb)); wslay_frame_context_free(ctx); } wslay-release-1.1.1/tests/wslay_frame_test.h000066400000000000000000000040641367333100700211610ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_FRAME_TEST_H #define WSLAY_FRAME_TEST_H void test_wslay_frame_context_init(void); void test_wslay_frame_recv(void); void test_wslay_frame_recv_1byte(void); void test_wslay_frame_recv_fragmented(void); void test_wslay_frame_recv_interleaved_ctrl_frame(void); void test_wslay_frame_recv_zero_payloadlen(void); void test_wslay_frame_recv_too_large_payload(void); void test_wslay_frame_recv_ctrl_frame_too_large_payload(void); void test_wslay_frame_recv_minimum_ext_payload16(void); void test_wslay_frame_recv_minimum_ext_payload64(void); void test_wslay_frame_send(void); void test_wslay_frame_send_fragmented(void); void test_wslay_frame_send_interleaved_ctrl_frame(void); void test_wslay_frame_send_1byte_masked(void); void test_wslay_frame_send_zero_payloadlen(void); void test_wslay_frame_send_too_large_payload(void); void test_wslay_frame_send_ctrl_frame_too_large_payload(void); #endif /* WSLAY_FRAME_TEST_H */ wslay-release-1.1.1/tests/wslay_queue_test.c000066400000000000000000000041121367333100700212000ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_queue_test.h" #include #include "wslay_queue.h" void test_wslay_queue(void) { int ints[] = { 1, 2, 3, 4, 5 }; int i; struct wslay_queue *queue = wslay_queue_new(); CU_ASSERT(wslay_queue_empty(queue)); for(i = 0; i < 5; ++i) { wslay_queue_push(queue, &ints[i]); CU_ASSERT_EQUAL(ints[0], *(int*)(wslay_queue_top(queue))); CU_ASSERT(!wslay_queue_empty(queue)); } for(i = 0; i < 5; ++i) { CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue))); wslay_queue_pop(queue); } CU_ASSERT(wslay_queue_empty(queue)); for(i = 0; i < 5; ++i) { wslay_queue_push_front(queue, &ints[i]); CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue))); CU_ASSERT(!wslay_queue_empty(queue)); } for(i = 4; i >= 0; --i) { CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_queue_top(queue))); wslay_queue_pop(queue); } CU_ASSERT(wslay_queue_empty(queue)); wslay_queue_free(queue); } wslay-release-1.1.1/tests/wslay_queue_test.h000066400000000000000000000024061367333100700212110ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_QUEUE_TEST_H #define WSLAY_QUEUE_TEST_H void test_wslay_queue(void); #endif /* WSLAY_QUEUE_TEST_H */ wslay-release-1.1.1/tests/wslay_session_test.c000066400000000000000000000023131367333100700215400ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_session_test.h" #include wslay-release-1.1.1/tests/wslay_session_test.h000066400000000000000000000023531367333100700215510ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_SESSION_TEST_H #define WSLAY_SESSION_TEST_H #endif // WSLAY_SESSION_TEST_H wslay-release-1.1.1/tests/wslay_stack_test.c000066400000000000000000000033571367333100700211730ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 "wslay_stack_test.h" #include #include "wslay_stack.h" void test_wslay_stack() { int ints[] = { 1, 2, 3, 4, 5 }; int i; struct wslay_stack *stack = wslay_stack_new(); CU_ASSERT(wslay_stack_empty(stack)); for(i = 0; i < 5; ++i) { wslay_stack_push(stack, &ints[i]); CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_stack_top(stack))); CU_ASSERT(!wslay_stack_empty(stack)); } for(i = 4; i >= 0; --i) { CU_ASSERT_EQUAL(ints[i], *(int*)(wslay_stack_top(stack))); wslay_stack_pop(stack); } CU_ASSERT(wslay_stack_empty(stack)); wslay_stack_free(stack); } wslay-release-1.1.1/tests/wslay_stack_test.h000066400000000000000000000023771367333100700212010ustar00rootroot00000000000000/* * Wslay - The WebSocket Library * * Copyright (c) 2011, 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 WSLAY_STACK_TEST_H #define WSLAY_STACK_TEST_H void test_wslay_stack(); #endif // WSLAY_STACK_TEST_H wslay-release-1.1.1/wslay-config.cmake.in000066400000000000000000000000621367333100700203010ustar00rootroot00000000000000 include("${CMAKE_CURRENT_LIST_DIR}/wslay.cmake")