From 6aca17b49b95ce24c9de85af9964a656d3cf8d70 Mon Sep 17 00:00:00 2001 From: Camden Narzt Date: Sun, 29 Dec 2024 11:37:30 -0700 Subject: [PATCH] update websocketpp --- .../vendor-modified/websocketpp/COPYING | 4 + .../vendor-modified/websocketpp/changelog.md | 444 ------------ .../vendor-modified/websocketpp/readme.md | 49 -- .../websocketpp/websocketpp/common/cpp11.hpp | 4 +- .../websocketpp/common/functional.hpp | 9 +- .../websocketpp/websocketpp/common/md5.hpp | 2 +- .../websocketpp/websocketpp/connection.hpp | 245 +++++-- .../websocketpp/websocketpp/endpoint.hpp | 8 +- .../websocketpp/websocketpp/error.hpp | 7 +- .../websocketpp/websocketpp/frame.hpp | 11 + .../websocketpp/http/constants.hpp | 659 +++++++++++------- .../websocketpp/http/impl/parser.hpp | 82 ++- .../websocketpp/http/impl/request.hpp | 143 +++- .../websocketpp/http/impl/response.hpp | 183 +++-- .../websocketpp/websocketpp/http/parser.hpp | 55 +- .../websocketpp/websocketpp/http/request.hpp | 42 +- .../websocketpp/websocketpp/http/response.hpp | 71 +- .../websocketpp/impl/connection_impl.hpp | 248 +++++-- .../websocketpp/impl/endpoint_impl.hpp | 140 ++-- .../websocketpp/impl/utilities_impl.hpp | 22 +- .../websocketpp/websocketpp/logger/basic.hpp | 14 +- .../websocketpp/message_buffer/message.hpp | 8 +- .../websocketpp/processors/hybi13.hpp | 34 +- .../websocketpp/roles/client_endpoint.hpp | 8 +- .../websocketpp/roles/server_endpoint.hpp | 178 ++++- .../websocketpp/websocketpp/sha1/sha1.hpp | 2 + .../websocketpp/transport/asio/connection.hpp | 88 ++- .../websocketpp/transport/asio/endpoint.hpp | 295 ++++---- .../transport/asio/security/none.hpp | 17 +- .../transport/asio/security/tls.hpp | 11 +- .../websocketpp/transport/base/endpoint.hpp | 11 + .../websocketpp/transport/stub/endpoint.hpp | 18 +- .../websocketpp/websocketpp/uri.hpp | 497 ++++++++++++- .../websocketpp/websocketpp/utilities.hpp | 7 - .../websocketpp/websocketpp/version.hpp | 6 +- 35 files changed, 2272 insertions(+), 1350 deletions(-) delete mode 100644 src/cxx_supportlib/vendor-modified/websocketpp/changelog.md delete mode 100644 src/cxx_supportlib/vendor-modified/websocketpp/readme.md diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/COPYING b/src/cxx_supportlib/vendor-modified/websocketpp/COPYING index f8cc5ba1b8..14fb71561a 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/COPYING +++ b/src/cxx_supportlib/vendor-modified/websocketpp/COPYING @@ -1,5 +1,7 @@ Main Library: +BSD 3-Clause License + Copyright (c) 2014, Peter Thorson. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -64,6 +66,8 @@ use as a header only library. This conversion was done by Peter Thorson (webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed under the same license as the original, which is listed below. +New BSD License / 3-Clause BSD License + Copyright (c) 2011, Micael Hildenborg All rights reserved. diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/changelog.md b/src/cxx_supportlib/vendor-modified/websocketpp/changelog.md deleted file mode 100644 index 3488a1981e..0000000000 --- a/src/cxx_supportlib/vendor-modified/websocketpp/changelog.md +++ /dev/null @@ -1,444 +0,0 @@ -HEAD - -0.8.2 - 2020-04-19 -- Examples: Update print_client_tls example to remove use of deprecated - OpenSSL functions. -- Compatibility: Removes the use of make_shared in a number of cases where - it would be incompatible with newer versions of ASIO. Thank you Stefan - Floeren for the patch. #810 #814 #862 #843 #794 #808 -- CMake: Update cmake installer to better handle dependencies when using - g++ on MacOS. Thank you Luca Palano for reporting and a patch. #831 -- CMake: Update cmake installer to use a variable for the include directory - improving the ability of the install to be customized. THank you Schrijvers - Luc and Gianfranco Costamanga for reporting and a patch. #842 - -0.8.1 - 2018-07-16 -Note: This release does not change library behavior. It only corrects issues -in the installer and test system. -- Test Suite: Adjust test suite to match behavior introduced in 0.8.0. Thank - you Gianfranco Costamagna for reporting and a patch. #731 -- CMake: Update cmake installer to only install library files globally. - Thank you Gianfraco Costamanga for reporting and a patch. #732 - -0.8.0 - 2018-07-12 -- Examples: Add `print_client` example. This demonstrates a minimal non-TLS - client that connects to a server and prints out the messages it receives. -- Examples: Add `print_client_tls` example. This demonstrates a minimal TLS - client, including basic support via Asio+OpenSSL for certificate chain - and hostname verification. -- Feature: Add getter for all headers to the HTTP parsers. This allows a - wrapping library to enumerate all headers to send upstream. Thank you Jupp - Müller for reporting and an initial pull request. -- Improvement: Move the `socket_init_handler` to execute as a part of `init_asio` - rather than connection `pre_init`. This allows setting of socket options prior - to the bind/listen/accept system calls. Thank you ChristianRobl3D for - reporting #530. -- Improvement: Timers in transport integration tests should only fail if their - own test times out, rather than any test. #643 Thank you Alex Korotkin for - reporting and a patch. -- Improvement: Preserve transport layer error codes in more cases, particularly - during calls to `endpoint::listen`. #652 Thank you vadz for reporting and - patches. -- Compatibility: Make sure the chrono library used by Boost/Asio is in sync - with what the websocketpp is using. Thank you Flow86 for reporting and a - patch. -- Compatibility: Update `telemetry_client` to use a slightly more cross platform - method of sleeping. Should work on windows now. Thank you Meir Yanovich for - reporting. -- Compatibility: Updated permessage-deflate support to reflect that the zlib - library does not actually support a sliding window size of 256 bits. - WebSocket++ will no longer negotiate 256 bit deflate windows. If the user - of the library tries to request a 256 bit window a 512 bit window will be - specified instead (This was the previous behavior). #596 #653 Thank you - Vinnie Falco and Gianfranco Costamagna for reporting. -- Compatibility: Better error handling and logging in cases where extension - requests parse correctly but negotiation fails. -- Compatibility: Removed custom handling of `SSL_R_SHORT_READ` error condition. - This error code no longer exists in modern versions of OpenSSL and causes - a build error. It wasn't being used for anything particularly important - (slightly improving error reporting) and there isn't a great replacement. - #599 Thank you Gianfranco Costamagna for reporting. -- Compatibility: Add missing `` headers. Fixes issues with g++ 5.4.0. - #638 Thank you Alex Korotkin for reporting and a patch. -- Compatibility: Remove the use of `std::auto_ptr` and `std::binary_function` - from builds with C++11 or later. These features are deprecated and were - removed entirely in C++17. This change allows building WebSocket++ on - C++17 compilers. #592 Thank you Michal Fojtak for reporting and a patch -- Compatibility: Add 1014 close code and adds missing descriptions for codes - 1012 and 1013. #589 Thank you jbwdevries and ronneke1996 for reporting and - patches. -- Compatibility: Add hooks to support `mingw-std-threads` C++11 thread and mutex - polyfill library as an alternative to Boost. #608 Thank you Peter Taylor for - reporting and an initial patch. -- Compatibility: Changed the handshake connection token to 'Upgrade' from - 'upgrade'. Technically this header is supposed to be processed case - insensitively. In practice, there are browsers (such as Edge) that don't do - this and they tend to use the uppercase value used as an example in RFC6455. - Thank you Johann Bauer for reporting and a patch. #727 -- Bug: Store loggers in shared pointers to avoid crashes related to connections - trying to write logs entries after their respective endpoint has been - deallocated. Thank you Thalhammer for reporting and Jupp Müller for the - patch. #539 #501 -- Bug: Change default listen backlog from 0 to `socket_base::max_connections`. - #549. Thank you derwassi and zwelab for reporting and na1pir for providing - access to hardware to debug the issue. -- Bug: Fix a crash in the accept loop when `get_connection` fails. #551 Thank you - Walter Gray for a patch. -- Bug/Documentation: Fix incorrect example code that used - `websocketpp::lib::error_code` instead of `websocketpp::exception`. Thank you - heretic13 for reporting -- Bug: Fix uninitialized shared pointer in Asio transport test suite. #647 - Thank you Alex Korotkin for reporting and a patch. -- Bug: Fix a thread safety issue in the permessage-deflate extension that - caused message corruption when sending compressed messages from a different - thread than the main I/O thread. #615 Thank you KyleNyenhuis and Pieter De - Gendt for reporting and a patch. -- Bug: Fix an undefined behavior issue performing a 64 bit wide shift on a 64 - bit value. #636 Thank you Gregor Jasny for reporting and a patch -- Bug: Fix some compile issues with ASIO_STANDALONE. #662 #665 Thank you - chronoxor and Guillaume Egles for reporting and patches. - -0.7.0 - 2016-02-22 -- MINOR BREAKING SOCKET POLICY CHANGE: Asio transport socket policy method - `cancel_socket` will now return `lib::asio::error_code` instead of `void`. - Custom Asio transport socket policies will need to be updated accordingly. - This does not affect anyone using the bundled socket policies. -- Feature: Basic support for the permessage-deflate extension. #344 -- Feature: Allow accessing the local endpoint when using the Asio transport. - This allows inspection of the address and port in cases where they are chosen - by the operating system rather than the user. Thank you Andreas Weis and - Muzahid Hussain for reporting and related code. #458 -- Feature: Add support for subprotocols in Hybi00. Thank you Lukas Obermann - for reporting and a patch. #518 -- Feature: Adds `tcp_pre_bind handler` to Asio transport. This allows setting - arbitrary socket options after the listen acceptor has been created but before - the socket bind has been performed. #634 #439 Thank you Gregor Jasny for - the patch. -- Improvement: Better automatic std::chrono feature detection for Visual Studio -- Improvement: Major refactoring to bundled CMake build system. CMake can now be - used to build all of the examples and the test suite. Thank you Thijs Wenker - for a significant portion of this code. #378, #435, #449 -- Improvement: In build environments where `lib::error_code` and - `lib::asio::error_code` match (such as using `boost::asio` with - `boost::system_error` or standalone asio with `std::system_error`, transport - errors are passed through natively rather than being reported as a translated - `pass_through` error type. -- Improvement: Add a `get_transport_error` method to Asio transport connections - to allow retrieving a machine readable native transport error. -- Improvement: Add `connection::get_response`, `connection::get_response_code`, - and `connection::get_response_msg` methods to allow accessing additional - information about the HTTP responses that WebSocket++ sends. #465 Thank you - Flow86 for reporting. -- Improvement: Removes use of empty strings ("") in favor of `string::clear()` - and `string::empty()`. This avoids generating unnecessary temporary objects. - #468 Thank you Vladislav Yaroslavlev for reporting and a patch. -- Documentation: Adds an example demonstrating the use of external `io_service` -- Documentation: Adds a simple `echo_client` example. -- Documentation: Begins migration of the web based user manual into Doxygen. -- Bug: Fix memory leak when `init_asio` produces an error. #454 Thank you Mark - Grimes for reporting and fixing. -- Bug: Fix crash when processing a specially crafted HTTP header. Thank you Eli - Fidler for reporting, test cases, and a patch. #456 -- Bug: Fix an issue where standalone Asio builds that use TLS would not compile - due to lingering boost code. #448 Thank you mjsp for reporting -- Bug: Fix an issue where canceling a socket could throw an exception on some - older Windows XP platforms. It now prints an appropriate set of log messages - instead. Thank you Thijs Wenker for reporting and researching solutions. #460 -- Bug: Fix an issue where deferred HTTP connections that start sending a very - long response before their HTTP handler ends would result in a second set of - HTTP headers being injected into the output. Thank you Kevin Smith for - reporting and providing test case details. #443 -- Bug: Fix an issue where the wrong type of strand was being created. Thank you - Bastien Brunnenstein for reporting and a patch. #462 -- Bug: Fix an issue where TLS includes were broken for Asio Standalone builds. - Thank you giachi and Bastien Brunnenstein for reporting. #491 -- Bug: Remove the use of cached read and write handlers in the Asio transport. - This feature caused memory leaks when the `io_service` the connection was - running on was abruptly stopped. There isn't a clean and safe way of using - this optimization without global state and the associated locks. The locks - perform worse. Thank you Xavier Gibert for reporting, test cases, and code. - Fixes #490. -- Bug: Fix a heap buffer overflow when checking very short URIs. Thank you - Xavier Gibert for reporting and a patch #524 -- Compatibility: Fixes a number of build & config issues on Visual Studio 2015 -- Compatibility: Removes non-standards compliant masking behavior. #395, #469 -- Compatibility: Replace deprecated use of `auto_ptr` on systems where - `unique_ptr` is available. - -0.6.0 - 2015-06-02 -- MINOR BREAKING TRANSPORT POLICY CHANGE: Custom transport policies will now be - required to include a new method `void set_uri(uri_ptr u)`. An implementation - is not required. The stub transport policy includes an example stub method - that can be added to any existing custom transport policy to fulfill this - requirement. This does not affect anyone using the bundled transports or - configs. -- MINOR BREAKING SOCKET POLICY CHANGE: Custom asio transport socket policies - will now be required to include a new method `void set_uri(uri_ptr u)`. Like - with the transport layer, an implementation is not required. This does not - affect anyone using the bundled socket policies. -- MINOR BREAKING DEPENDENCY CHANGE: When using Boost versions greater than or - equal to 1.49 in C++03 mode, `libboost-chrono` is needed now instead of - `libboost-date_time`. Users with C++11 compilers or using Boost versions 1.48 - and earlier are not affected. Note: This change affects the bundled unit test - suite. -- Feature: WebSocket++ Asio transport policy can now be used with the standalone - version of Asio (1.8.0+) when a C++11 compiler and standard library are - present. This means that it is possible now to use WebSocket++'s Asio - transport entirely without Boost. Thank you Robert Seiler for proof of concept - code that was used as a guide for this implementation. Fixes #324 -- Feature: Adds a vectored/scatter-gather write handler to the iostream - transport. -- Feature: Adds the ability to defer sending an HTTP response until sometime - after the `http_handler` is run. This allows processing of long running http - handlers to defer their response until it is ready without blocking the - network thread. references #425 -- Improvement: `echo_server_tls` has been update to demonstrate how to configure - it for Mozilla's recommended intermediate and modern TLS security profiles. -- Improvement: `endpoint::set_timer` now uses a steady clock provided by - `boost::chrono` or `std::chrono` where available instead of the non-monotonic - system clock. Thank you breyed for reporting. fixes #241 -- Improvement: Outgoing TLS connections to servers using the SNI extension to - choose a certificate will now work. Thank you moozzyk for reporting. - Fixes #400 -- Improvement: Removes an unnecessary mutex lock in `get_con_from_hdl`. -- Cleanup: Asio transport policy has been refactored to remove many Boost - dependencies. On C++03 compilers the `boost::noncopyable` dependency has been - removed and the `boost::date_time` dependency has been replaced with the newer - `boost::chrono` when possible. On C++11 compilers the `boost::aligned_storage` - and `boost::date_time` dependencies are gone, replaced with equivalent C++11 - standard library features. -- Bug: Fixes a potential dangling pointer and inconsistent error message - handling in `websocketpp::exception`. #432 Thank you Tom Swirly for the fix. - -0.5.1 - 2015-02-27 -- Bug: Fixes an issue where some frame data was counted against the max header - size limit, resulting in connections that included a lot of frame data - immediately after the opening handshake to fail. -- Bug: Fix a typo in the name of the set method for `max_http_body_size`. #406 - Thank you jplatte for reporting. - -0.5.0 - 2015-01-22 -- BREAKING UTILITY CHANGE: Deprecated methods `http::parser::parse_headers`, - `http::response::parse_complete`, and `http::request::parse_complete` have - been removed. -- Security: Disabled SSLv3 in example servers. -- Feature: Adds basic support for accessing HTTP request bodies in the http - handler. #181 -- Feature: Adds the ability to register a shutdown handler when using the - iostream transport. This provides a clean interface for triggering the shut - down of external sockets and other cleanup without hooking in to higher level - WebSocket handlers. -- Feature: Adds the ability to register a write handler when using the iostream - transport. This handler can be used to handle transport output in place of - registering an ostream to write to. -- Feature: Adds a new logging policy that outputs to syslog. #386 Thank you Tom - Hughes for submitting the initial version of this policy. -- Improvement: Message payload logging now prints text for text messages rather - than binary. -- Improvement: Overhaul of handshake state machine. Should make it impossible - for exceptions to bubble out of transport methods like `io_service::run`. -- Improvement: Overhaul of handshake error reporting. Fail handler error codes - will be more detailed and precise. Adds new [fail] and [http] logging channels - that log failed websocket connections and successful HTTP connections - respectively. A new aggregate channel package, `alevel::access_core`, allows - enabling connect, disconnect, fail, and http together. Successful HTTP - connections will no longer trigger a fail handler. -- Improvement: Ability to terminate connection during an http handler to cleanly - suppress the default outgoing HTTP response. -- Documentation: Add Sending & Receiving Messages step to chapter one of the - `utility_client` tutorial. Update `utility_client` example to match. -- Cleanup: Removes unused files & STL includes. Adds required STL includes. - Normalizes include order. -- Bug: Fixes a fatal state error when a handshake response is completed - immediately after that handshake times out. #389 -- Bug: MinGW fixes; C++11 feature detection, localtime use. #393 Thank you - Schebb for reporting, code, and testing. -- Bug: Fixes an issue where `websocketpp::exception::what()` could return an out - of scope pointer. #397 Thank you fabioang for reporting. -- Bug: Fixes an issue where endpoints were not reset properly after a call to - `endpoint::listen` failed. #390 Thank you wyyqyl for reporting. - -0.4.0 - 2014-11-04 -- BREAKING API CHANGE: All WebSocket++ methods now throw an exception of type - `websocketpp::exception` which derives from `std::exception`. This normalizes - all exception types under the standard exception hierarchy and allows - WebSocket++ exceptions to be caught in the same statement as others. The error - code that was previously thrown is wrapped in the exception object and can be - accessed via the `websocketpp::exception::code()` method. -- BREAKING API CHANGE: Custom logging policies have some new required - constructors that take generic config settings rather than pointers to - std::ostreams. This allows writing logging policies that do not involve the - use of std::ostream. This does not affect anyone using the built in logging - policies. -- BREAKING UTILITY CHANGE: `websocketpp::lib::net::htonll` and - `websocketpp::lib::net::ntohll` have been prefixed with an underscore to avoid - conflicts with similarly named macros in some operating systems. If you are - using the WebSocket++ provided 64 bit host/network byte order functions you - will need to switch to the prefixed versions. -- BREAKING UTILITY CHANGE: The signature of `base64_encode` has changed from - `websocketpp::base64_encode(unsigned char const *, unsigned int)` to - `websocketpp::base64_encode(unsigned char const *, size_t)`. -- BREAKING UTILITY CHANGE: The signature of `sha1::calc` has changed from - `websocketpp::sha1::calc(void const *, int, unsigned char *)` to - `websocketpp::sha1::calc(void const *, size_t, unsigned char *)` -- Feature: Adds incomplete `minimal_server` and `minimal_client` configs that - can be used to build custom configs without pulling in the dependencies of - `core` or `core_client`. These configs will offer a stable base config to - future-proof custom configs. -- Improvement: Core library no longer has std::iostream as a dependency. - std::iostream is still required for the optional iostream logging policy and - iostream transport. -- Bug: C++11 Chrono support was being incorrectly detected by the `boost_config` - header. Thank you Max Dmitrichenko for reporting and a patch. -- Bug: use of `std::put_time` is now guarded by a unique flag rather than a - chrono library flag. Thank you Max Dmitrichenko for reporting. -- Bug: Fixes non-thread safe use of std::localtime. #347 #383 -- Compatibility: Adjust usage of std::min to be more compatible with systems - that define a min(...) macro. -- Compatibility: Removes unused parameters from all library, test, and example - code. This assists with those developing with -Werror and -Wunused-parameter - #376 -- Compatibility: Renames ntohll and htonll methods to avoid conflicts with - platform specific macros. #358 #381, #382 Thank you logotype, unphased, - svendjo -- Cleanup: Removes unused functions, fixes variable shadow warnings, normalizes - all whitespace in library, examples, and tests to 4 spaces. #376 - -0.3.0 - 2014-08-10 -- Feature: Adds `start_perpetual` and `stop_perpetual` methods to asio transport - These may be used to replace manually managed `asio::io_service::work` objects -- Feature: Allow setting pong and handshake timeouts at runtime. -- Feature: Allows changing the listen backlog queue length. -- Feature: Split tcp init into pre and post init. -- Feature: Adds URI method to extract query string from URI. Thank you Banaan - for code. #298 -- Feature: Adds a compile time switch to asio transport config to disable - certain multithreading features (some locks, asio strands) -- Feature: Adds the ability to pause reading on a connection. Paused connections - will not read more data from their socket, allowing TCP flow control to work - without blocking the main thread. -- Feature: Adds the ability to specify whether or not to use the `SO_REUSEADDR` - TCP socket option. The default for this value has been changed from `true` to - `false`. -- Feature: Adds the ability to specify a maximum message size. -- Feature: Adds `close::status::get_string(...)` method to look up a human - readable string given a close code value. -- Feature: Adds `connection::read_all(...)` method to iostream transport as a - convenience method for reading all data into the connection buffer without the - end user needing to manually loop on `read_some`. -- Improvement: Open, close, and pong timeouts can be disabled entirely by - setting their duration to 0. -- Improvement: Numerous performance improvements. Including: tuned default - buffer sizes based on profiling, caching of handler binding for async - reads/writes, non-malloc allocators for read/write handlers, disabling of a - number of questionably useful range sanity checks in tight inner loops. -- Improvement: Cleaned up the handling of TLS related errors. TLS errors will - now be reported with more detail on the info channel rather than all being - `tls_short_read` or `pass_through`. In addition, many cases where a TLS short - read was in fact expected are no longer classified as errors. Expected TLS - short reads and quasi-expected socket shutdown related errors will no longer - be reported as unclean WebSocket shutdowns to the application. Information - about them will remain in the info error channel for debugging purposes. -- Improvement: `start_accept` and `listen` errors are now reported to the caller - either via an exception or an ec parameter. -- Improvement: Outgoing writes are now batched for improved message throughput - and reduced system call and TCP frame overhead. -- Bug: Fix some cases of calls to empty lib::function objects. -- Bug: Fix memory leak of connection objects due to cached handlers holding on to - reference counted pointers. #310 Thank you otaras for reporting. -- Bug: Fix issue with const endpoint accessors (such as `get_user_agent`) not - compiling due to non-const mutex use. #292 Thank you logofive for reporting. -- Bug: Fix handler allocation crash with multithreaded `io_service`. -- Bug: Fixes incorrect whitespace handling in header parsing. #301 Thank you - Wolfram Schroers for reporting -- Bug: Fix a crash when parsing empty HTTP headers. Thank you Thingol for - reporting. -- Bug: Fix a crash following use of the `stop_listening` function. Thank you - Thingol for reporting. -- Bug: Fix use of variable names that shadow function parameters. The library - should compile cleanly with -Wshadow now. Thank you giszo for reporting. #318 -- Bug: Fix an issue where `set_open_handshake_timeout` was ignored by server - code. Thank you Robin Rowe for reporting. -- Bug: Fix an issue where custom timeout values weren't being propagated from - endpoints to new connections. -- Bug: Fix a number of memory leaks related to server connection failures. #323 - #333 #334 #335 Thank you droppy and aydany for reporting and patches. - reporting. -- Compatibility: Fix compile time conflict with Visual Studio's MIN/MAX macros. - Thank you Robin Rowe for reporting. -- Documentation: Examples and test suite build system now defaults to clang on - OS X - -0.3.0-alpha4 - 2013-10-11 -- HTTP requests ending normally are no longer logged as errors. Thank you Banaan - for reporting. #294 -- Eliminates spurious expired timers in certain error conditions. Thank you - Banaan for reporting. #295 -- Consolidates all bundled library licenses into the COPYING file. #294 -- Updates bundled sha1 library to one with a cleaner interface and more - straight-forward license. Thank you lotodore for reporting and Evgeni Golov - for reviewing. #294 -- Re-introduces strands to asio transport, allowing `io_service` thread pools to - be used (with some limitations). -- Removes endpoint code that kept track of a connection list that was never used - anywhere. Removes a lock and reduces connection creation/deletion complexity - from O(log n) to O(1) in the number of connections. -- A number of internal changes to transport APIs -- Deprecates iostream transport `readsome` in favor of `read_some` which is more - consistent with the naming of the rest of the library. -- Adds preliminary signaling to iostream transport of eof and fatal transport - errors -- Updates transport code to use shared pointers rather than raw pointers to - prevent asio from retaining pointers to connection methods after the - connection goes out of scope. #293 Thank you otaras for reporting. -- Fixes an issue where custom headers couldn't be set for client connections - Thank you Jerry Win and Wolfram Schroers for reporting. -- Fixes a compile error on visual studio when using interrupts. Thank you Javier - Rey Neira for reporting this. -- Adds new 1012 and 1013 close codes per IANA registry -- Add `set_remote_endpoint` method to iostream transport. -- Add `set_secure` method to iostream transport. -- Fix typo in .gitattributes file. Thank you jstarasov for reporting this. #280 -- Add missing locale include. Thank you Toninoso for reporting this. #281 -- Refactors `asio_transport` endpoint and adds full documentation and exception - free varients of all methods. -- Removes `asio_transport` endpoint method cancel(). Use `stop_listen()` instead -- Wrap internal `io_service` `run_one()` method -- Suppress error when trying to shut down a connection that was already closed - -0.3.0-alpha3 - 2013-07-16 -- Minor refactor to bundled sha1 library -- HTTP header comparisons are now case insensitive. #220, #275 -- Refactors URI to be exception free and not use regular expressions. This - eliminates the dependency on boost or C++11 regex libraries allowing native - C++11 usage on GCC 4.4 and higher and significantly reduces staticly built - binary sizes. -- Updates handling of Server and User-Agent headers to better handle custom - settings and allow suppression of these headers for security purposes. -- Fix issue where pong timeout handler always fired. Thank you Steven Klassen - for reporting this bug. -- Add ping and pong endpoint wrapper methods -- Add `get_request()` pass through method to connection to allow calling methods - specific to the HTTP policy in use. -- Fix issue compile error with `WEBSOCKETPP_STRICT_MASKING` enabled and another - issue where `WEBSOCKETPP_STRICT_MASKING` was not applied to incoming messages. - Thank you Petter Norby for reporting and testing these bugs. #264 -- Add additional macro guards for use with boost_config. Thank you breyed - for testing and code. #261 - -0.3.0-alpha2 - 2013-06-09 -- Fix a regression that caused servers being sent two close frames in a row - to end a connection uncleanly. #259 -- Fix a regression that caused spurious frames following a legitimate close - frames to erroneously trigger handlers. #258 -- Change default HTTP response error code when no http_handler is defined from - 500/Internal Server Error to 426/Upgrade Required -- Remove timezone from logger timestamp to work around issues with the Windows - implementation of strftime. Thank you breyed for testing and code. #257 -- Switch integer literals to char literals to improve VCPP compatibility. - Thank you breyed for testing and code. #257 -- Add MSVCPP warning suppression for the bundled SHA1 library. Thank you breyed - for testing and code. #257 - -0.3.0-alpha1 - 2013-06-09 -- Initial Release diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/readme.md b/src/cxx_supportlib/vendor-modified/websocketpp/readme.md deleted file mode 100644 index 6cc608516f..0000000000 --- a/src/cxx_supportlib/vendor-modified/websocketpp/readme.md +++ /dev/null @@ -1,49 +0,0 @@ -WebSocket++ (0.8.2) -========================== - -WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket -Protocol. It allows integrating WebSocket client and server functionality into -C++ programs. It uses interchangeable network transport modules including one -based on raw char buffers, one based on C++ iostreams, and one based on Asio -(either via Boost or standalone). End users can write additional transport -policies to support other networking or event libraries as needed. - -Major Features -============== -* Full support for RFC6455 -* Partial support for Hixie 76 / Hybi 00, 07-17 draft specs (server only) -* Message/event based interface -* Supports secure WebSockets (TLS), IPv6, and explicit proxies. -* Flexible dependency management (C++11 Standard Library or Boost) -* Interchangeable network transport modules (raw, iostream, Asio, or custom) -* Portable/cross platform (Posix/Windows, 32/64bit, Intel/ARM/PPC) -* Thread-safe - -Get Involved -============ - -[![Build Status](https://travis-ci.org/zaphoyd/websocketpp.png)](https://travis-ci.org/zaphoyd/websocketpp) - -**Project Website** -http://www.zaphoyd.com/websocketpp/ - -**User Manual** -http://docs.websocketpp.org/ - -**GitHub Repository** -https://github.com/zaphoyd/websocketpp/ - -GitHub pull requests should be submitted to the `develop` branch. - -**Announcements Mailing List** -http://groups.google.com/group/websocketpp-announcements/ - -**IRC Channel** - #websocketpp (freenode) - -**Discussion / Development / Support Mailing List / Forum** -http://groups.google.com/group/websocketpp/ - -Author -====== -Peter Thorson - websocketpp@zaphoyd.com diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/cpp11.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/cpp11.hpp index 492a3b8ff9..b5f3d5d2a5 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/cpp11.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/cpp11.hpp @@ -88,9 +88,11 @@ #endif #ifndef __GNUC__ - // GCC as of version 4.9 (latest) does not support std::put_time yet. + // GCC as of version 4.9 does not support std::put_time yet. // so ignore it #define _WEBSOCKETPP_PUTTIME_ + + // todo: std::put_time may be present in version 5+ #endif #else // In the absence of a blanket define, try to use compiler versions or diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/functional.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/functional.hpp index 25ed933987..d332dd15ec 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/functional.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/functional.hpp @@ -52,7 +52,7 @@ #ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_ #include #else - #include + #include #include #include #endif @@ -83,7 +83,12 @@ namespace lib { using boost::function; using boost::bind; using boost::ref; - namespace placeholders = boost::placeholders; + namespace placeholders { + /// \todo this feels hacky, is there a better way? + using ::_1; + using ::_2; + using ::_3; + } // See above definition for more details on what this is and why it exists #define _WEBSOCKETPP_REF(x) boost::ref(x) diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/md5.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/md5.hpp index e11ef177dd..279725f453 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/md5.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/common/md5.hpp @@ -198,7 +198,7 @@ static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) { * On little-endian machines, we can process properly aligned * data without copying it. */ - if (!((reinterpret_cast(data)) & 3)) { + if (!((data - (md5_byte_t const *)0) & 3)) { /* data are properly aligned */ X = (md5_word_t const *)data; } else { diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/connection.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/connection.hpp index d019fce339..1d4b30636e 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/connection.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/connection.hpp @@ -892,13 +892,12 @@ class connection * get_subprotocol(). Subprotocol requests should be added in order of * preference. * - * @param request The subprotocol to request - * @param ec A reference to an error code that will be filled in the case of - * errors + * @param[in] request The subprotocol to request + * @param[out] ec A status code describing the outcome of the operation */ void add_subprotocol(std::string const & request, lib::error_code & ec); - /// Adds the given subprotocol string to the request list + /// Adds the given subprotocol string to the request list (exception) /** * Adds a subprotocol to the list to send with the opening handshake. This * may be called multiple times to request more than one. If the server @@ -907,7 +906,7 @@ class connection * get_subprotocol(). Subprotocol requests should be added in order of * preference. * - * @param request The subprotocol to request + * @param[in] request The subprotocol to request */ void add_subprotocol(std::string const & request); @@ -920,13 +919,12 @@ class connection * * This member function is valid on server endpoints/connections only * - * @param value The subprotocol to select - * @param ec A reference to an error code that will be filled in the case of - * errors + * @param[in] value The subprotocol to select + * @param[out] ec A status code describing the outcome of the operation */ void select_subprotocol(std::string const & value, lib::error_code & ec); - /// Select a subprotocol to use + /// Select a subprotocol to use (exception) /** * Indicates which subprotocol should be used for this connection. Valid * only during the validate handler callback. Subprotocol selected must have @@ -935,7 +933,7 @@ class connection * * This member function is valid on server endpoints/connections only * - * @param value The subprotocol to select + * @param[in] value The subprotocol to select */ void select_subprotocol(std::string const & value); @@ -947,7 +945,7 @@ class connection /** * Retrieve the value of a header from the handshake HTTP request. * - * @param key Name of the header to get + * @param[in] key Name of the header to get * @return The value of the header */ std::string const & get_request_header(std::string const & key) const; @@ -967,7 +965,7 @@ class connection /** * Retrieve the value of a header from the handshake HTTP request. * - * @param key Name of the header to get + * @param[in] key Name of the header to get * @return The value of the header */ std::string const & get_response_header(std::string const & key) const; @@ -996,7 +994,7 @@ class connection return m_response.get_status_msg(); } - /// Set response status code and message + /// Set response status code and message (exception free) /** * Sets the response status code to `code` and looks up the corresponding * message for standard codes. Non-standard codes will be entered as Unknown @@ -1006,13 +1004,56 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * - * @param code Code to set - * @param msg Message to set - * @see websocketpp::http::response::set_status + * @since 0.9.0 + * + * @param[in] code Code to set + * @param[in] msg Message to set + * @param[out] ec A status code describing the outcome of the operation + * @see websocketpp::http::parser::response::set_status + * @see websocketpp::http::status_code::value (list of valid codes) + */ + void set_status(http::status_code::value code, lib::error_code & ec); + +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Set response status code and message (exception) + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param[in] code Code to set + * @param[in] msg Message to set + * @throw websocketpp::exception + * @see websocketpp::http::parser::response::set_status() + * @see websocketpp::http::status_code::value (list of valid codes) */ void set_status(http::status_code::value code); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + + /// Set response status code and message (exception free) + /** + * Sets the response status code and message to independent custom values. + * use set_status(status_code::value) to set the code and have the standard + * message be automatically set. + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @since 0.9.0 + * + * @param[in] code Code to set + * @param[in] msg Message to set + * @param[out] ec A status code describing the outcome of the operation + * @see websocketpp::http::response::set_status() + * @see websocketpp::http::status_code::value (list of valid codes) + */ + void set_status(http::status_code::value code, std::string const & msg, + lib::error_code & ec); - /// Set response status code and message + /// Set response status code and message (exception) /** * Sets the response status code and message to independent custom values. * use set_status(status_code::value) to set the code and have the standard @@ -1021,13 +1062,15 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * - * @param code Code to set - * @param msg Message to set - * @see websocketpp::http::response::set_status + * @param[in] code Code to set + * @param[in] msg Message to set + * @throw websocketpp::exception + * @see websocketpp::http::parser::response::set_status() + * @see websocketpp::http::status_code::value (list of valid codes) */ void set_status(http::status_code::value code, std::string const & msg); - /// Set response body content + /// Set response body content (exception free) /** * Set the body content of the HTTP response to the parameter string. Note * set_body will also set the Content-Length HTTP header to the appropriate @@ -1037,51 +1080,159 @@ class connection * This member function is valid only from the http() and validate() handler * callbacks. * - * @param value String data to include as the body content. + * @since 0.9.0 + * + * @param[in] value String data to include as the body content. + * @param[out] ec A status code describing the outcome of the operation * @see websocketpp::http::response::set_body + * @see set_body(std::string const &) (exception version) + */ + void set_body(std::string const & value, lib::error_code & ec); + +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Set response body content (exception) + /** + * Set the body content of the HTTP response to the parameter string. Note + * set_body will also set the Content-Length HTTP header to the appropriate + * value. If you want the Content-Length header to be something else set it + * to something else after calling set_body + * + * This member function is valid only from the http() and validate() handler + * callbacks. + * + * @param[in] value String data to include as the body content. + * @throw websocketpp::exception + * @see websocketpp::http::response::set_body + * @see set_body(std::string const &, lib::error_code &) + * (exception free version) */ void set_body(std::string const & value); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Append a header +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// @copydoc websocketpp::connection::set_body(std::string const &, lib::error_code &) + void set_body(std::string && value, lib::error_code & ec); + +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + /// @copydoc websocketpp::connection::set_body(std::string const &) + void set_body(std::string && value); +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + + /// Append a header (exception free) /** - * If a header with this name already exists the value will be appended to - * the existing header to form a comma separated list of values. Use + * Set the value of a header in the handshake HTTP request or response. If + * a header with this name already exists the value will be appended to the + * existing header to form a comma separated list of values. Use * `connection::replace_header` to overwrite existing values. * - * This member function is valid only from the http() and validate() handler - * callbacks, or to a client connection before connect has been called. + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called + * + * @since 0.9.0 + * + * @param[in] key Name of the header to set + * @param[in] val Value to add + * @param[out] ec A status code describing the outcome of the operation + * @see connection::replace_header + * @see websocketpp::http::parser::parser::append_header + * @see append_header(std::string const &, std::string const &) + * (exception version) + */ + void append_header(std::string const & key, std::string const & val, + lib::error_code & ec); + + /// Append a header (exception) + /** + * Set the value of a header in the handshake HTTP request or response. If + * a header with this name already exists the value will be appended to the + * existing header to form a comma separated list of values. Use + * `connection::replace_header` to overwrite existing values. + * + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called * - * @param key Name of the header to set - * @param val Value to add - * @see replace_header - * @see websocketpp::http::parser::append_header + * @param[in] key Name of the header to set + * @param[in] val Value to add + * @throw websocketpp::exception + * @see connection::replace_header + * @see websocketpp::http::parser::parser::append_header + * @see append_header(std::string const &, std::string const &, + * lib::error_code &) (exception free version) */ void append_header(std::string const & key, std::string const & val); - /// Replace a header + /// Replace a header (exception free) /** - * If a header with this name already exists the old value will be replaced + * Set the value of a header in the handshake HTTP request or response. If + * a header with this name already exists the old value will be replaced * Use `connection::append_header` to append to a list of existing values. * - * This member function is valid only from the http() and validate() handler - * callbacks, or to a client connection before connect has been called. + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called + * + * @param[in] key Name of the header to set + * @param[in] val Value to set + * @param[out] ec A status code describing the outcome of the operation + * @see connection::append_header + * @see websocketpp::http::parser::parser::replace_header + * @see replace_header(std::string const &, std::string const &) + * (exception version) + */ + void replace_header(std::string const & key, std::string const & val, + lib::error_code & ec); + + /// Replace a header (exception) + /** + * Set the value of a header in the handshake HTTP request or response. If + * a header with this name already exists the old value will be replaced + * Use `connection::append_header` to append to a list of existing values. * - * @param key Name of the header to set - * @param val Value to set - * @see append_header - * @see websocketpp::http::parser::replace_header + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called + * + * @param[in] key Name of the header to set + * @param[in] val Value to set + * @throw websocketpp::exception + * @see connection::append_header + * @see websocketpp::http::parser::parser::replace_header + * @see replace_header(std::string const & key, std::string const & val, + * lib::error_code & ec) (exception free version) */ void replace_header(std::string const & key, std::string const & val); - /// Remove a header + /// Remove a header (exception free) /** - * Removes a header from the response. + * Removes a header from the handshake HTTP request or response. * - * This member function is valid only from the http() and validate() handler - * callbacks, or to a client connection before connect has been called. + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called + * + * @param[in] key The name of the header to remove + * @param[out] ec A status code describing the outcome of the operation + * @see websocketpp::http::parser::parser::remove_header + * @see remove_header(std::string const &) (exception version) + */ + void remove_header(std::string const & key, lib::error_code & ec); + + /// Remove a header (exception) + /** + * Removes a header from the handshake HTTP request or response. + * + * *When can this member function be called?* + * - Servers: Valid from the http and validate handlers + * - Clients: Valid before websocketpp::client::connect() has been called * - * @param key The name of the header to remove - * @see websocketpp::http::parser::remove_header + * @param[in] key The name of the header to remove + * @throw websocketpp::exception + * @see websocketpp::http::parser::parser::remove_header + * @see remove_header(std::string const &, lib::error_code &) + * (exception free version) */ void remove_header(std::string const & key); @@ -1125,8 +1276,8 @@ class connection /// Defer HTTP Response until later (Exception free) /** * Used in the http handler to defer the HTTP response for this connection - * until later. Handshake timers will be canceled and the connection will be - * left open until `send_http_response` or an equivalent is called. + * until later. Handshake timers will be canceled and the connection will + * be left open until `send_http_response` or an equivalent is called. * * Warning: deferred connections won't time out and as a result can tie up * resources. diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/endpoint.hpp index c124b1d9ab..a26d14dfc2 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/endpoint.hpp @@ -28,6 +28,8 @@ #ifndef WEBSOCKETPP_ENDPOINT_HPP #define WEBSOCKETPP_ENDPOINT_HPP +#include + #include #include @@ -109,7 +111,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { /// Destructor - ~endpoint() {} + ~endpoint() {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no copy constructor because endpoints are not copyable @@ -649,6 +651,7 @@ class endpoint : public config::transport_type, public config::endpoint_base { return con; } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Retrieves a connection_ptr from a connection_hdl (exception version) connection_ptr get_con_from_hdl(connection_hdl hdl) { lib::error_code ec; @@ -658,8 +661,9 @@ class endpoint : public config::transport_type, public config::endpoint_base { } return con; } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ protected: - connection_ptr create_connection(); + connection_ptr create_connection(lib::error_code & ec); lib::shared_ptr m_alog; lib::shared_ptr m_elog; diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/error.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/error.hpp index 562fb6ed71..20f409dced 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/error.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/error.hpp @@ -143,7 +143,10 @@ enum value { http_parse_error, /// Extension negotiation failed - extension_neg_failed + extension_neg_failed, + + /// General transport error, consult more specific transport error code + transport_error }; // enum value @@ -221,6 +224,8 @@ class category : public lib::error_category { return "HTTP parse error"; case error::extension_neg_failed: return "Extension negotiation failed"; + case error::transport_error: + return "An error occurred in the underlying transport. Consult transport error code for more details."; default: return "Unknown"; } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/frame.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/frame.hpp index 95178c3df9..18a990b366 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/frame.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/frame.hpp @@ -120,6 +120,17 @@ namespace opcode { (v >= control_rsvb && v <= control_rsvf); } + /// Check if an opcode is invalid + /** + * Invalid opcodes are negative or require greater than 4 bits to store. + * + * @param v The opcode to test. + * @return Whether or not the opcode is invalid. + */ + inline bool invalid(value v) { + return (v > 0xF || v < 0); + } + /// Check if an opcode is for a control frame /** * @param v The opcode to test. diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/constants.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/constants.hpp index f946cb3178..02382f1010 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/constants.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/constants.hpp @@ -34,275 +34,428 @@ #include #include +#include + namespace websocketpp { /// HTTP handling support namespace http { - /// The type of an HTTP attribute list - /** - * The attribute list is an unordered key/value map. Encoded attribute - * values are delimited by semicolons. - */ - typedef std::map attribute_list; - - /// The type of an HTTP parameter list - /** - * The parameter list is an ordered pairing of a parameter and its - * associated attribute list. Encoded parameter values are delimited by - * commas. - */ - typedef std::vector< std::pair > parameter_list; - - /// Literal value of the HTTP header delimiter - static char const header_delimiter[] = "\r\n"; - - /// Literal value of the HTTP header separator - static char const header_separator[] = ":"; - - /// Literal value of an empty header - static std::string const empty_header; - - /// Maximum size in bytes before rejecting an HTTP header as too big. - size_t const max_header_size = 16000; - /// Default Maximum size in bytes for HTTP message bodies. - size_t const max_body_size = 32000000; - - /// Number of bytes to use for temporary istream read buffers - size_t const istream_buffer = 512; - - /// invalid HTTP token characters - /** - * 0x00 - 0x32, 0x7f-0xff - * ( ) < > @ , ; : \ " / [ ] ? = { } - */ - static char const header_token[] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f - 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f - 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f - 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f - 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff - }; - - /// Is the character a token - inline bool is_token_char(unsigned char c) { - return (header_token[c] == 1); - } +/// The type of an HTTP attribute list +/** + * The attribute list is an unordered key/value map. Encoded attribute + * values are delimited by semicolons. + */ +typedef std::map attribute_list; + +/// The type of an HTTP parameter list +/** + * The parameter list is an ordered pairing of a parameter and its + * associated attribute list. Encoded parameter values are delimited by + * commas. + */ +typedef std::vector< std::pair > parameter_list; + +/// Literal value of the HTTP header delimiter +static char const header_delimiter[] = "\r\n"; + +/// Literal value of the HTTP header separator +static char const header_separator[] = ":"; + +/// Literal value of an empty header +static std::string const empty_header; + +/// Maximum size in bytes before rejecting an HTTP header as too big. +size_t const max_header_size = 16000; + +/// Default Maximum size in bytes for HTTP message bodies. +size_t const max_body_size = 32000000; + +/// Number of bytes to use for temporary istream read buffers +size_t const istream_buffer = 512; + +/// invalid HTTP token characters +/** + * 0x00 - 0x32, 0x7f-0xff + * ( ) < > @ , ; : \ " / [ ] ? = { } + */ +static char const header_token[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f + 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f + 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f + 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff +}; + +/// Is the character a token +inline bool is_token_char(unsigned char c) { + return (header_token[c] == 1); +} + +/// Is the character a non-token +inline bool is_not_token_char(unsigned char c) { + return !header_token[c]; +} + +/// Is the character whitespace +/** + * whitespace is space (32) or horizontal tab (9) + */ +inline bool is_whitespace_char(unsigned char c) { + return (c == 9 || c == 32); +} + +/// Is the character non-whitespace +inline bool is_not_whitespace_char(unsigned char c) { + return (c != 9 && c != 32); +} + +/// HTTP Status codes +namespace status_code { +/// Known values for HTTP Status codes +enum value { + uninitialized = 0, + + continue_code = 100, + switching_protocols = 101, + + ok = 200, + created = 201, + accepted = 202, + non_authoritative_information = 203, + no_content = 204, + reset_content = 205, + partial_content = 206, + + multiple_choices = 300, + moved_permanently = 301, + found = 302, + see_other = 303, + not_modified = 304, + use_proxy = 305, + temporary_redirect = 307, + + bad_request = 400, + unauthorized = 401, + payment_required = 402, + forbidden = 403, + not_found = 404, + method_not_allowed = 405, + not_acceptable = 406, + proxy_authentication_required = 407, + request_timeout = 408, + conflict = 409, + gone = 410, + length_required = 411, + precondition_failed = 412, + request_entity_too_large = 413, + request_uri_too_long = 414, + unsupported_media_type = 415, + request_range_not_satisfiable = 416, + expectation_failed = 417, + im_a_teapot = 418, + upgrade_required = 426, + precondition_required = 428, + too_many_requests = 429, + request_header_fields_too_large = 431, + + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503, + gateway_timeout = 504, + http_version_not_supported = 505, + not_extended = 510, + network_authentication_required = 511 +}; - /// Is the character a non-token - inline bool is_not_token_char(unsigned char c) { - return !header_token[c]; +/// Given a status code value, return the default status message +/** + * + * @param[in] code The HTTP status code to look up + * @return A string representing the default status message for this code + * @see websocketpp::http::status_code::value (list of valid codes) + */ +inline std::string get_string(value code) { + switch (code) { + case uninitialized: + return "Uninitialized"; + case continue_code: + return "Continue"; + case switching_protocols: + return "Switching Protocols"; + case ok: + return "OK"; + case created: + return "Created"; + case accepted: + return "Accepted"; + case non_authoritative_information: + return "Non Authoritative Information"; + case no_content: + return "No Content"; + case reset_content: + return "Reset Content"; + case partial_content: + return "Partial Content"; + case multiple_choices: + return "Multiple Choices"; + case moved_permanently: + return "Moved Permanently"; + case found: + return "Found"; + case see_other: + return "See Other"; + case not_modified: + return "Not Modified"; + case use_proxy: + return "Use Proxy"; + case temporary_redirect: + return "Temporary Redirect"; + case bad_request: + return "Bad Request"; + case unauthorized: + return "Unauthorized"; + case payment_required: + return "Payment Required"; + case forbidden: + return "Forbidden"; + case not_found: + return "Not Found"; + case method_not_allowed: + return "Method Not Allowed"; + case not_acceptable: + return "Not Acceptable"; + case proxy_authentication_required: + return "Proxy Authentication Required"; + case request_timeout: + return "Request Timeout"; + case conflict: + return "Conflict"; + case gone: + return "Gone"; + case length_required: + return "Length Required"; + case precondition_failed: + return "Precondition Failed"; + case request_entity_too_large: + return "Request Entity Too Large"; + case request_uri_too_long: + return "Request-URI Too Long"; + case unsupported_media_type: + return "Unsupported Media Type"; + case request_range_not_satisfiable: + return "Requested Range Not Satisfiable"; + case expectation_failed: + return "Expectation Failed"; + case im_a_teapot: + return "I'm a teapot"; + case upgrade_required: + return "Upgrade Required"; + case precondition_required: + return "Precondition Required"; + case too_many_requests: + return "Too Many Requests"; + case request_header_fields_too_large: + return "Request Header Fields Too Large"; + case internal_server_error: + return "Internal Server Error"; + case not_implemented: + return "Not Implemented"; + case bad_gateway: + return "Bad Gateway"; + case service_unavailable: + return "Service Unavailable"; + case gateway_timeout: + return "Gateway Timeout"; + case http_version_not_supported: + return "HTTP Version Not Supported"; + case not_extended: + return "Not Extended"; + case network_authentication_required: + return "Network Authentication Required"; + default: + return "Unknown"; } +} - /// Is the character whitespace - /** - * whitespace is space (32) or horizontal tab (9) - */ - inline bool is_whitespace_char(unsigned char c) { - return (c == 9 || c == 32); +} // namespace status_code + +/// An exception type specific to HTTP errors +/** + * Includes additional details, such as HTTP error code, + * HTTP error message, and a body to return with the HTTP + * error response. + */ +class exception : public std::exception { +public: + exception(const std::string& log_msg, + status_code::value error_code, + const std::string& error_msg = std::string(), + const std::string& body = std::string()) + : m_msg(log_msg) + , m_error_msg(error_msg) + , m_body(body) + , m_error_code(error_code) {} + + ~exception() throw() {} + + virtual const char* what() const throw() { + return m_msg.c_str(); } - /// Is the character non-whitespace - inline bool is_not_whitespace_char(unsigned char c) { - return (c != 9 && c != 32); + std::string m_msg; + std::string m_error_msg; + std::string m_body; + status_code::value m_error_code; +}; + + +/// HTTP parser errors +namespace error { +enum value { + /// Catch-all error for http parser errors that don't fit in other + /// categories + general = 1, + + /// The specified data contains illegal characters for the context + invalid_format, + + /// The header name specified contains illegal characters + invalid_header_name, + + /// The body value is larger than the configured maximum size + body_too_large, + + /// The transfer encoding is not supported + unsupported_transfer_encoding, + + /// The transfer encoding is unknown + unknown_transfer_encoding, + + /// A header line was missing a separator + missing_header_separator, + + /// The request headers are larger than the configured maximum size + request_header_fields_too_large, + + /// The request was missing some required values + incomplete_request, + + /// The response status line was missing some required values + incomplete_status_line, + + /// An istream read command returned with the bad flag set + istream_bad, + + /// An istream read succeeded but read (and discarded) more bits from the + /// stream than it needed + istream_overread, +}; + +/// Get the HTTP status code associated with the error +inline status_code::value get_status_code(error::value value) { + switch(value) { + case error::general: + return status_code::bad_request; + case error::invalid_format: + return status_code::bad_request; + case error::invalid_header_name: + return status_code::bad_request; + case error::body_too_large: + return status_code::request_entity_too_large; + case error::unsupported_transfer_encoding: + return status_code::internal_server_error; + case error::unknown_transfer_encoding: + return status_code::bad_request; + case error::missing_header_separator: + return status_code::bad_request; + case error::request_header_fields_too_large: + return status_code::request_header_fields_too_large; + case error::incomplete_request: + return status_code::bad_request; + case error::incomplete_status_line: + return status_code::bad_request; + case error::istream_bad: + return status_code::internal_server_error; + case error::istream_overread: + return status_code::internal_server_error; + default: + return status_code::bad_request; } +} - /// HTTP Status codes - namespace status_code { - enum value { - uninitialized = 0, - - continue_code = 100, - switching_protocols = 101, - - ok = 200, - created = 201, - accepted = 202, - non_authoritative_information = 203, - no_content = 204, - reset_content = 205, - partial_content = 206, - - multiple_choices = 300, - moved_permanently = 301, - found = 302, - see_other = 303, - not_modified = 304, - use_proxy = 305, - temporary_redirect = 307, - - bad_request = 400, - unauthorized = 401, - payment_required = 402, - forbidden = 403, - not_found = 404, - method_not_allowed = 405, - not_acceptable = 406, - proxy_authentication_required = 407, - request_timeout = 408, - conflict = 409, - gone = 410, - length_required = 411, - precondition_failed = 412, - request_entity_too_large = 413, - request_uri_too_long = 414, - unsupported_media_type = 415, - request_range_not_satisfiable = 416, - expectation_failed = 417, - im_a_teapot = 418, - upgrade_required = 426, - precondition_required = 428, - too_many_requests = 429, - request_header_fields_too_large = 431, - - internal_server_error = 500, - not_implemented = 501, - bad_gateway = 502, - service_unavailable = 503, - gateway_timeout = 504, - http_version_not_supported = 505, - not_extended = 510, - network_authentication_required = 511 - }; - - // TODO: should this be inline? - inline std::string get_string(value c) { - switch (c) { - case uninitialized: - return "Uninitialized"; - case continue_code: - return "Continue"; - case switching_protocols: - return "Switching Protocols"; - case ok: - return "OK"; - case created: - return "Created"; - case accepted: - return "Accepted"; - case non_authoritative_information: - return "Non Authoritative Information"; - case no_content: - return "No Content"; - case reset_content: - return "Reset Content"; - case partial_content: - return "Partial Content"; - case multiple_choices: - return "Multiple Choices"; - case moved_permanently: - return "Moved Permanently"; - case found: - return "Found"; - case see_other: - return "See Other"; - case not_modified: - return "Not Modified"; - case use_proxy: - return "Use Proxy"; - case temporary_redirect: - return "Temporary Redirect"; - case bad_request: - return "Bad Request"; - case unauthorized: - return "Unauthorized"; - case payment_required: - return "Payment Required"; - case forbidden: - return "Forbidden"; - case not_found: - return "Not Found"; - case method_not_allowed: - return "Method Not Allowed"; - case not_acceptable: - return "Not Acceptable"; - case proxy_authentication_required: - return "Proxy Authentication Required"; - case request_timeout: - return "Request Timeout"; - case conflict: - return "Conflict"; - case gone: - return "Gone"; - case length_required: - return "Length Required"; - case precondition_failed: - return "Precondition Failed"; - case request_entity_too_large: - return "Request Entity Too Large"; - case request_uri_too_long: - return "Request-URI Too Long"; - case unsupported_media_type: - return "Unsupported Media Type"; - case request_range_not_satisfiable: - return "Requested Range Not Satisfiable"; - case expectation_failed: - return "Expectation Failed"; - case im_a_teapot: - return "I'm a teapot"; - case upgrade_required: - return "Upgrade Required"; - case precondition_required: - return "Precondition Required"; - case too_many_requests: - return "Too Many Requests"; - case request_header_fields_too_large: - return "Request Header Fields Too Large"; - case internal_server_error: - return "Internal Server Error"; - case not_implemented: - return "Not Implemented"; - case bad_gateway: - return "Bad Gateway"; - case service_unavailable: - return "Service Unavailable"; - case gateway_timeout: - return "Gateway Timeout"; - case http_version_not_supported: - return "HTTP Version Not Supported"; - case not_extended: - return "Not Extended"; - case network_authentication_required: - return "Network Authentication Required"; - default: - return "Unknown"; - } - } +/// HTTP parser error category +class category : public lib::error_category { +public: + char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ { + return "websocketpp.http"; } - class exception : public std::exception { - public: - exception(const std::string& log_msg, - status_code::value error_code, - const std::string& error_msg = std::string(), - const std::string& body = std::string()) - : m_msg(log_msg) - , m_error_msg(error_msg) - , m_body(body) - , m_error_code(error_code) {} - - ~exception() throw() {} - - virtual const char* what() const throw() { - return m_msg.c_str(); + std::string message(int value) const { + switch(value) { + case error::general: + return "Generic http parser error"; + case error::invalid_format: + return "The specified data contains illegal characters for the context"; + case error::invalid_header_name: + return "The header name specified contains illegal characters"; + case error::body_too_large: + return "The body value is larger than the configured maximum size"; + case error::unsupported_transfer_encoding: + return "The transfer encoding is not supported"; + case error::unknown_transfer_encoding: + return "The transfer encoding is unknown"; + case error::missing_header_separator: + return "A header line was missing a separator"; + case error::request_header_fields_too_large: + return "The request headers are larger than the configured maximum size"; + case error::incomplete_request: + return "The request was missing some required values"; + case error::incomplete_status_line: + return "The response status line was missing some required values"; + case error::istream_bad: + return "An istream read command returned with the bad flag set"; + case error::istream_overread: + return "An istream read succeeded but read (and discarded) more bits from the stream than it needed"; + default: + return "Unknown"; } + } +}; - std::string m_msg; - std::string m_error_msg; - std::string m_body; - status_code::value m_error_code; - }; +/// Get a reference to a static copy of the asio transport error category +inline lib::error_category const & get_category() { + static category instance; + return instance; } + +/// Create an error code with the given value and the asio transport category +inline lib::error_code make_error_code(error::value e) { + return lib::error_code(static_cast(e), get_category()); } +} // namespace error +} // namespace http +} // namespace websocketpp + +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ +template<> struct is_error_code_enum +{ + static bool const value = true; +}; +_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ + + + #endif // HTTP_CONSTANTS_HPP diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/parser.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/parser.hpp index fd24adb5d5..c8c0eee785 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/parser.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/parser.hpp @@ -38,11 +38,16 @@ namespace websocketpp { namespace http { namespace parser { -inline void parser::set_version(std::string const & version) { +inline lib::error_code parser::set_version(std::string const & version) { + // todo: validation? m_version = version; + + return lib::error_code(); } inline std::string const & parser::get_header(std::string const & key) const { + // This find is case insensitive due to the case insensitive comparator + // templated into header_list. header_list::const_iterator h = m_headers.find(key); if (h == m_headers.end()) { @@ -64,11 +69,11 @@ inline bool parser::get_header_as_plist(std::string const & key, return this->parse_parameter_list(it->second,out); } -inline void parser::append_header(std::string const & key, std::string const & +inline lib::error_code parser::append_header(std::string const & key, std::string const & val) { if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) { - throw exception("Invalid header name",status_code::bad_request); + return error::make_error_code(error::invalid_header_name); } if (this->get_header(key).empty()) { @@ -76,32 +81,51 @@ inline void parser::append_header(std::string const & key, std::string const & } else { m_headers[key] += ", " + val; } + return lib::error_code(); } -inline void parser::replace_header(std::string const & key, std::string const & +inline lib::error_code parser::replace_header(std::string const & key, std::string const & val) { + if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) { + return error::make_error_code(error::invalid_header_name); + } + m_headers[key] = val; + return lib::error_code(); } -inline void parser::remove_header(std::string const & key) { +inline lib::error_code parser::remove_header(std::string const & key) +{ + if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) { + return error::make_error_code(error::invalid_header_name); + } + m_headers.erase(key); + return lib::error_code(); } -inline void parser::set_body(std::string const & value) { +inline lib::error_code parser::set_body(std::string const & value) { + lib::error_code ec; if (value.size() == 0) { - remove_header("Content-Length"); + ec = remove_header("Content-Length"); + if (ec) { return ec; } + m_body.clear(); - return; + return lib::error_code(); } - // TODO: should this method respect the max size? If so how should errors - // be indicated? + if (value.size() > m_body_bytes_max) { + return error::make_error_code(error::body_too_large); + } std::stringstream len; len << value.size(); - replace_header("Content-Length", len.str()); + ec = replace_header("Content-Length", len.str()); + if (ec) { return ec; } + m_body = value; + return lib::error_code(); } inline bool parser::parse_parameter_list(std::string const & in, @@ -116,7 +140,7 @@ inline bool parser::parse_parameter_list(std::string const & in, return (it == in.begin()); } -inline bool parser::prepare_body() { +inline bool parser::prepare_body(lib::error_code & ec) { if (!get_header("Content-Length").empty()) { std::string const & cl_header = get_header("Content-Length"); char * end; @@ -127,38 +151,44 @@ inline bool parser::prepare_body() { m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10); if (m_body_bytes_needed > m_body_bytes_max) { - throw exception("HTTP message body too large", - status_code::request_entity_too_large); + ec = error::make_error_code(error::body_too_large); + return false; } m_body_encoding = body_encoding::plain; + ec = lib::error_code(); return true; } else if (get_header("Transfer-Encoding") == "chunked") { - // TODO + // ec = error::make_error_code(error::unsupported_transfer_encoding); + // TODO: support for chunked transfers? Is that too much HTTP logic? //m_body_encoding = body_encoding::chunked; return false; } else { + ec = lib::error_code(); return false; } } -inline size_t parser::process_body(char const * buf, size_t len) { +inline size_t parser::process_body(char const * buf, size_t len, + lib::error_code & ec) +{ if (m_body_encoding == body_encoding::plain) { size_t processed = (std::min)(m_body_bytes_needed,len); m_body.append(buf,processed); m_body_bytes_needed -= processed; + ec = lib::error_code(); return processed; } else if (m_body_encoding == body_encoding::chunked) { - // TODO: - throw exception("Unexpected body encoding", - status_code::internal_server_error); + ec = error::make_error_code(error::unsupported_transfer_encoding); + return 0; + // TODO: support for chunked transfers? } else { - throw exception("Unexpected body encoding", - status_code::internal_server_error); + ec = error::make_error_code(error::unknown_transfer_encoding); + return 0; } } -inline void parser::process_header(std::string::iterator begin, +inline lib::error_code parser::process_header(std::string::iterator begin, std::string::iterator end) { std::string::iterator cursor = std::search( @@ -169,11 +199,13 @@ inline void parser::process_header(std::string::iterator begin, ); if (cursor == end) { - throw exception("Invalid header line",status_code::bad_request); + return error::make_error_code(error::body_too_large); } - append_header(strip_lws(std::string(begin,cursor)), - strip_lws(std::string(cursor+sizeof(header_separator)-1,end))); + // any error from append header represents our final error status + return append_header( + strip_lws(std::string(begin,cursor)), + strip_lws(std::string(cursor+sizeof(header_separator)-1,end))); } inline header_list const & parser::get_headers() const { diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/request.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/request.hpp index 311a620f62..eae77a606f 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/request.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/request.hpp @@ -38,20 +38,37 @@ namespace websocketpp { namespace http { namespace parser { -inline size_t request::consume(char const * buf, size_t len) { - size_t bytes_processed; +inline size_t request::consume(char const * buf, size_t len, lib::error_code & ec) +{ + size_t bytes_processed = 0; - if (m_ready) {return 0;} + if (m_ready) { + // the request is already complete. End immediately without reading. + ec = lib::error_code(); + return 0; + } if (m_body_bytes_needed > 0) { - bytes_processed = process_body(buf,len); + // The headers are complete, but we are still expecting more body + // bytes. Process body bytes. + bytes_processed = process_body(buf, len, ec); + if (ec) { + return bytes_processed; + } + + // if we have ready all the expected body bytes set the ready flag if (body_ready()) { m_ready = true; } return bytes_processed; } - // copy new header bytes into buffer + // at this point we have an incomplete request still waiting for headers + + // copy new candidate bytes into our local buffer. This buffer may have + // leftover bytes from previous calls. Not all of these bytes are + // necessarily header bytes (they might be body or even data after this + // request entirely for a keepalive request) m_buf->append(buf,len); // Search for delimiter in buf. If found read until then. If not read all @@ -59,39 +76,66 @@ inline size_t request::consume(char const * buf, size_t len) { std::string::iterator end; for (;;) { - // search for line delimiter + // search for line delimiter in our local buffer end = std::search( begin, m_buf->end(), header_delimiter, header_delimiter+sizeof(header_delimiter)-1 ); - - m_header_bytes += (end-begin+sizeof(header_delimiter)); - - if (m_header_bytes > max_header_size) { - // exceeded max header size - throw exception("Maximum header size exceeded.", - status_code::request_header_fields_too_large); - } if (end == m_buf->end()) { - // we are out of bytes. Discard the processed bytes and copy the - // remaining unprecessed bytes to the beginning of the buffer - std::copy(begin,end,m_buf->begin()); - m_buf->resize(static_cast(end-begin)); - m_header_bytes -= m_buf->size(); + // we didn't find the delimiter + + // check that the confirmed header bytes plus the outstanding + // candidate bytes do not put us over the header size limit. + if (m_header_bytes + (end - begin) > max_header_size) { + ec = error::make_error_code(error::request_header_fields_too_large); + return 0; + } + // We are out of bytes but not over any limits yet. Discard the + // processed bytes and copy the remaining unprecessed bytes to the + // beginning of the buffer in prep for another call to consume. + + // If there are no processed bytes in the buffer right now don't + // copy the unprocessed ones over themselves. + if (begin != m_buf->begin()) { + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + } + + ec = lib::error_code(); return len; } - //the range [begin,end) now represents a line to be processed. + // at this point we have found a delimiter and the range [begin,end) + // represents a line to be processed + + // update count of header bytes read so far + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + + if (m_header_bytes > max_header_size) { + // This read exceeded max header size + ec = error::make_error_code(error::request_header_fields_too_large); + return 0; + } + if (end-begin == 0) { - // we got a blank line + // we got a blank line, which indicates the end of the headers + + // If we never got a valid method or are missing a host header then + // this request is invalid. if (m_method.empty() || get_header("Host").empty()) { - throw exception("Incomplete Request",status_code::bad_request); + ec = error::make_error_code(error::incomplete_request); + return 0; } + // any bytes left over in the local buffer are bytes we didn't use. + // When we report how many bytes we consumed we need to subtract + // these so the caller knows that they need to be processed by some + // other logic. bytes_processed = ( len - static_cast(m_buf->end()-end) + sizeof(header_delimiter) - 1 @@ -104,27 +148,47 @@ inline size_t request::consume(char const * buf, size_t len) { // continue capturing content-length bytes and expose them as a // request body. - if (prepare_body()) { - bytes_processed += process_body(buf+bytes_processed,len-bytes_processed); + bool need_more = prepare_body(ec); + if (ec) { + return 0; + } + + if (need_more) { + bytes_processed += process_body(buf+bytes_processed,len-bytes_processed,ec); + if (ec) { + return 0; + } if (body_ready()) { m_ready = true; } + ec = lib::error_code(); return bytes_processed; } else { m_ready = true; // return number of bytes processed (starting bytes - bytes left) + ec = lib::error_code(); return bytes_processed; } } else { + // we got a line with content if (m_method.empty()) { - this->process(begin,end); + // if we haven't found a method yet process this line as a first line + ec = this->process(begin, end); } else { - this->process_header(begin,end); + // this is a second (or later) line, process as a header + ec = this->process_header(begin, end); + } + if (ec) { + return 0; } } + // if we got here it means there is another header line to read. + // advance our cursor to the first character after the most recent + // delimiter found. begin = end+(sizeof(header_delimiter)-1); + } } @@ -148,40 +212,49 @@ inline std::string request::raw_head() const { return ret.str(); } -inline void request::set_method(std::string const & method) { +inline lib::error_code request::set_method(std::string const & method) +{ if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) { - throw exception("Invalid method token.",status_code::bad_request); + return error::make_error_code(error::invalid_format); } m_method = method; + return lib::error_code(); } -inline void request::set_uri(std::string const & uri) { +inline lib::error_code request::set_uri(std::string const & uri) { // TODO: validation? m_uri = uri; + + return lib::error_code(); } -inline void request::process(std::string::iterator begin, std::string::iterator +inline lib::error_code request::process(std::string::iterator begin, std::string::iterator end) { + lib::error_code ec; + std::string::iterator cursor_start = begin; std::string::iterator cursor_end = std::find(begin,end,' '); if (cursor_end == end) { - throw exception("Invalid request line1",status_code::bad_request); + return error::make_error_code(error::incomplete_request); } - set_method(std::string(cursor_start,cursor_end)); + ec = set_method(std::string(cursor_start,cursor_end)); + if (ec) { return ec; } cursor_start = cursor_end+1; cursor_end = std::find(cursor_start,end,' '); if (cursor_end == end) { - throw exception("Invalid request line2",status_code::bad_request); + return error::make_error_code(error::incomplete_request); } - set_uri(std::string(cursor_start,cursor_end)); - set_version(std::string(cursor_end+1,end)); + ec = set_uri(std::string(cursor_start,cursor_end)); + if (ec) { return ec; } + + return set_version(std::string(cursor_end+1,end)); } } // namespace parser diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/response.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/response.hpp index 4400cda5c8..8890ee1031 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/response.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/impl/response.hpp @@ -39,14 +39,25 @@ namespace websocketpp { namespace http { namespace parser { -inline size_t response::consume(char const * buf, size_t len) { - if (m_state == DONE) {return 0;} +inline size_t response::consume(char const * buf, size_t len, lib::error_code & ec) { + if (m_state == DONE) { + // the response is already complete. End immediately without reading. + ec = lib::error_code(); + return 0; + } if (m_state == BODY) { - return this->process_body(buf,len); + // The headers are complete, but we are still expecting more body + // bytes. Process body bytes. + return this->process_body(buf,len,ec); } - // copy new header bytes into buffer + // at this point we have an incomplete response still waiting for headers + + // copy new candidate bytes into our local buffer. This buffer may have + // leftover bytes from previous calls. Not all of these bytes are + // necessarily header bytes (they might be body or even data after this + // request entirely for a keepalive request) m_buf->append(buf,len); // Search for delimiter in buf. If found read until then. If not read all @@ -63,80 +74,119 @@ inline size_t response::consume(char const * buf, size_t len) { header_delimiter + sizeof(header_delimiter) - 1 ); - m_header_bytes += (end-begin+sizeof(header_delimiter)); - - if (m_header_bytes > max_header_size) { - // exceeded max header size - throw exception("Maximum header size exceeded.", - status_code::request_header_fields_too_large); - } - if (end == m_buf->end()) { - // we are out of bytes. Discard the processed bytes and copy the - // remaining unprecessed bytes to the beginning of the buffer - std::copy(begin,end,m_buf->begin()); - m_buf->resize(static_cast(end-begin)); + // we didn't find the delimiter + + // check that the confirmed header bytes plus the outstanding + // candidate bytes do not put us over the header size limit. + if (m_header_bytes + (end - begin) > max_header_size) { + ec = error::make_error_code(error::request_header_fields_too_large); + return 0; + } + + // We are out of bytes but not over any limits yet. Discard the + // processed bytes and copy the remaining unprecessed bytes to the + // beginning of the buffer in prep for another call to consume. + + // If there are no processed bytes in the buffer right now don't + // copy the unprocessed ones over themselves. + if (begin != m_buf->begin()) { + std::copy(begin,end,m_buf->begin()); + m_buf->resize(static_cast(end-begin)); + } m_read += len; - m_header_bytes -= m_buf->size(); + ec = lib::error_code(); return len; } - //the range [begin,end) now represents a line to be processed. + // at this point we have found a delimiter and the range [begin,end) + // represents a line to be processed + + // update count of header bytes read so far + m_header_bytes += (end-begin+sizeof(header_delimiter)); + + if (m_header_bytes > max_header_size) { + // This read exceeded max header size + ec = error::make_error_code(error::request_header_fields_too_large); + return 0; + } + if (end-begin == 0) { - // we got a blank line + // we got a blank line, which indicates the end of the headers + + // If we are still looking for a response line then this request + // is incomplete if (m_state == RESPONSE_LINE) { - throw exception("Incomplete Request",status_code::bad_request); + ec = error::make_error_code(error::incomplete_request); + return 0; } // TODO: grab content-length std::string length = get_header("Content-Length"); if (length.empty()) { - // no content length found, read indefinitely + // no content length found, read indefinitely? m_read = 0; } else { std::istringstream ss(length); if ((ss >> m_read).fail()) { - throw exception("Unable to parse Content-Length header", - status_code::bad_request); + ec = error::make_error_code(error::invalid_format); + return 0; } } + // transition state to reading the response body m_state = BODY; - // calc header bytes processed (starting bytes - bytes left) + // calculate how many bytes in the local buffer are bytes we didn't + // use for the headers. size_t read = ( len - static_cast(m_buf->end() - end) + sizeof(header_delimiter) - 1 ); - // if there were bytes left process them as body bytes + // if there were bytes left process them as body bytes. + // read is incremented with the number of body bytes processed. + // It is possible that there are still some bytes not read. These + // will be 'returned' to the caller by having the return value be + // less than len. if (read < len) { - read += this->process_body(buf+read,(len-read)); + read += this->process_body(buf+read,(len-read),ec); + } + if (ec) { + return 0; } // frees memory used temporarily during header parsing m_buf.reset(); + ec = lib::error_code(); return read; } else { + // we got a line if (m_state == RESPONSE_LINE) { - this->process(begin,end); + ec = this->process(begin,end); m_state = HEADERS; } else { - this->process_header(begin,end); + ec = this->process_header(begin,end); + } + if (ec) { + return 0; } } + // if we got here it means there is another header line to read. + // advance our cursor to the first character after the most recent + // delimiter found. begin = end+(sizeof(header_delimiter) - 1); } } -inline size_t response::consume(std::istream & s) { +inline size_t response::consume(std::istream & s, lib::error_code & ec) { char buf[istream_buffer]; size_t bytes_read; size_t bytes_processed; @@ -147,12 +197,21 @@ inline size_t response::consume(std::istream & s) { bytes_read = static_cast(s.gcount()); if (s.fail() || s.eof()) { - bytes_processed = this->consume(buf,bytes_read); + bytes_processed = this->consume(buf,bytes_read,ec); total += bytes_processed; + if (ec) { return total; } + if (bytes_processed != bytes_read) { - // problem - break; + // we read more data from the stream than we needed for the + // HTTP response. This extra data gets thrown away now. + // Returning it to the caller is complicated so we alert the + // caller at least. This whole method has been deprecated + // because this convenience method doesnt really add useful + // functionality to the library, but makes it difficult to + // recover from error cases. + ec = error::make_error_code(error::istream_overread); + return total; } } else if (s.bad()) { // problem @@ -162,12 +221,21 @@ inline size_t response::consume(std::istream & s) { // the newline that was discarded, since our raw consume function // expects the newline to be be there. buf[bytes_read-1] = '\n'; - bytes_processed = this->consume(buf,bytes_read); + bytes_processed = this->consume(buf,bytes_read,ec); total += bytes_processed; + if (ec) { return total; } + if (bytes_processed != bytes_read) { - // problem - break; + // we read more data from the stream than we needed for the + // HTTP response. This extra data gets thrown away now. + // Returning it to the caller is complicated so we alert the + // caller at least. This whole method has been deprecated + // because this convenience method doesnt really add useful + // functionality to the library, but makes it difficult to + // recover from error cases. + ec = error::make_error_code(error::istream_overread); + return total; } } } @@ -188,28 +256,41 @@ inline std::string response::raw() const { return ret.str(); } -inline void response::set_status(status_code::value code) { - // TODO: validation? +inline lib::error_code response::set_status(status_code::value code) { + // In theory the type of status_code::value should prevent setting any + // invalid values. Messages are canned and looked up and known to be + // valid. + // TODO: Is there anything else that would need validation here? m_status_code = code; m_status_msg = get_string(code); + return lib::error_code(); } -inline void response::set_status(status_code::value code, std::string const & - msg) +inline lib::error_code response::set_status(status_code::value code, + std::string const & msg) { - // TODO: validation? + // In theory the type of status_code::value should prevent setting any + // invalid values. + // TODO: Is there anything else that would need validation here? + // length or content of message? + // Per RFC2616 + // Reason-Phrase = * + // TEXT = = + // CTL = + // LWS = [CRLF] 1*( SP | HT ) m_status_code = code; m_status_msg = msg; + return lib::error_code(); } -inline void response::process(std::string::iterator begin, +inline lib::error_code response::process(std::string::iterator begin, std::string::iterator end) { std::string::iterator cursor_start = begin; std::string::iterator cursor_end = std::find(begin,end,' '); if (cursor_end == end) { - throw exception("Invalid response line",status_code::bad_request); + return error::make_error_code(error::incomplete_status_line); } set_version(std::string(cursor_start,cursor_end)); @@ -218,7 +299,7 @@ inline void response::process(std::string::iterator begin, cursor_end = std::find(cursor_start,end,' '); if (cursor_end == end) { - throw exception("Invalid request line",status_code::bad_request); + return error::make_error_code(error::incomplete_status_line); } int code; @@ -226,18 +307,23 @@ inline void response::process(std::string::iterator begin, std::istringstream ss(std::string(cursor_start,cursor_end)); if ((ss >> code).fail()) { - throw exception("Unable to parse response code",status_code::bad_request); + return error::make_error_code(error::incomplete_status_line); } - set_status(status_code::value(code),std::string(cursor_end+1,end)); + // todo: validation of status code? Technically there are limits on what + // status codes can be. Right now we follow Postel's law and check only + // that the valid is an integer and let the next layer decide what to do. + // Is this reasonable or should we be more aggressive? + + // validation of the status message will pass through + return set_status(status_code::value(code),std::string(cursor_end+1,end)); } -inline size_t response::process_body(char const * buf, size_t len) { +inline size_t response::process_body(char const * buf, size_t len, lib::error_code & ec) { // If no content length was set then we read forever and never set m_ready if (m_read == 0) { - //m_body.append(buf,len); - //return len; m_state = DONE; + ec = lib::error_code(); return 0; } @@ -256,6 +342,7 @@ inline size_t response::process_body(char const * buf, size_t len) { m_body.append(buf,to_read); m_read -= to_read; + ec = lib::error_code(); return to_read; } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/parser.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/parser.hpp index 9d309ec91b..2b4d194c9c 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/parser.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/parser.hpp @@ -417,12 +417,13 @@ class parser { * @todo Does this method need any validation? * * @param [in] version The value to set the HTTP version to. + * @return A status code describing the outcome of the operation. */ - void set_version(std::string const & version); + lib::error_code set_version(std::string const & version); /// Get the value of an HTTP header /** - * @todo Make this method case insensitive. + * Note: per HTTP specs header values are compared case insensitively. * * @param [in] key The name/key of the header to get. * @return The value associated with the given HTTP header key. @@ -457,16 +458,19 @@ class parser { * indicated value. If a header with the name `key` already exists, `val` * will be appended to the existing value. * - * @todo Make this method case insensitive. + * Note: per HTTP specs header values are compared case insensitively. + * * @todo Should there be any restrictions on which keys are allowed? - * @todo Exception free varient * * @see replace_header * + * @since 0.9.0 (return value added, exceptions removed) + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. + * @return A status code describing the outcome of the operation. */ - void append_header(std::string const & key, std::string const & val); + lib::error_code append_header(std::string const & key, std::string const & val); /// Set a value for an HTTP header, replacing an existing value /** @@ -474,27 +478,33 @@ class parser { * indicated value. If a header with the name `key` already exists, `val` * will replace the existing value. * - * @todo Make this method case insensitive. - * @todo Should there be any restrictions on which keys are allowed? - * @todo Exception free varient + * Note: per HTTP specs header values are compared case insensitively. * * @see append_header * + * @since 0.9.0 (return value added) + * * @param [in] key The name/key of the header to append to. * @param [in] val The value to append. + * @return A status code describing the outcome of the operation. */ - void replace_header(std::string const & key, std::string const & val); + lib::error_code replace_header(std::string const & key, std::string const & val); /// Remove a header from the parser /** * Removes the header entirely from the parser. This is different than * setting the value of the header to blank. * - * @todo Make this method case insensitive. + * Note: per HTTP specs header values are compared case insensitively. * + * @since 0.9.0 (return value added) + * * @param [in] key The name/key of the header to remove. + * @return A status code describing the outcome of the operation. */ - void remove_header(std::string const & key); + lib::error_code remove_header(std::string const & key); + + // todo exception varients for the above 3? /// Get HTTP body /** @@ -513,9 +523,12 @@ class parser { * value. If you want the Content-Length header to be something else, do so * via replace_header("Content-Length") after calling set_body() * + * @since 0.9.0 (return value added) + * * @param value String data to include as the body content. + * @return A status code describing the outcome of the operation. */ - void set_body(std::string const & value); + lib::error_code set_body(std::string const & value); /// Get body size limit /** @@ -554,12 +567,14 @@ class parser { protected: /// Process a header line /** - * @todo Update this method to be exception free. - * + * @since 0.9.0 (return value added, exceptions removed) + * * @param [in] begin An iterator to the beginning of the sequence. * @param [in] end An iterator to the end of the sequence. + * @return A status code describing the outcome of the operation. */ - void process_header(std::string::iterator begin, std::string::iterator end); + lib::error_code process_header(std::string::iterator begin, + std::string::iterator end); /// Prepare the parser to begin parsing body data /** @@ -570,23 +585,27 @@ class parser { * * Must not be called until after all headers have been processed. * - * @since 0.5.0 + * @since 0.5.0 (no parameters) + * @since 0.9.0 (ec parameter added, exceptions removed) * + * @param [out] ec A status code describing the outcome of the operation. * @return True if more bytes are needed to load the body, false otherwise. */ - bool prepare_body(); + bool prepare_body(lib::error_code & ec); /// Process body data /** * Parses body data. * * @since 0.5.0 + * @since 0.9.0 (ec parameter added, exceptions removed) * * @param [in] begin An iterator to the beginning of the sequence. * @param [in] end An iterator to the end of the sequence. + * @param [out] ec A status code describing the outcome of the operation. * @return The number of bytes processed */ - size_t process_body(char const * buf, size_t len); + size_t process_body(char const * buf, size_t len, lib::error_code & ec); /// Check if the parser is done parsing the body /** diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/request.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/request.hpp index 3355c99b86..ca8c5ac8cf 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/request.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/request.hpp @@ -72,11 +72,14 @@ class request : public parser { * error reasons include malformed requests, incomplete requests, and max * header size being reached. * - * @param buf Pointer to byte buffer - * @param len Size of byte buffer + * @since 0.9.0 Added ec parameter + * + * @param [in] buf Pointer to byte buffer + * @param [in] len Size of byte buffer + * @param [out] ec A status code describing the outcome of the operation. * @return Number of bytes processed. */ - size_t consume(char const * buf, size_t len); + size_t consume(char const * buf, size_t len, lib::error_code & ec); /// Returns whether or not the request is ready for reading. bool ready() const { @@ -89,16 +92,32 @@ class request : public parser { /// Returns the raw request headers only (similar to an HTTP HEAD request) std::string raw_head() const; - /// Set the HTTP method. Must be a valid HTTP token - void set_method(std::string const & method); + /// Set the HTTP method. + /** + * Must be a valid HTTP token + * + * @since 0.9.0 added return value and removed exception + * + * @param [in] method The value to set the method to. + * @return A status code describing the outcome of the operation. + */ + lib::error_code set_method(std::string const & method); /// Return the request method std::string const & get_method() const { return m_method; } - /// Set the HTTP uri. Must be a valid HTTP uri - void set_uri(std::string const & uri); + /// Set the HTTP uri. + /** + * Must be a valid HTTP uri + * + * @since 0.9.0 Return value added + * + * @param uri The URI to set + * @return A status code describing the outcome of the operation. + */ + lib::error_code set_uri(std::string const & uri); /// Return the requested URI std::string const & get_uri() const { @@ -107,7 +126,14 @@ class request : public parser { private: /// Helper function for message::consume. Process request line - void process(std::string::iterator begin, std::string::iterator end); + /** + * @since 0.9.0 (ec parameter added, exceptions removed) + * + * @param [in] begin An iterator to the beginning of the sequence. + * @param [in] end An iterator to the end of the sequence. + * @return A status code describing the outcome of the operation. + */ + lib::error_code process(std::string::iterator begin, std::string::iterator end); lib::shared_ptr m_buf; std::string m_method; diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/response.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/response.hpp index e724a3d3a9..64bce76a89 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/response.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/http/response.hpp @@ -77,21 +77,24 @@ class response : public parser { * the ready flag will be set. Further calls to consume once ready will be * ignored. * - * Consume will throw an http::exception in the case of an error. Typical - * error reasons include malformed responses, incomplete responses, and max - * header size being reached. + * As of 0.9.0, consume will return a status code describing the output of + * the operation. Earlier versions threw an `http::exception`. The status + * code will be zero/default constructed on success and non-zero on error. + * Typical error reasons include malformed responses, incomplete responses, + * and max header size being reached. * - * @param buf Pointer to byte buffer - * @param len Size of byte buffer + * @since 0.9.0 Added ec parameter + * + * @param [in] buf Pointer to byte buffer + * @param [in] len Size of byte buffer + * @param [out] ec A status code describing the outcome of the operation. * @return Number of bytes processed. */ - size_t consume(char const * buf, size_t len); + size_t consume(char const * buf, size_t len, lib::error_code & ec); /// Process bytes in the input buffer (istream version) /** * Process bytes from istream s. Returns the number of bytes processed. - * Bytes left unprocessed means bytes left over after the final header - * delimiters. * * Consume is a streaming processor. It may be called multiple times on one * response and the full headers need not be available before processing can @@ -99,15 +102,40 @@ class response : public parser { * the ready flag will be set. Further calls to consume once ready will be * ignored. * - * Consume will throw an http::exception in the case of an error. Typical - * error reasons include malformed responses, incomplete responses, and max - * header size being reached. + * As of 0.9.0, consume will return a status code describing the output of + * the operation. Earlier versions threw an `http::exception`. The status + * code will be zero/default constructed on success and non-zero on error. + * Typical error reasons include malformed responses, incomplete responses, + * and max header size being reached. + * + * **WARNING:** If not all the bytes were needed to complete the HTTP + * request those bytes will still be removed from the istream and discarded. + * If this happens an error `istream_overread` will be returned. This means + * that the response read was successful but that some unrelated data was + * lost. If you don't care about these bytes you can ignore the error. + * + * If there is an HTTP processing error and an istream overread in the same + * call only the HTTP processing error will be returned. + * + * If you might need bytes after the header in the istream you should NOT + * use this wrapper and instead read data out of the istream directly and + * pass it to consume(char const *, size_t, lib::error_code). This method + * allows you to retain overread data. + * + * @deprecated 0.9.0 This overload is dangerous in that it can overread the + * stream and there isn't a good way to recover bytes lost this way. As of + * 0.9.0 an error is raised when this situation happens, but generally, it + * would be better for the calling application to read the stream itself and + * call consume(char const *, size_t, lib::error_code) instead which provides + * a better method of identifying and recovering from overreads. * - * @param buf Pointer to byte buffer - * @param len Size of byte buffer + * @since 0.9.0 Added ec parameter + * + * @param s pointer to an istream to read from + * @param [out] ec A status code describing the outcome of the operation. * @return Number of bytes processed. */ - size_t consume(std::istream & s); + size_t consume(std::istream & s, lib::error_code & ec); /// Returns true if the response is ready. /** @@ -132,10 +160,12 @@ class response : public parser { * use set_status(status_code::value,std::string) overload to set both * values explicitly. * + * @since 0.9.0 Added return value + * * @param code Code to set - * @param msg Message to set + * @return A status code describing the outcome of the operation. */ - void set_status(status_code::value code); + lib::error_code set_status(status_code::value code); /// Set response status code and message /** @@ -143,10 +173,13 @@ class response : public parser { * use set_status(status_code::value) to set the code and have the standard * message be automatically set. * + * @since 0.9.0 Added return value + * * @param code Code to set * @param msg Message to set + * @return A status code describing the outcome of the operation. */ - void set_status(status_code::value code, std::string const & msg); + lib::error_code set_status(status_code::value code, std::string const & msg); /// Return the response status code status_code::value get_status_code() const { @@ -159,10 +192,10 @@ class response : public parser { } private: /// Helper function for consume. Process response line - void process(std::string::iterator begin, std::string::iterator end); + lib::error_code process(std::string::iterator begin, std::string::iterator end); /// Helper function for processing body bytes - size_t process_body(char const * buf, size_t len); + size_t process_body(char const * buf, size_t len, lib::error_code & ec); enum state { RESPONSE_LINE = 0, diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/connection_impl.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/connection_impl.hpp index bf88c95526..6ab7414af6 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/connection_impl.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/connection_impl.hpp @@ -220,6 +220,7 @@ void connection::ping(std::string const& payload, lib::error_code& ec) { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::ping(std::string const & payload) { lib::error_code ec; @@ -228,6 +229,7 @@ void connection::ping(std::string const & payload) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::handle_pong_timeout(std::string payload, @@ -291,6 +293,7 @@ void connection::pong(std::string const& payload, lib::error_code& ec) { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::pong(std::string const & payload) { lib::error_code ec; @@ -299,6 +302,7 @@ void connection::pong(std::string const & payload) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::close(close::status::value const code, @@ -322,6 +326,7 @@ void connection::close(close::status::value const code, ec = this->send_close_frame(code,tr,false,close::status::terminal(code)); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::close(close::status::value const code, std::string const & reason) @@ -332,6 +337,7 @@ void connection::close(close::status::value const code, throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Trigger the on_interrupt handler /** @@ -474,6 +480,7 @@ void connection::add_subprotocol(std::string const & value, m_requested_subprotocols.push_back(value); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::add_subprotocol(std::string const & value) { lib::error_code ec; @@ -482,6 +489,7 @@ void connection::add_subprotocol(std::string const & value) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template @@ -510,8 +518,10 @@ void connection::select_subprotocol(std::string const & value, } m_subprotocol = value; + ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::select_subprotocol(std::string const & value) { lib::error_code ec; @@ -520,6 +530,7 @@ void connection::select_subprotocol(std::string const & value) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ template @@ -540,112 +551,203 @@ connection::get_response_header(std::string const & key) const { return m_response.get_header(key); } -// TODO: EXCEPTION_FREE +template +void connection::set_status(http::status_code::value code, + lib::error_code & ec) +{ + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + ec = error::make_error_code(error::invalid_state); + return; + } + ec = m_response.set_status(code); +} + +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::set_status(http::status_code::value code) +{ + lib::error_code ec; + this->set_status(code, ec); + if (ec) { + throw exception("Call to set_status from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + +template +void connection::set_status(http::status_code::value code, + std::string const & msg, lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw exception("Call to set_status from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); + return; } - m_response.set_status(code); + + ec = m_response.set_status(code,msg); } -// TODO: EXCEPTION_FREE +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::set_status(http::status_code::value code, std::string const & msg) +{ + lib::error_code ec; + this->set_status(code, msg); + if (ec) { + throw exception("Call to set_status from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + +template +void connection::set_body(std::string const & value, + lib::error_code & ec) { if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw exception("Call to set_status from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); + return; } - m_response.set_status(code,msg); + ec = m_response.set_body(value); } -// TODO: EXCEPTION_FREE +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::set_body(std::string const & value) { + lib::error_code ec; + this->set_body(value, ec); + if (ec) { + throw exception("Call to set_body from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ +template +void connection::set_body(std::string && value, + lib::error_code & ec) +{ if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw exception("Call to set_status from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); + return; } - m_response.set_body(value); + ec = m_response.set_body(std::move(value)); } -// TODO: EXCEPTION_FREE +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ +template +void connection::set_body(std::string && value) { + lib::error_code ec; + this->set_body(std::move(value), ec); + if (ec) { + throw exception("Call to set_body from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ +#endif // _WEBSOCKETPP_MOVE_SEMANTICS_ + template void connection::append_header(std::string const & key, - std::string const & val) + std::string const & val, lib::error_code & ec) { if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection - m_response.append_header(key,val); + ec = m_response.append_header(key, val); } else { - throw exception("Call to append_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection - m_request.append_header(key,val); + ec = m_request.append_header(key, val); } else { - throw exception("Call to append_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } } -// TODO: EXCEPTION_FREE +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::replace_header(std::string const & key, +void connection::append_header(std::string const & key, std::string const & val) +{ + lib::error_code ec; + this->append_header(key, val, ec); + if (ec) { + throw exception("Call to append_header from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + +template +void connection::replace_header(std::string const & key, + std::string const & val, lib::error_code & ec) { if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection - m_response.replace_header(key,val); + ec = m_response.replace_header(key, val); } else { - throw exception("Call to replace_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection - m_request.replace_header(key,val); + ec = m_request.replace_header(key, val); } else { - throw exception("Call to replace_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } } -// TODO: EXCEPTION_FREE +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template -void connection::remove_header(std::string const & key) +void connection::replace_header(std::string const & key, + std::string const & val) +{ + lib::error_code ec; + this->replace_header(key, val, ec); + if (ec) { + throw exception("Call to replace_header from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + +template +void connection::remove_header(std::string const & key, + lib::error_code & ec) { if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection - m_response.remove_header(key); + ec = m_response.remove_header(key); } else { - throw exception("Call to remove_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection - m_request.remove_header(key); + ec = m_request.remove_header(key); } else { - throw exception("Call to remove_header from invalid state", - error::make_error_code(error::invalid_state)); + ec = error::make_error_code(error::invalid_state); } } } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ +template +void connection::remove_header(std::string const & key) +{ + lib::error_code ec; + this->remove_header(key, ec); + if (ec) { + throw exception("Call to remove_header from invalid state", ec); + } +} +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Defer HTTP Response until later /** * Used in the http handler to defer the HTTP response for this connection @@ -698,6 +800,7 @@ void connection::send_http_response(lib::error_code & ec) { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ template void connection::send_http_response() { lib::error_code ec; @@ -706,6 +809,7 @@ void connection::send_http_response() { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ @@ -848,12 +952,14 @@ void connection::handle_read_handshake(lib::error_code const & ec, } size_t bytes_processed = 0; - try { - bytes_processed = m_request.consume(m_buf,bytes_transferred); - } catch (http::exception &e) { - // All HTTP exceptions will result in this request failing and an error + lib::error_code consume_ec; + + bytes_processed = m_request.consume(m_buf, bytes_transferred, consume_ec); + if (consume_ec) { + // All HTTP errors will result in this request failing and an error // response being returned. No more bytes will be read in this con. - m_response.set_status(e.m_error_code,e.m_error_msg); + m_response.set_status(http::error::get_status_code(http::error::value(consume_ec.value()))); + log_err(log::elevel::fatal,"Fatal error reading request: ",consume_ec); this->write_http_response_error(error::make_error_code(error::http_parse_error)); return; } @@ -925,6 +1031,15 @@ void connection::handle_read_handshake(lib::error_code const & ec, this->write_http_response(handshake_ec); } } else { + // The HTTP parser reported that it was not ready and wants more data. + // Assert that it actually consumed all the data present before overwriting + // the buffer. This should always be the case. + if (bytes_transferred != bytes_processed) { + m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP request parser failed to consume all bytes from a read request."); + this->terminate(make_error_code(error::general)); + return; + } + // read at least 1 more byte transport_con_type::async_read_at_least( 1, @@ -1197,7 +1312,7 @@ lib::error_code connection::process_handshake_request() { return error::make_error_code(error::http_connection_ended); } } else { - set_status(http::status_code::upgrade_required); + m_response.set_status(http::status_code::upgrade_required); return error::make_error_code(error::upgrade_required); } @@ -1302,6 +1417,7 @@ void connection::write_http_response(lib::error_code const & ec) { } if (m_response.get_status_code() == http::status_code::uninitialized) { + lib::error_code status_ec; m_response.set_status(http::status_code::internal_server_error); m_ec = error::make_error_code(error::general); } else { @@ -1583,12 +1699,15 @@ void connection::handle_read_http_response(lib::error_code const & ec, } size_t bytes_processed = 0; - // TODO: refactor this to use error codes rather than exceptions - try { - bytes_processed = m_response.consume(m_buf,bytes_transferred); - } catch (http::exception & e) { - m_elog->write(log::elevel::rerror, - std::string("error in handle_read_http_response: ")+e.what()); + + lib::error_code consume_ec; + + bytes_processed = m_response.consume(m_buf, bytes_transferred, consume_ec); + if (consume_ec) { + // An HTTP error while reading a response doesn't give us many options other than log + // and terminate. + m_response.set_status(http::error::get_status_code(http::error::value(consume_ec.value()))); + log_err(log::elevel::rerror,"error in handle_read_http_response: ",consume_ec); this->terminate(make_error_code(error::general)); return; } @@ -1648,6 +1767,15 @@ void connection::handle_read_http_response(lib::error_code const & ec, this->handle_read_frame(lib::error_code(), m_buf_cursor); } else { + // The HTTP parser reported that it was not ready and wants more data. + // Assert that it actually consumed all the data present before overwriting + // the buffer. This should always be the case. + if (bytes_transferred != bytes_processed) { + m_elog->write(log::elevel::fatal,"Assertion Failed: HTTP response parser failed to consume all bytes from a read request."); + this->terminate(make_error_code(error::general)); + return; + } + transport_con_type::async_read_at_least( 1, m_buf, @@ -1706,6 +1834,12 @@ void connection::terminate(lib::error_code const & ec) { m_handshake_timer.reset(); } + // Cancel ping timer + if (m_ping_timer) { + m_ping_timer->cancel(); + m_handshake_timer.reset(); + } + terminate_status tstat = unknown; if (ec) { m_ec = ec; @@ -1779,13 +1913,18 @@ void connection::handle_terminate(terminate_status tstat, // call the termination handler if it exists // if it exists it might (but shouldn't) refer to a bad memory location. // If it does, we don't care and should catch and ignore it. + // todo: there has to be a better way to do this. if (m_termination_handler) { +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ try { m_termination_handler(type::get_shared()); } catch (std::exception const & e) { m_elog->write(log::elevel::warn, std::string("termination_handler call failed. Reason was: ")+e.what()); } +#else + m_termination_handler(type::get_shared()); +#endif } } @@ -1968,12 +2107,12 @@ void connection::process_control_frame(typename config::message_type::pt } } } else if (op == frame::opcode::PONG) { - if (m_pong_handler) { - m_pong_handler(m_connection_hdl, msg->get_payload()); - } if (m_ping_timer) { m_ping_timer->cancel(); } + if (m_pong_handler) { + m_pong_handler(m_connection_hdl, msg->get_payload()); + } } else if (op == frame::opcode::CLOSE) { m_alog->write(log::alevel::devel,"got close frame"); // record close code and reason somewhere @@ -2123,6 +2262,13 @@ lib::error_code connection::send_close_frame(close::status::value code, m_state = session::state::closing; + // Cancel any outstanding ping timers. Once we are in state closing the + // library no longer processes non-close frames, so any pongs will be + // dropped. + if (m_ping_timer) { + m_ping_timer->cancel(); + } + if (ack) { m_was_clean = true; } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/endpoint_impl.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/endpoint_impl.hpp index 2aac1d9dac..e945a78f3b 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/endpoint_impl.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/endpoint_impl.hpp @@ -34,7 +34,7 @@ namespace websocketpp { template typename endpoint::connection_ptr -endpoint::create_connection() { +endpoint::create_connection(lib::error_code & ec) { m_alog->write(log::alevel::devel,"create_connection"); //scoped_lock_type lock(m_state_lock); @@ -81,8 +81,6 @@ endpoint::create_connection() { } con->set_max_http_body_size(m_max_http_body_size); - lib::error_code ec; - ec = transport_type::init(con); if (ec) { m_elog->write(log::elevel::fatal,ec.message()); @@ -103,13 +101,6 @@ void endpoint::interrupt(connection_hdl hdl, lib::error_code ec = con->interrupt(); } -template -void endpoint::interrupt(connection_hdl hdl) { - lib::error_code ec; - interrupt(hdl,ec); - if (ec) { throw exception(ec); } -} - template void endpoint::pause_reading(connection_hdl hdl, lib::error_code & ec) { @@ -119,13 +110,6 @@ void endpoint::pause_reading(connection_hdl hdl, lib::error_c ec = con->pause_reading(); } -template -void endpoint::pause_reading(connection_hdl hdl) { - lib::error_code ec; - pause_reading(hdl,ec); - if (ec) { throw exception(ec); } -} - template void endpoint::resume_reading(connection_hdl hdl, lib::error_code & ec) { @@ -135,13 +119,6 @@ void endpoint::resume_reading(connection_hdl hdl, lib::error_ ec = con->resume_reading(); } -template -void endpoint::resume_reading(connection_hdl hdl) { - lib::error_code ec; - resume_reading(hdl,ec); - if (ec) { throw exception(ec); } -} - template void endpoint::send_http_response(connection_hdl hdl, lib::error_code & ec) @@ -151,13 +128,6 @@ void endpoint::send_http_response(connection_hdl hdl, con->send_http_response(ec); } -template -void endpoint::send_http_response(connection_hdl hdl) { - lib::error_code ec; - send_http_response(hdl,ec); - if (ec) { throw exception(ec); } -} - template void endpoint::send(connection_hdl hdl, std::string const & payload, frame::opcode::value op, lib::error_code & ec) @@ -168,15 +138,6 @@ void endpoint::send(connection_hdl hdl, std::string const & p ec = con->send(payload,op); } -template -void endpoint::send(connection_hdl hdl, std::string const & payload, - frame::opcode::value op) -{ - lib::error_code ec; - send(hdl,payload,op,ec); - if (ec) { throw exception(ec); } -} - template void endpoint::send(connection_hdl hdl, void const * payload, size_t len, frame::opcode::value op, lib::error_code & ec) @@ -187,73 +148,113 @@ void endpoint::send(connection_hdl hdl, void const * payload, } template -void endpoint::send(connection_hdl hdl, void const * payload, - size_t len, frame::opcode::value op) +void endpoint::send(connection_hdl hdl, message_ptr msg, + lib::error_code & ec) { - lib::error_code ec; - send(hdl,payload,len,op,ec); - if (ec) { throw exception(ec); } + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + ec = con->send(msg); } template -void endpoint::send(connection_hdl hdl, message_ptr msg, +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} - ec = con->send(msg); + con->close(code,reason,ec); } template -void endpoint::send(connection_hdl hdl, message_ptr msg) { - lib::error_code ec; - send(hdl,msg,ec); - if (ec) { throw exception(ec); } +void endpoint::ping(connection_hdl hdl, std::string const & + payload, lib::error_code & ec) +{ + connection_ptr con = get_con_from_hdl(hdl,ec); + if (ec) {return;} + con->ping(payload,ec); } template -void endpoint::close(connection_hdl hdl, close::status::value - const code, std::string const & reason, +void endpoint::pong(connection_hdl hdl, std::string const & payload, lib::error_code & ec) { connection_ptr con = get_con_from_hdl(hdl,ec); if (ec) {return;} - con->close(code,reason,ec); + con->pong(payload,ec); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ +// If exceptions are enabled, define wrapper methods that throw exceptions + template -void endpoint::close(connection_hdl hdl, close::status::value - const code, std::string const & reason) +void endpoint::interrupt(connection_hdl hdl) { + lib::error_code ec; + interrupt(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::pause_reading(connection_hdl hdl) { + lib::error_code ec; + pause_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::resume_reading(connection_hdl hdl) { + lib::error_code ec; + resume_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send_http_response(connection_hdl hdl) { + lib::error_code ec; + send_http_response(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::send(connection_hdl hdl, std::string const & payload, + frame::opcode::value op) { lib::error_code ec; - close(hdl,code,reason,ec); + send(hdl,payload,op,ec); if (ec) { throw exception(ec); } } template -void endpoint::ping(connection_hdl hdl, std::string const & - payload, lib::error_code & ec) +void endpoint::send(connection_hdl hdl, void const * payload, + size_t len, frame::opcode::value op) { - connection_ptr con = get_con_from_hdl(hdl,ec); - if (ec) {return;} - con->ping(payload,ec); + lib::error_code ec; + send(hdl,payload,len,op,ec); + if (ec) { throw exception(ec); } } template -void endpoint::ping(connection_hdl hdl, std::string const & payload) +void endpoint::send(connection_hdl hdl, message_ptr msg) { + lib::error_code ec; + send(hdl,msg,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::close(connection_hdl hdl, close::status::value + const code, std::string const & reason) { lib::error_code ec; - ping(hdl,payload,ec); + close(hdl,code,reason,ec); if (ec) { throw exception(ec); } } template -void endpoint::pong(connection_hdl hdl, std::string const & payload, - lib::error_code & ec) +void endpoint::ping(connection_hdl hdl, std::string const & payload) { - connection_ptr con = get_con_from_hdl(hdl,ec); - if (ec) {return;} - con->pong(payload,ec); + lib::error_code ec; + ping(hdl,payload,ec); + if (ec) { throw exception(ec); } } template @@ -263,6 +264,7 @@ void endpoint::pong(connection_hdl hdl, std::string const & p pong(hdl,payload,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ } // namespace websocketpp diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/utilities_impl.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/utilities_impl.hpp index 6f86e22f58..dd4b51794b 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/utilities_impl.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/impl/utilities_impl.hpp @@ -34,33 +34,19 @@ namespace websocketpp { namespace utility { -inline std::string to_lower(std::string const & in) { - std::string out = in; - std::transform(out.begin(),out.end(),out.begin(),::tolower); - return out; -} - inline std::string to_hex(std::string const & input) { - std::string output; - std::string hex = "0123456789ABCDEF"; - - for (size_t i = 0; i < input.size(); i++) { - output += hex[(input[i] & 0xF0) >> 4]; - output += hex[input[i] & 0x0F]; - output += " "; - } - - return output; + return to_hex(input.c_str(), input.size()); } inline std::string to_hex(uint8_t const * input, size_t length) { std::string output; - std::string hex = "0123456789ABCDEF"; + output.reserve(length * 3); + char const * hex = "0123456789ABCDEF"; for (size_t i = 0; i < length; i++) { output += hex[(input[i] & 0xF0) >> 4]; output += hex[input[i] & 0x0F]; - output += " "; + output += ' '; } return output; diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/logger/basic.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/logger/basic.hpp index 84514130e7..4c9d836493 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/logger/basic.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/logger/basic.hpp @@ -58,33 +58,33 @@ namespace log { template class basic { public: - basic(channel_type_hint::value h = + basic(channel_type_hint::value h = channel_type_hint::access) : m_static_channels(0xffffffff) , m_dynamic_channels(0) , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} - basic(std::ostream * out) + basic(std::ostream * out) : m_static_channels(0xffffffff) , m_dynamic_channels(0) , m_out(out) {} - basic(level c, channel_type_hint::value h = + basic(level c, channel_type_hint::value h = channel_type_hint::access) : m_static_channels(c) , m_dynamic_channels(0) , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {} - basic(level c, std::ostream * out) + basic(level c, std::ostream * out) : m_static_channels(c) , m_dynamic_channels(0) , m_out(out) {} /// Destructor - ~basic() {} + ~basic() {} /// Copy constructor - basic(basic const & other) + basic(basic const & other) : m_static_channels(other.m_static_channels) , m_dynamic_channels(other.m_dynamic_channels) , m_out(other.m_out) @@ -97,7 +97,7 @@ class basic { #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ /// Move constructor - basic(basic && other) + basic(basic && other) : m_static_channels(other.m_static_channels) , m_dynamic_channels(other.m_dynamic_channels) , m_out(other.m_out) diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/message_buffer/message.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/message_buffer/message.hpp index da36e20012..3d94a63e28 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/message_buffer/message.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/message_buffer/message.hpp @@ -112,7 +112,7 @@ class message { , m_terminal(false) , m_compressed(false) { - m_payload.reserve(size); + m_payload.reserve(size + 16); } /// Return whether or not the message has been prepared for sending @@ -273,7 +273,7 @@ class message { * @param len The length of new payload in bytes. */ void set_payload(void const * payload, size_t len) { - m_payload.reserve(len); + m_payload.reserve(len + 16); char const * pl = static_cast(payload); m_payload.assign(pl, pl + len); } @@ -285,7 +285,7 @@ class message { * @param payload A string containing the data array to append. */ void append_payload(std::string const & payload) { - m_payload.append(payload); + append_payload(payload.data(), payload.size()); } /// Append payload data @@ -296,7 +296,7 @@ class message { * @param len The length of payload in bytes */ void append_payload(void const * payload, size_t len) { - m_payload.reserve(m_payload.size()+len); + m_payload.reserve(m_payload.size()+len + 16); m_payload.append(static_cast(payload),len); } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/processors/hybi13.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/processors/hybi13.hpp index 93b6409cfd..ca12439266 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/processors/hybi13.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/processors/hybi13.hpp @@ -88,11 +88,11 @@ class hybi13 : public processor { err_str_pair negotiate_extensions(request_type const & request) { return negotiate_extensions_helper(request); } - + err_str_pair negotiate_extensions(response_type const & response) { return negotiate_extensions_helper(response); } - + /// Extension negotiation helper function /** * This exists mostly because the code for requests and responses is @@ -137,11 +137,11 @@ class hybi13 : public processor { // if we have already successfully negotiated this extension // then skip any other requests to negotiate the same one - // with different parameters + // with different parameters if (m_permessage_deflate.is_enabled()) { continue; } - + // attempt to negotiate this offer neg_ret = m_permessage_deflate.negotiate(it->second); @@ -158,8 +158,8 @@ class hybi13 : public processor { lib::error_code ec = m_permessage_deflate.init(base::m_server); if (ec) { - // Negotiation succeeded but initialization failed this is - // an error that should stop negotiation of permessage + // Negotiation succeeded but initialization failed this is + // an error that should stop negotiation of permessage // deflate. Return the reason for the init failure ret.first = ec; @@ -174,7 +174,7 @@ class hybi13 : public processor { } } - // support for future extensions would go here. Should check the value of + // support for future extensions would go here. Should check the value of // ret.first before continuing. Might need to consider whether failure of // negotiation of an earlier extension should stop negotiation of subsequent // ones @@ -206,7 +206,7 @@ class hybi13 : public processor { * generic struct if other user input parameters to the processed handshake * are found. */ - lib::error_code process_handshake(request_type const & request, + lib::error_code process_handshake(request_type const & request, std::string const & subprotocol, response_type & response) const { std::string server_key = request.get_header("Sec-WebSocket-Key"); @@ -440,12 +440,12 @@ class hybi13 : public processor { ec = make_error_code(error::message_too_big); break; } - + m_data_msg = msg_metadata( m_msg_manager->get_message(op,m_bytes_needed), frame::get_masking_key(m_basic_header,m_extended_header) ); - + if (m_permessage_deflate.is_enabled()) { m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header)); } @@ -453,12 +453,12 @@ class hybi13 : public processor { // Fetch the underlying payload buffer from the data message we // are writing into. std::string & out = m_data_msg.msg_ptr->get_raw_payload(); - + if (out.size() + m_bytes_needed > base::m_max_message_size) { ec = make_error_code(error::message_too_big); break; } - + // Each frame starts a new masking key. All other state // remains between frames. m_data_msg.prepared_key = prepare_masking_key( @@ -467,7 +467,7 @@ class hybi13 : public processor { m_extended_header ) ); - + out.reserve(out.size() + m_bytes_needed); } m_current_msg = &m_data_msg; @@ -878,6 +878,12 @@ class hybi13 : public processor { return make_error_code(error::invalid_opcode); } + // Check for invalid opcodes + // TODO: unit tests for this? + if (frame::opcode::invalid(op)) { + return make_error_code(error::invalid_opcode); + } + // Check for fragmented control message if (frame::opcode::is_control(op) && !frame::get_fin(h)) { return make_error_code(error::fragmented_control); @@ -1003,7 +1009,7 @@ class hybi13 : public processor { out->set_header(frame::prepare_header(h,e)); std::copy(payload.begin(),payload.end(),o.begin()); } - + out->set_opcode(op); out->set_prepared(true); diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/client_endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/client_endpoint.hpp index 4d0c433b0c..94ae7ae0a5 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/client_endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/client_endpoint.hpp @@ -92,10 +92,14 @@ class client : public endpoint,config> { return connection_ptr(); } - connection_ptr con = endpoint_type::create_connection(); + connection_ptr con = endpoint_type::create_connection(ec); if (!con) { - ec = error::make_error_code(error::con_creation_failed); + // if the transport doesn't have a more specific error, set + // a generic one. + if (!ec) { + ec = error::make_error_code(error::con_creation_failed); + } return con; } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/server_endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/server_endpoint.hpp index 9cc652f75c..04fee18f9a 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/server_endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/roles/server_endpoint.hpp @@ -64,6 +64,9 @@ class server : public endpoint,config> { /// Type of the endpoint component of this server typedef endpoint endpoint_type; + /// The type and signature of the callback passed to the start_accept method + typedef lib::function accept_loop_handler; + friend class connection; explicit server() : endpoint_type(true) @@ -72,11 +75,11 @@ class server : public endpoint,config> { } /// Destructor - ~server() {} + ~server() {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no copy constructor because endpoints are not copyable - server(server &) = delete; + server(server &) = delete; // no copy assignment operator because endpoints are not copyable server & operator=(server const &) = delete; @@ -84,7 +87,7 @@ class server : public endpoint,config> { #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ /// Move constructor - server(server && o) : endpoint,config>(std::move(o)) {} + server(server && o) : endpoint,config>(std::move(o)) {} #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ // no move assignment operator because of const member variables @@ -101,10 +104,34 @@ class server : public endpoint,config> { * Note: The connection must either be started or terminated using * connection::terminate in order to avoid memory leaks. * + * @deprecated 0.9.0 use `get_connection(lib::error_code &)` instead. + * + * @see `get_connection(lib::error_code &)` for an alternative that + * returns a detailed error code on failure. + * * @return A pointer to the new connection. */ connection_ptr get_connection() { - return endpoint_type::create_connection(); + lib::error_code ec; + return endpoint_type::create_connection(ec); + } + + /// Create and initialize a new connection + /** + * The connection will be initialized and ready to begin. Call its start() + * method to begin the processing loop. + * + * Note: The connection must either be started or terminated using + * connection::terminate in order to avoid memory leaks. + * + * @since 0.9.0 + * + * @param [out] ec A status code that indicates why the failure occurred + * if the returned pointer is blank. + * @return A pointer to the new connection. + */ + connection_ptr get_connection(lib::error_code & ec) { + return endpoint_type::create_connection(ec); } /// Starts the server's async connection acceptance loop (exception free) @@ -115,7 +142,17 @@ class server : public endpoint,config> { * * Refer to documentation for the transport policy you are using for * instructions on how to stop this acceptance loop. + * + * Error handling: + * start_accept will return an error via the `ec` parameter if there is a + * problem starting the accept loop. Once successfully started the loop will + * continue to renew itself after each connection. This method has no way of + * delivering that happen after the loop is started. Use + * `start_accept(accept_loop_handler)` instead to get full error information + * no matter when the async loop ends. * + * @deprecated use `start_accept(accept_loop_handler) instead + * * @param [out] ec A status code indicating an error, if any. */ void start_accept(lib::error_code & ec) { @@ -125,7 +162,7 @@ class server : public endpoint,config> { } ec = lib::error_code(); - connection_ptr con = get_connection(); + connection_ptr con = get_connection(ec); if (!con) { ec = error::make_error_code(error::con_creation_failed); @@ -134,7 +171,7 @@ class server : public endpoint,config> { transport_type::async_accept( lib::static_pointer_cast(con), - lib::bind(&type::handle_accept,this,con,lib::placeholders::_1), + lib::bind(&type::handle_accept_legacy,this,con,lib::placeholders::_1), ec ); @@ -145,14 +182,95 @@ class server : public endpoint,config> { } } - /// Starts the server's async connection acceptance loop + /// Starts the server's async connection acceptance loop (exception free) /** - * Initiates the server connection acceptance loop. Must be called after - * listen. This method will have no effect until the underlying io_service - * starts running. It may be called after the io_service is already running. + * Initiates the server connection acceptance loop. Requires a transport + * policy that supports an asyncronous listen+accept loop. Must be called + * while the endpoint is listening (or start_accept will return immediately + * with an error that the server is not listening). + * + * Consult the documentation for the underlying transport for information + * about exactly when this code will start running, when in the transport + * event loop it makes sense to call it, and for instructions on how to + * stop this acceptance loop. * - * Refer to documentation for the transport policy you are using for - * instructions on how to stop this acceptance loop. + * Error handling: + * start_accept will attempt to start an asyncronous acceptance loop that + * accepts a connection and then re-issues a new accept command. If this loop + * ends or fails for any reason (including immediately) the `completion_handler` + * will be called with two status codes. The first is the library level status + * code the second is the underlying transport status code (if any). + * + * @since 0.9.0 + * + * @param completion_handler A handler function to be called when the async + * accept loop ends. + */ + void start_accept(accept_loop_handler completion_handler) { + // This check will happen again in async_accept but if we do it here we can + // avoid setting up and tearing down a connection if we know that we can't + // actually accept a connection. + if (!transport_type::is_listening()) { + completion_handler(error::make_error_code(error::transport_error), + error::make_error_code(error::async_accept_not_listening)); + return; + } + + lib::error_code tec; + connection_ptr con = get_connection(tec); + + if (!con) { + completion_handler(error::make_error_code(error::con_creation_failed),tec); + return; + } + + transport_type::async_accept( + lib::static_pointer_cast(con), + lib::bind(&type::handle_accept,this, + con, + completion_handler, + lib::placeholders::_1), + tec + ); + + if (tec) { + if (con) { + // If the connection was constructed but the accept failed, + // terminate the connection to prevent memory leaks. + con->terminate(lib::error_code()); + } + + endpoint_type::m_elog->write(log::elevel::rerror, + "Async_accept failed: "+tec.message()); + + // let the end user know about the error + completion_handler(error::make_error_code(error::transport_error),tec); + } + } + +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + /// Starts the server's async connection acceptance loop (exception) + /** + * Initiates the server connection acceptance loop. Requires a transport + * policy that supports an asyncronous listen+accept loop. Must be called + * while the endpoint is listening (or start_accept will return immediately + * with an error that the server is not listening). + * + * Consult the documentation for the underlying transport for information + * about exactly when this code will start running, when in the transport + * event loop it makes sense to call it, and for instructions on how to + * stop this acceptance loop. + * + * Error handling: + * start_accept will throw an exception if there is a problem starting the + * accept loop. Once successfully started the loop will continue to renew + * itself after each connection. This method has no way of delivering that + * happen after the loop is started. Use `start_accept(accept_loop_handler)` + * instead to get full error information no matter when the async loop ends. + * + * @deprecated use `start_accept(accept_loop_handler)` instead + * + * @exception websocketpp::exception If the accept loop fails to be set up. */ void start_accept() { lib::error_code ec; @@ -161,9 +279,10 @@ class server : public endpoint,config> { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ - /// Handler callback for start_accept - void handle_accept(connection_ptr con, lib::error_code const & ec) { + /// Handler callback for start_accept (deprecated) + void handle_accept_legacy(connection_ptr con, lib::error_code const & ec) { if (ec) { con->terminate(ec); @@ -188,6 +307,37 @@ class server : public endpoint,config> { "Restarting async_accept loop failed: "+ec.message()); } } + + /// Handler callback for start_accept + void handle_accept(connection_ptr con, + accept_loop_handler completion_handler, + lib::error_code const & tec) + { + // deal with the newly accepted connection + if (tec) { + // terminate the connection and pass the transport error + // code along + con->terminate(tec); + + // log the transport error before restarting the loop + if (tec == error::operation_canceled) { + endpoint_type::m_elog->write(log::elevel::info, + "handle_accept error: "+tec.message()); + } else { + endpoint_type::m_elog->write(log::elevel::rerror, + "handle_accept error: "+tec.message()); + } + } else { + con->start(); + } + + // todo: are there any `tec` codes that should prompt us to end + // without restarting the loop? + + // attempt to restart the async accept loop for the next connection + // this method will deliver any errors via the completion_handler + start_accept(completion_handler); + } }; } // namespace websocketpp diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/sha1/sha1.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/sha1/sha1.hpp index 6b48d9578c..fba6cc4c54 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/sha1/sha1.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/sha1/sha1.hpp @@ -36,6 +36,8 @@ under the same license as the original, which is listed below. #ifndef SHA1_DEFINED #define SHA1_DEFINED +#include + namespace websocketpp { namespace sha1 { diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/connection.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/connection.hpp index 57dda74a2c..c9fa7d4620 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/connection.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/connection.hpp @@ -51,7 +51,8 @@ #include #include -namespace websocketpp { +namespace websocketpp +{ namespace transport { namespace asio { @@ -86,11 +87,12 @@ class connection : public config::socket_type::socket_con_type { typedef typename response_type::ptr response_ptr; /// Type of a pointer to the Asio io_service being used - typedef lib::asio::io_service * io_service_ptr; + typedef lib::asio::io_context* io_service_ptr; /// Type of a pointer to the Asio io_service::strand being used - typedef lib::shared_ptr strand_ptr; + typedef lib::shared_ptr strand_ptr; /// Type of a pointer to the Asio timer class typedef lib::shared_ptr timer_ptr; + using clk = lib::chrono::steady_clock; // connection is friends with its associated endpoint to allow the endpoint // to call private/protected utility methods that we don't want to expose @@ -194,12 +196,14 @@ class connection : public config::socket_type::socket_con_type { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set the proxy to connect through (exception) void set_proxy(std::string const & uri) { lib::error_code ec; set_proxy(uri,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set the basic auth credentials to use (exception free) /** @@ -228,6 +232,7 @@ class connection : public config::socket_type::socket_con_type { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set the basic auth credentials to use (exception) void set_proxy_basic_auth(std::string const & username, std::string const & password) @@ -236,6 +241,7 @@ class connection : public config::socket_type::socket_con_type { set_proxy_basic_auth(username,password,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set the proxy timeout duration (exception free) /** @@ -257,12 +263,14 @@ class connection : public config::socket_type::socket_con_type { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Set the proxy timeout duration (exception) void set_proxy_timeout(long duration) { lib::error_code ec; set_proxy_timeout(duration,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ std::string const & get_proxy() const { return m_proxy; @@ -317,7 +325,7 @@ class connection : public config::socket_type::socket_con_type { lib::asio::milliseconds(duration)) ); - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { new_timer->async_wait(m_strand->wrap(lib::bind( &type::handle_timer, get_shared(), new_timer, @@ -461,8 +469,8 @@ class connection : public config::socket_type::socket_con_type { lib::error_code init_asio (io_service_ptr io_service) { m_io_service = io_service; - if (config::enable_multithreading) { - m_strand.reset(new lib::asio::io_service::strand(*io_service)); + if constexpr(config::enable_multithreading) { + m_strand.reset(new lib::asio::io_context::strand(*io_service)); } lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, @@ -572,12 +580,12 @@ class connection : public config::socket_type::socket_con_type { void handle_post_init(timer_ptr post_timer, init_handler callback, lib::error_code const & ec) { - if (ec == transport::error::operation_aborted || - (post_timer && lib::asio::is_neg(post_timer->expires_from_now()))) - { - m_alog->write(log::alevel::devel,"post_init cancelled"); - return; - } + if(ec == transport::error::operation_aborted + || (post_timer && lib::asio::is_neg(post_timer->expiry() - clk::now()))) + { + m_alog->write(log::alevel::devel, "post_init cancelled"); + return; + } if (post_timer) { post_timer->cancel(); @@ -625,7 +633,7 @@ class connection : public config::socket_type::socket_con_type { ); // Send proxy request - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_write( socket_con_type::get_next_layer(), m_bufs, @@ -678,8 +686,8 @@ class connection : public config::socket_type::socket_con_type { // Timer expired or the operation was aborted for some reason. // Whatever aborted it will be issuing the callback so we are safe to // return - if (ec == lib::asio::error::operation_aborted || - lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + if(ec == lib::asio::error::operation_aborted + || lib::asio::is_neg(m_proxy_data->timer->expiry().time_since_epoch())) { m_elog->write(log::elevel::devel,"write operation aborted"); return; @@ -703,12 +711,11 @@ class connection : public config::socket_type::socket_con_type { if (!m_proxy_data) { m_elog->write(log::elevel::library, "assertion failed: !m_proxy_data in asio::connection::proxy_read"); - m_proxy_data->timer->cancel(); callback(make_error_code(error::general)); return; } - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_read_until( socket_con_type::get_next_layer(), m_proxy_data->read_buf, @@ -750,8 +757,8 @@ class connection : public config::socket_type::socket_con_type { // Timer expired or the operation was aborted for some reason. // Whatever aborted it will be issuing the callback so we are safe to // return - if (ec == lib::asio::error::operation_aborted || - lib::asio::is_neg(m_proxy_data->timer->expires_from_now())) + if(ec == lib::asio::error::operation_aborted + || lib::asio::is_neg(m_proxy_data->timer->expiry() - clk::now())) { m_elog->write(log::elevel::devel,"read operation aborted"); return; @@ -772,9 +779,19 @@ class connection : public config::socket_type::socket_con_type { return; } + // todo: switch this to using non-istream based consume std::istream input(&m_proxy_data->read_buf); - m_proxy_data->res.consume(input); + lib::error_code istream_ec; + m_proxy_data->res.consume(input, istream_ec); + if (istream_ec) { + // there was an error while reading from the proxy + m_elog->write(log::elevel::info, + "An HTTP handling error occurred while reading a response from the proxy server: "+istream_ec.message()); + // todo: do we need to translate this error? + callback(istream_ec); + return; + } if (!m_proxy_data->res.headers_ready()) { // we read until the headers were done in theory but apparently @@ -836,7 +853,7 @@ class connection : public config::socket_type::socket_con_type { return; }*/ - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_read( socket_con_type::get_socket(), lib::asio::buffer(buf,len), @@ -906,7 +923,7 @@ class connection : public config::socket_type::socket_con_type { void async_write(const char* buf, size_t len, write_handler handler) { m_bufs.push_back(lib::asio::buffer(buf,len)); - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_write( socket_con_type::get_socket(), m_bufs, @@ -939,11 +956,14 @@ class connection : public config::socket_type::socket_con_type { void async_write(std::vector const & bufs, write_handler handler) { std::vector::const_iterator it; + // todo: check if this underlying socket supports efficient scatter/gather io + // if not, coalesce buffers before we send to the underlying transport. + for (it = bufs.begin(); it != bufs.end(); ++it) { m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len)); } - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_write( socket_con_type::get_socket(), m_bufs, @@ -1011,19 +1031,19 @@ class connection : public config::socket_type::socket_con_type { * This needs to be thread safe */ lib::error_code interrupt(interrupt_handler handler) { - if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + if constexpr(config::enable_multithreading) { + boost::asio::post(*m_io_service, m_strand->wrap(handler)); } else { - m_io_service->post(handler); + boost::asio::post(*m_io_service, handler); } return lib::error_code(); } lib::error_code dispatch(dispatch_handler handler) { - if (config::enable_multithreading) { - m_io_service->post(m_strand->wrap(handler)); + if constexpr(config::enable_multithreading) { + boost::asio::post(*m_io_service, m_strand->wrap(handler)); } else { - m_io_service->post(handler); + boost::asio::post(*m_io_service, handler); } return lib::error_code(); } @@ -1094,11 +1114,11 @@ class connection : public config::socket_type::socket_con_type { void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler callback, lib::asio::error_code const & ec) { - if (ec == lib::asio::error::operation_aborted || - lib::asio::is_neg(shutdown_timer->expires_from_now())) - { - m_alog->write(log::alevel::devel,"async_shutdown cancelled"); - return; + if(ec == lib::asio::error::operation_aborted + || lib::asio::is_neg(shutdown_timer->expiry() - clk::now())) + { + m_alog->write(log::alevel::devel, "async_shutdown cancelled"); + return; } shutdown_timer->cancel(); diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/endpoint.hpp index 94509adb3b..0f98107fc4 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/endpoint.hpp @@ -28,15 +28,15 @@ #ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP #define WEBSOCKETPP_TRANSPORT_ASIO_HPP -#include -#include -#include - -#include -#include +#include #include #include +#include +#include +#include +#include +#include #include #include @@ -78,7 +78,7 @@ class endpoint : public config::socket_type { typedef typename transport_con_type::ptr transport_con_ptr; /// Type of a pointer to the ASIO io_service being used - typedef lib::asio::io_service * io_service_ptr; + typedef lib::asio::io_context * io_service_ptr; /// Type of a shared pointer to the acceptor being used typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used @@ -86,18 +86,21 @@ class endpoint : public config::socket_type { /// Type of timer handle typedef lib::shared_ptr timer_ptr; /// Type of a shared pointer to an io_service work object - typedef lib::shared_ptr work_ptr; + typedef lib::shared_ptr< + lib::asio::executor_work_guard> + work_ptr; /// Type of socket pre-bind handler typedef lib::function tcp_pre_bind_handler; + using clk = lib::chrono::steady_clock; // generate and manage our own io_service explicit endpoint() - : m_io_service(NULL) - , m_external_io_service(false) - , m_listen_backlog(lib::asio::socket_base::max_connections) - , m_reuse_addr(false) - , m_state(UNINITIALIZED) + : m_io_service(NULL) + , m_external_io_service(false) + , m_listen_backlog(lib::asio::socket_base::max_listen_connections) + , m_reuse_addr(false) + , m_state(UNINITIALIZED) { //std::cout << "transport::asio::endpoint constructor" << std::endl; } @@ -128,18 +131,18 @@ class endpoint : public config::socket_type { #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ - endpoint (endpoint && src) - : config::socket_type(std::move(src)) - , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) - , m_tcp_post_init_handler(src.m_tcp_post_init_handler) - , m_io_service(src.m_io_service) - , m_external_io_service(src.m_external_io_service) - , m_acceptor(src.m_acceptor) - , m_listen_backlog(lib::asio::socket_base::max_connections) - , m_reuse_addr(src.m_reuse_addr) - , m_elog(src.m_elog) - , m_alog(src.m_alog) - , m_state(src.m_state) + endpoint(endpoint&& src) + : config::socket_type(std::move(src)) + , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler) + , m_tcp_post_init_handler(src.m_tcp_post_init_handler) + , m_io_service(src.m_io_service) + , m_external_io_service(src.m_external_io_service) + , m_acceptor(src.m_acceptor) + , m_listen_backlog(lib::asio::socket_base::max_listen_connections) + , m_reuse_addr(src.m_reuse_addr) + , m_elog(src.m_elog) + , m_alog(src.m_alog) + , m_state(src.m_state) { src.m_io_service = NULL; src.m_external_io_service = false; @@ -159,7 +162,7 @@ class endpoint : public config::socket_type { rhs.m_io_service = NULL; rhs.m_external_io_service = false; rhs.m_acceptor = NULL; - rhs.m_listen_backlog = lib::asio::socket_base::max_connections; + rhs.m_listen_backlog = lib::asio::socket_base::max_listen_connections; rhs.m_state = UNINITIALIZED; // TODO: this needs to be updated @@ -201,6 +204,7 @@ class endpoint : public config::socket_type { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// initialize asio transport with external io_service /** * Initialize the ASIO transport policy for this endpoint using the provided @@ -214,6 +218,7 @@ class endpoint : public config::socket_type { init_asio(ptr,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Initialize asio transport with internal io_service (exception free) /** @@ -230,15 +235,16 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio(service.get(), ec); if( !ec ) service.release(); // Call was successful, transfer ownership m_external_io_service = false; } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Initialize asio transport with internal io_service /** * This method of initialization will allocate and use an internally managed @@ -252,15 +258,16 @@ class endpoint : public config::socket_type { // TODO: remove the use of auto_ptr when C++98/03 support is no longer // necessary. #ifdef _WEBSOCKETPP_CPP11_MEMORY_ - lib::unique_ptr service(new lib::asio::io_service()); + lib::unique_ptr service(new lib::asio::io_context()); #else - lib::auto_ptr service(new lib::asio::io_service()); + lib::auto_ptr service(new lib::asio::io_context()); #endif init_asio( service.get() ); // If control got this far without an exception, then ownership has successfully been taken service.release(); m_external_io_service = false; } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Sets the tcp pre bind handler /** @@ -330,7 +337,7 @@ class endpoint : public config::socket_type { * * New values affect future calls to listen only. * - * The default value is specified as *::asio::socket_base::max_connections + * The default value is specified as *::asio::socket_base::max_listen_connections * which uses the operating system defined maximum queue length. Your OS * may restrict or silently lower this value. A value of zero may cause * all connections to be rejected. @@ -375,7 +382,7 @@ class endpoint : public config::socket_type { * * @return A reference to the endpoint's io_service */ - lib::asio::io_service & get_io_service() { + lib::asio::io_context & get_io_service() { return *m_io_service; } @@ -449,20 +456,6 @@ class endpoint : public config::socket_type { ec = lib::error_code(); } - - - /// Set up endpoint for listening manually - /** - * Bind the internal acceptor using the settings specified by the endpoint e - * - * @param ep An endpoint to read settings from - */ - void listen(lib::asio::ip::tcp::endpoint const & ep) { - lib::error_code ec; - listen(ep,ec); - if (ec) { throw exception(ec); } - } - /// Set up endpoint for listening with protocol and port (exception free) /** * Bind the internal acceptor using the given internet protocol and port. @@ -485,26 +478,6 @@ class endpoint : public config::socket_type { listen(ep,ec); } - /// Set up endpoint for listening with protocol and port - /** - * Bind the internal acceptor using the given internet protocol and port. - * The endpoint must have been initialized by calling init_asio before - * listening. - * - * Common options include: - * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() - * - IPv4 only: lib::asio::ip::tcp::v4() - * - * @param internet_protocol The internet protocol to use. - * @param port The port to listen on. - */ - template - void listen(InternetProtocol const & internet_protocol, uint16_t port) - { - lib::asio::ip::tcp::endpoint ep(internet_protocol, port); - listen(ep); - } - /// Set up endpoint for listening on a port (exception free) /** * Bind the internal acceptor using the given port. The IPv6 protocol with @@ -521,22 +494,6 @@ class endpoint : public config::socket_type { listen(lib::asio::ip::tcp::v6(), port, ec); } - /// Set up endpoint for listening on a port - /** - * Bind the internal acceptor using the given port. The IPv6 protocol with - * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use - * the overload that allows specifying the protocol explicitly. - * - * The endpoint must have been initialized by calling init_asio before - * listening. - * - * @param port The port to listen on. - * @param ec Set to indicate what error occurred, if any. - */ - void listen(uint16_t port) { - listen(lib::asio::ip::tcp::v6(), port); - } - /// Set up endpoint for listening on a host and service (exception free) /** * Bind the internal acceptor using the given host and service. More details @@ -547,12 +504,20 @@ class endpoint : public config::socket_type { * The endpoint must have been initialized by calling init_asio before * listening. * + * Once listening the underlying io_service will be kept open indefinitely. + * Calling endpoint::stop_listening will stop the endpoint from accepting + * new connections. See the documentation for stop listening for more details + * about shutting down Asio Transport based endpoints. + * + * @see stop_listening(lib::error_code &) + * * @param host A string identifying a location. May be a descriptive name or * a numeric address string. * @param service A string identifying the requested service. This may be a * descriptive name or a numeric string corresponding to a port number. * @param ec Set to indicate what error occurred, if any. */ +#if BOOST_VERSION < 108700 void listen(std::string const & host, std::string const & service, lib::error_code & ec) { @@ -569,34 +534,19 @@ class endpoint : public config::socket_type { } listen(*endpoint_iterator,ec); } +#endif - /// Set up endpoint for listening on a host and service + /// Stop listening (exception free) /** - * Bind the internal acceptor using the given host and service. More details - * about what host and service can be are available in the Asio - * documentation for ip::basic_resolver_query::basic_resolver_query's - * constructors. + * Stop listening and accepting new connections. * - * The endpoint must have been initialized by calling init_asio before - * listening. + * If the endpoint needs to shut down fully (i.e. close all connections) + * this member function is necessary but not sufficient. In addition to + * stopping listening, individual connections will need to be ended via + * their respective connection::close. * - * @param host A string identifying a location. May be a descriptive name or - * a numeric address string. - * @param service A string identifying the requested service. This may be a - * descriptive name or a numeric string corresponding to a port number. - * @param ec Set to indicate what error occurred, if any. - */ - void listen(std::string const & host, std::string const & service) - { - lib::error_code ec; - listen(host,service,ec); - if (ec) { throw exception(ec); } - } - - /// Stop listening (exception free) - /** - * Stop listening and accepting new connections. This will not end any - * existing connections. + * For more details on clean closing, please refer to @ref clean_close + * "Cleanly closing Asio Transport based endpoints" * * @since 0.3.0-alpha4 * @param ec A status code indicating an error, if any. @@ -615,6 +565,87 @@ class endpoint : public config::socket_type { ec = lib::error_code(); } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ + // if exceptions are avaliable, define listen overloads that use them + + /// Set up endpoint for listening manually + /** + * Bind the internal acceptor using the settings specified by the endpoint e + * + * @param ep An endpoint to read settings from + */ + void listen(lib::asio::ip::tcp::endpoint const & ep) { + lib::error_code ec; + listen(ep,ec); + if (ec) { throw exception(ec); } + } + + /// Set up endpoint for listening with protocol and port + /** + * Bind the internal acceptor using the given internet protocol and port. + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Common options include: + * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6() + * - IPv4 only: lib::asio::ip::tcp::v4() + * + * @param internet_protocol The internet protocol to use. + * @param port The port to listen on. + */ + template + void listen(InternetProtocol const & internet_protocol, uint16_t port) + { + lib::asio::ip::tcp::endpoint ep(internet_protocol, port); + listen(ep); + } + + /// Set up endpoint for listening on a port + /** + * Bind the internal acceptor using the given port. The IPv6 protocol with + * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use + * the overload that allows specifying the protocol explicitly. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * @param port The port to listen on. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(uint16_t port) { + listen(lib::asio::ip::tcp::v6(), port); + } + + /// Set up endpoint for listening on a host and service + /** + * Bind the internal acceptor using the given host and service. More + * details about what host and service can be are available in the Asio + * documentation for ip::basic_resolver_query::basic_resolver_query's + * constructors. + * + * The endpoint must have been initialized by calling init_asio before + * listening. + * + * Once listening the underlying io_service will be kept open indefinitely. + * Calling endpoint::stop_listening will stop the endpoint from accepting + * new connections. See the documentation for stop listening for more + * details about shutting down Asio Transport based endpoints. + * + * @see stop_listening() + * + * @param host A string identifying a location. May be a descriptive name + * or a numeric address string. + * @param service A string identifying the requested service. This may be a + * descriptive name or a numeric string corresponding to a port number. + * @param ec Set to indicate what error occurred, if any. + */ + void listen(std::string const & host, std::string const & service) + { + lib::error_code ec; + listen(host,service,ec); + if (ec) { throw exception(ec); } + } + /// Stop listening /** * Stop listening and accepting new connections. This will not end any @@ -627,6 +658,7 @@ class endpoint : public config::socket_type { stop_listening(ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ /// Check if the endpoint is listening /** @@ -666,7 +698,7 @@ class endpoint : public config::socket_type { /// wraps the reset method of the internal io_service object void reset() { - m_io_service->reset(); + m_io_service->restart(); } /// wraps the stopped method of the internal io_service object @@ -687,7 +719,9 @@ class endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work.reset(new lib::asio::io_service::work(*m_io_service)); + m_work.reset( + new lib::asio::executor_work_guard( + m_io_service->get_executor())); } /// Clears the endpoint's perpetual flag, allowing it to exit when empty @@ -776,7 +810,7 @@ class endpoint : public config::socket_type { m_alog->write(log::alevel::devel, "asio::async_accept"); - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { m_acceptor->async_accept( tcon->get_raw_socket(), tcon->get_strand()->wrap(lib::bind( @@ -799,6 +833,7 @@ class endpoint : public config::socket_type { } } +#ifndef _WEBSOCKETPP_NO_EXCEPTIONS_ /// Accept the next connection attempt and assign it to con. /** * @param tcon The connection to accept into. @@ -809,6 +844,7 @@ class endpoint : public config::socket_type { async_accept(tcon,callback,ec); if (ec) { throw exception(ec); } } +#endif // _WEBSOCKETPP_NO_EXCEPTIONS_ protected: /// Initialize logging /** @@ -883,7 +919,7 @@ class endpoint : public config::socket_type { port = pu->get_port_str(); } - tcp::resolver::query query(host,port); + boost::asio::ip::basic_resolver_query query(host,port); if (m_alog->static_test(log::alevel::devel)) { m_alog->write(log::alevel::devel, @@ -903,9 +939,9 @@ class endpoint : public config::socket_type { ) ); - if (config::enable_multithreading) { + if constexpr (config::enable_multithreading) { m_resolver->async_resolve( - query, + query.host_name(), query.service_name(), tcon->get_strand()->wrap(lib::bind( &type::handle_resolve, this, @@ -918,7 +954,7 @@ class endpoint : public config::socket_type { ); } else { m_resolver->async_resolve( - query, + query.host_name(), query.service_name(), lib::bind( &type::handle_resolve, this, @@ -964,16 +1000,17 @@ class endpoint : public config::socket_type { callback(ret_ec); } + void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer, connect_handler callback, lib::asio::error_code const & ec, - lib::asio::ip::tcp::resolver::iterator iterator) + boost::asio::ip::basic_resolver_results iterator) { - if (ec == lib::asio::error::operation_aborted || - lib::asio::is_neg(dns_timer->expires_from_now())) - { - m_alog->write(log::alevel::devel,"async_resolve cancelled"); - return; - } + if(ec == lib::asio::error::operation_aborted + || lib::asio::is_neg(dns_timer->expiry() - clk::now())) + { + m_alog->write(log::alevel::devel, "async_resolve cancelled"); + return; + } dns_timer->cancel(); @@ -986,9 +1023,7 @@ class endpoint : public config::socket_type { if (m_alog->static_test(log::alevel::devel)) { std::stringstream s; s << "Async DNS resolve successful. Results: "; - - lib::asio::ip::tcp::resolver::iterator it, end; - for (it = iterator; it != end; ++it) { + for (auto it = iterator.begin(); it != iterator.end(); ++it) { s << (*it).endpoint() << " "; } @@ -1011,7 +1046,7 @@ class endpoint : public config::socket_type { ) ); - if (config::enable_multithreading) { + if constexpr(config::enable_multithreading) { lib::asio::async_connect( tcon->get_raw_socket(), iterator, @@ -1076,12 +1111,12 @@ class endpoint : public config::socket_type { void handle_connect(transport_con_ptr tcon, timer_ptr con_timer, connect_handler callback, lib::asio::error_code const & ec) { - if (ec == lib::asio::error::operation_aborted || - lib::asio::is_neg(con_timer->expires_from_now())) - { - m_alog->write(log::alevel::devel,"async_connect cancelled"); - return; - } + if(ec == lib::asio::error::operation_aborted + || lib::asio::is_neg(con_timer->expiry() - clk::now())) + { + m_alog->write(log::alevel::devel, "async_connect cancelled"); + return; + } con_timer->cancel(); diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/none.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/none.hpp index 6c7d352410..51b8a16d22 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/none.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/none.hpp @@ -63,9 +63,9 @@ class connection : public lib::enable_shared_from_this { typedef lib::shared_ptr ptr; /// Type of a pointer to the Asio io_service being used - typedef lib::asio::io_service* io_service_ptr; + typedef lib::asio::io_context* io_service_ptr; /// Type of a pointer to the Asio io_service strand being used - typedef lib::shared_ptr strand_ptr; + typedef lib::shared_ptr strand_ptr; /// Type of the ASIO socket being used typedef lib::asio::ip::tcp::socket socket_type; /// Type of a shared pointer to the socket being used. @@ -156,7 +156,8 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service + * Asio components to the io_service. At this stage the connection is + * speculative, the server may not have actually received a new connection. * * @param service A pointer to the endpoint's io_service * @param strand A shared pointer to the connection's asio strand @@ -170,10 +171,6 @@ class connection : public lib::enable_shared_from_this { m_socket.reset(new lib::asio::ip::tcp::socket(*service)); - if (m_socket_init_handler) { - m_socket_init_handler(m_hdl, *m_socket); - } - m_state = READY; return lib::error_code(); @@ -194,7 +191,7 @@ class connection : public lib::enable_shared_from_this { /// Pre-initialize security policy /** - * Called by the transport after a new connection is created to initialize + * Called by the transport after a new connection is accepted to initialize * the socket component of the connection. This method is not allowed to * write any bytes to the wire. This initialization happens before any * proxies or other intermediate wrappers are negotiated. @@ -207,6 +204,10 @@ class connection : public lib::enable_shared_from_this { return; } + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, *m_socket); + } + m_state = READING; callback(lib::error_code()); diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/tls.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/tls.hpp index 04ac379036..e003a6787f 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/tls.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/asio/security/tls.hpp @@ -72,9 +72,9 @@ class connection : public lib::enable_shared_from_this { /// Type of a shared pointer to the ASIO socket being used typedef lib::shared_ptr socket_ptr; /// Type of a pointer to the ASIO io_service being used - typedef lib::asio::io_service * io_service_ptr; + typedef lib::asio::io_context * io_service_ptr; /// Type of a pointer to the ASIO io_service strand being used - typedef lib::shared_ptr strand_ptr; + typedef lib::shared_ptr strand_ptr; /// Type of a shared pointer to the ASIO TLS context being used typedef lib::shared_ptr context_ptr; @@ -195,10 +195,6 @@ class connection : public lib::enable_shared_from_this { } m_socket.reset(new socket_type(*service, *m_context)); - if (m_socket_init_handler) { - m_socket_init_handler(m_hdl, get_socket()); - } - m_io_service = service; m_strand = strand; m_is_server = is_server; @@ -247,6 +243,9 @@ class connection : public lib::enable_shared_from_this { } } #endif + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, get_socket()); + } callback(lib::error_code()); } diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/base/endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/base/endpoint.hpp index 4ed3e70fa2..ad77238705 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/base/endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/base/endpoint.hpp @@ -57,11 +57,22 @@ namespace websocketpp { * is a pointer to the transport connection component of the connection. When * complete, `handler` should be called with the the connection's * `connection_hdl` and any error that occurred. + * note: optional, client transports only * + * **async_accept**\n + * todo: + * note: optional, server transports only + * * **init_logging** * `void init_logging(const lib::shared_ptr& a, const lib::shared_ptr& e)`\n * Called once after construction to provide pointers to the endpoint's access * and error loggers. These may be stored and used to log messages or ignored. + * + * + * **is_listening** + * `bool is_listening()` + * Server roles only. Called to determine if the server is listening and that calls to + * start_accept / async_accept are valid. */ namespace transport { diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/stub/endpoint.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/stub/endpoint.hpp index 3bbb78f354..eb6570a4ca 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/stub/endpoint.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/transport/stub/endpoint.hpp @@ -89,6 +89,14 @@ class endpoint { bool is_secure() const { return false; } + + /// Tests whether or not the transport is currently listening for new connections + /** + * Server roles only + */ + bool is_listening() const { + return false; + } protected: /// Initialize logging /** @@ -103,7 +111,7 @@ class endpoint { * @param a A pointer to the access logger to use. * @param e A pointer to the error logger to use. */ - void init_logging(alog_type * a, elog_type * e) {} + void init_logging(lib::shared_ptr, lib::shared_ptr) {} /// Initiate a new connection /** @@ -116,6 +124,14 @@ class endpoint { cb(make_error_code(error::not_implemented)); } + /// Accept a new connection + /** + * Server roles only + */ + void async_accept(transport_con_ptr tcon, accept_handler cb, std::error_code & ec) { + ec = make_error_code(error::not_implemented); + } + /// Initialize a connection /** * Init is called by an endpoint once for each newly created connection. diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/uri.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/uri.hpp index c0b8b0cbd4..b2e6ebaaf0 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/uri.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/uri.hpp @@ -46,9 +46,357 @@ static uint16_t const uri_default_port = 80; /// Default port for wss:// static uint16_t const uri_default_secure_port = 443; + + +/// A group of helper methods for parsing and validating URIs against RFC 3986 +namespace uri_helper { + +/// RFC3986 unreserved character test +/** + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered `unreserved` + */ +inline bool unreserved(char c) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + return true; + } else if (c >= '0' && c <= '9') { + return true; + } else if (c == '-' || c == '.' || c == '_' || c == '~') { + return true; + } else { + return false; + } +} + +/// RFC3986 generic delimiter character test +/** + * @param c the char to test + * @return True if the character is considered a generic delimiter + */ +inline bool gen_delim(char c) { + switch(c) { + case ':': + case '/': + case '?': + case '#': + case '[': + case ']': + case '@': + return true; + default: + return false; + } +} + +/// RFC3986 subcomponent delimiter character test +/** + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered a subcomponent delimiter + */ +inline bool sub_delim(char c) { + switch(c) { + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return true; + default: + return false; + } +} + +/// RFC3986 hex digit character test +/** + * Case insensitive + * + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered a hexadecimal digit + */ +inline bool hexdigit(char c) { + switch(c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return true; + default: + return false; + } +} + +/// RFC3986 scheme character test +/** + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered a valid character for a uri scheme + */ +inline bool scheme(char c) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + return true; + } else if (c >= '0' && c <= '9') { + return true; + } else if (c == '+' || c == '-' || c == '.') { + return true; + } else { + return false; + } +} + +/// RFC3986 digit character test +/** + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered a digit (0-9) + */ +inline bool digit(char c) { + return c >= '0' && c <= '9'; +} + +/// RFC3986 digit character test (iterator version) +/** + * @since 0.8.3 + * + * @param c the char to test + * @return True if the character is considered a digit (0-9) + */ +inline bool digit(std::string::const_iterator it) { + return digit(*it); +} + + +/// RFC3986 per cent encoded character test +/** + * caller must range check (only caller knows the actual range) + * caller must check for leading % + * + * @since 0.8.3 + * + * @param it An iterator to the first character after the % sign + * @return True if both the character pointed at by the iterator and + * the next one represent a valid RFC3986 percent encoding + */ +inline bool pct_encoded(std::string::const_iterator it) { + return hexdigit(*it) && hexdigit(*(it + 1)); +} + +/// Tests a range for a valid IPv4 decimal octet +/** + * @since 0.8.3 + * + * @param start An iterator to the first character of the range to check (inclusive) + * @param start An iterator to the last character of the range to check (exclusive) + * @return True if the range represents a valid IPv4 decimal octet (0-255) + */ +inline bool dec_octet(std::string::const_iterator start, std::string::const_iterator end) { + if (end-start == 1) { + return digit(start); + } else if (end-start == 2) { + return ((*start >= '1' && *start <= '9') && digit(start+1)); + } else if (end-start == 3) { + if (*start == '1') { + return digit(start+1) && digit(start+2); + } else if (*start == '2') { + if (*(start+1) >= '0' && *(start+1) <= '4') { + return digit(start+2); + } else if (*(start+1) == '5') { + return *(start+2) >= '0' && *(start+2) <= '5'; + } + } + } + return false; +} + +/// Tests a range for a valid IPv4 literal +/** + * @since 0.8.3 + * + * @param start An iterator to the first character of the range to check (inclusive) + * @param start An iterator to the last character of the range to check (exclusive) + * @return True if the range represents a valid IPv4 literal address + */ +inline bool ipv4_literal(std::string::const_iterator start, std::string::const_iterator end) { + std::string::const_iterator cursor = start; + size_t counter = 0; + for (std::string::const_iterator it = start; it != end; ++it) { + if (*it == '.') { + if (dec_octet(cursor,it)) { + cursor = it+1; + counter++; + if (counter > 3) { + return false; + } + } else { + return false; + } + } + } + + // check final octet + return (counter == 3 && dec_octet(cursor,end)); +} + +/// Tests a range for a valid IPv6 hex quad +/** + * @since 0.8.3 + * + * @param start An iterator to the first character of the range to check (inclusive) + * @param start An iterator to the last character of the range to check (exclusive) + * @return True if the range represents a valid IPv6 hex quad + */ +inline bool hex4(std::string::const_iterator start, std::string::const_iterator end) { + if (end-start == 0 || end-start >4) { + return false; + } + for (std::string::const_iterator it = start; it != end; ++it) { + if (!hexdigit(*it)) { + return false; + } + } + return true; +} + +/// Tests a range for a valid IPv6 literal +/** + * @since 0.8.3 + * + * @param start An iterator to the first character of the range to check (inclusive) + * @param start An iterator to the last character of the range to check (exclusive) + * @return True if the range represents a valid IPv6 literal + */ +inline bool ipv6_literal(std::string::const_iterator start, std::string::const_iterator end) { + // initial range check + if (end-start > 45 && end-start >= 2) { + return false; + } + + // peal off and count hex4s until we run out of colons, + // note the abbreviation marker if we see one. + std::string::const_iterator cursor = start; + std::string::const_iterator it = start; + size_t count = 0; + size_t abbr = 0; + while (it != end) { + if (*it == ':') { + if (it == start) { + // if a : happens at the beginning, don't check for a hex quad, just advance + // the cursor. The abbreviation marker will be counted on the next pass + cursor++; + } else if (it-cursor == 0) { + // this is a double colon abbreviation marker + cursor++; + abbr++; + } else if (hex4(cursor,it)) { + cursor = it+1; + count++; + } else { + return false; + } + } + it++; + } + + // final bit either needs to be a hex4 or an IPv4 literal + if (cursor == end) { + // fine + } else if (hex4(cursor,end)) { + count++; + } else if (ipv4_literal(cursor, end)) { + count += 2; + } else { + return false; + } + + if ((abbr == 0 && count != 8) || (abbr == 1 && count > 7) || abbr > 1) { + return false; + } + + return true; +} + +/// Tests a character for validity for a registry name +/** + * will fail on %, which is valid, but only when used as a part of a multiple + * character escape sequence. Since this test checks a single character it + * can't tell whether a % character is valid so it returns false. The caller + * needs to catch and handle %s in another way. + * + * @since 0.8.3 + * + * @param c The character to test + * @return True if the range represents a valid IPv6 literal + */ +inline bool reg_name(char c) { + return unreserved(c) || sub_delim(c); +} + +/// Tests a range for validity for a registry name +/** + * @since 0.8.3 + * + * @param start An iterator to the first character of the range to check (inclusive) + * @param start An iterator to the last character of the range to check (exclusive) + * @return True if the range represents a valid registry name + */ +inline bool reg_name(std::string::const_iterator start, std::string::const_iterator end) { + std::string::const_iterator it = start; + while (it != end) { + if (*it == '%') { + // check for valid % encoded char + if (it+2 < end && uri_helper::pct_encoded(it+1)) { + it += 3; + continue; + } else { + return false; + } + } else if (!uri_helper::reg_name(*it)) { + return false; + } + ++it; + } + return true; +} + +} // end namespace uri_helper + + + + class uri { public: - explicit uri(std::string const & uri_string) : m_valid(false) { + explicit uri(std::string const & uri_string) : m_valid(false), m_ipv6_literal(false) { std::string::const_iterator it; std::string::const_iterator temp; @@ -57,6 +405,7 @@ class uri { it = uri_string.begin(); size_t uri_len = uri_string.length(); + // extract scheme. We only consider Websocket and HTTP URI schemes as valid if (uri_len >= 7 && std::equal(it,it+6,"wss://")) { m_secure = true; m_scheme = "wss"; @@ -101,17 +450,26 @@ class uri { return; } else { // validate IPv6 literal parts - // can contain numbers, a-f and A-F + if (!uri_helper::ipv6_literal(it,temp)) { + return; + } else { + m_ipv6_literal = true; + } m_host.append(it,temp); } it = temp+1; if (it == uri_string.end()) { state = 2; - } else if (*it == '/') { + } else if (*it == '/' || *it == '?' || *it == '#') { + // todo: better path parsing state = 2; - ++it; + + // we don't increment the iterator here because we want the + // delimiter to be read again as a part of the path } else if (*it == ':') { state = 1; + + // start reading port after the delimiter ++it; } else { // problem @@ -119,20 +477,44 @@ class uri { } } else { // IPv4 or hostname - // extract until : or / + // extract until : or first path component while (state == 0) { if (it == uri_string.end()) { state = 2; break; - } else if (*it == '/') { - state = 2; - } else if (*it == ':') { - // end hostname start port - state = 1; + } else if (*it == '%') { + // check for valid % encoded char + if (it+2 < uri_string.end() && uri_helper::pct_encoded(it+1)) { + m_host.append(it,it+2); + it += 3; + } + } else if (!uri_helper::reg_name(*it)) { + // we hit one of the general delimiters + if (*it == ':') { + // got host vs port delimiter + // end hostname start port + state = 1; + + // start reading port after the delimiter + ++it; + } else if (*it == '/' || *it == '#' || *it == '?') { + // one of the normal authority vs path delimiters + // end hostname and start parsing path + state = 2; + + // we don't increment the iterator here because we want the + // delimiter to be read again as a part of the path + } else { + // either @, [, or ] + // @ = userinfo fragment + // [ and ] = illegal, basically + return; + } } else { m_host += *it; + ++it; } - ++it; + } } @@ -140,17 +522,27 @@ class uri { std::string port; while (state == 1) { if (it == uri_string.end()) { - // state is not used after this point presently. - // this should be re-enabled if it ever is needed in a future - // refactoring - //state = 3; - break; - } else if (*it == '/') { + // if we stop parsing the port and there wasn't actually a port + // we have an invalid URI + if (port.empty()) { + return; + } state = 3; - } else { + } else if (uri_helper::digit(it)) { port += *it; + ++it; + } else { + // if we stop parsing the port and there wasn't actually a port + // we have an invalid URI + if (port.empty()) { + return; + } + state = 3; + + // we don't increment the iterator here because we want the + // delimiter to be read again as a part of the path } - ++it; + } lib::error_code ec; @@ -160,8 +552,14 @@ class uri { return; } - m_resource = "/"; + // step back one so the first char of the path delimiter doesn't get eaten m_resource.append(it,uri_string.end()); + + if (m_resource.empty()) { + m_resource = "/"; + } + + // todo: validate path component m_valid = true; @@ -174,7 +572,10 @@ class uri { , m_resource(resource.empty() ? "/" : resource) , m_port(port) , m_secure(secure) - , m_valid(true) {} + { + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + } uri(bool secure, std::string const & host, std::string const & resource) : m_scheme(secure ? "wss" : "ws") @@ -182,7 +583,10 @@ class uri { , m_resource(resource.empty() ? "/" : resource) , m_port(secure ? uri_default_secure_port : uri_default_port) , m_secure(secure) - , m_valid(true) {} + { + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + } uri(bool secure, std::string const & host, std::string const & port, std::string const & resource) @@ -193,7 +597,9 @@ class uri { { lib::error_code ec; m_port = get_port_from_string(port,ec); - m_valid = !ec; + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + + m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); } uri(std::string const & scheme, std::string const & host, uint16_t port, @@ -203,7 +609,10 @@ class uri { , m_resource(resource.empty() ? "/" : resource) , m_port(port) , m_secure(scheme == "wss" || scheme == "https") - , m_valid(true) {} + { + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + } uri(std::string scheme, std::string const & host, std::string const & resource) : m_scheme(scheme) @@ -211,7 +620,10 @@ class uri { , m_resource(resource.empty() ? "/" : resource) , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port) , m_secure(scheme == "wss" || scheme == "https") - , m_valid(true) {} + { + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + m_valid = m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end()); + } uri(std::string const & scheme, std::string const & host, std::string const & port, std::string const & resource) @@ -222,13 +634,24 @@ class uri { { lib::error_code ec; m_port = get_port_from_string(port,ec); - m_valid = !ec; + m_ipv6_literal = uri_helper::ipv6_literal(host.begin(), host.end()); + + m_valid = !ec && (m_ipv6_literal || uri_helper::reg_name(host.begin(), host.end())); } bool get_valid() const { return m_valid; } + // Check whether the host of this URI is an IPv6 literal address + /** + * @since 0.8.3 + * @return True if the host of this URI is an IPv6 literal address + */ + bool is_ipv6_literal() const { + return m_ipv6_literal; + } + bool get_secure() const { return m_secure; } @@ -243,17 +666,27 @@ class uri { std::string get_host_port() const { if (m_port == (m_secure ? uri_default_secure_port : uri_default_port)) { + // todo: should this have brackets for v6? return m_host; } else { std::stringstream p; - p << m_host << ":" << m_port; + if (m_ipv6_literal) { + p << "[" << m_host << "]:" << m_port; + } else { + p << m_host << ":" << m_port; + } + return p.str(); } } std::string get_authority() const { std::stringstream p; - p << m_host << ":" << m_port; + if (m_ipv6_literal) { + p << "[" << m_host << "]:" << m_port; + } else { + p << m_host << ":" << m_port; + } return p.str(); } @@ -274,7 +707,12 @@ class uri { std::string str() const { std::stringstream s; - s << m_scheme << "://" << m_host; + s << m_scheme << "://"; + if (m_ipv6_literal) { + s << "[" << m_host << "]"; + } else { + s << m_host; + } if (m_port != (m_secure ? uri_default_secure_port : uri_default_port)) { s << ":" << m_port; @@ -346,6 +784,7 @@ class uri { uint16_t m_port; bool m_secure; bool m_valid; + bool m_ipv6_literal; }; /// Pointer to a URI diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/utilities.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/utilities.hpp index b983c9f3a3..4f28b1e401 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/utilities.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/utilities.hpp @@ -128,13 +128,6 @@ typename T::const_iterator ci_find_substr(T const & haystack, needle, needle+size, my_equal(loc) ); } -/// Convert a string to lowercase -/** - * @param [in] in The string to convert - * @return The converted string - */ -std::string to_lower(std::string const & in); - /// Replace all occurrances of a substring with another /** * @param [in] subject The string to search in diff --git a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/version.hpp b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/version.hpp index 5766546f76..db263a1e9a 100644 --- a/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/version.hpp +++ b/src/cxx_supportlib/vendor-modified/websocketpp/websocketpp/version.hpp @@ -44,17 +44,17 @@ static int const major_version = 0; /// Library minor version number static int const minor_version = 8; /// Library patch version number -static int const patch_version = 2; +static int const patch_version = 3; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release * versions (dev, alpha, beta, rc). This will be blank for release versions. */ -static char const prerelease_flag[] = ""; +static char const prerelease_flag[] = "dev"; /// Default user agent string -static char const user_agent[] = "WebSocket++/0.8.2"; +static char const user_agent[] = "WebSocket++/0.8.3-dev"; } // namespace websocketpp