restbed-4.0/000077500000000000000000000000001270400672200130055ustar00rootroot00000000000000restbed-4.0/.gitattributes000066400000000000000000000010521270400672200156760ustar00rootroot00000000000000# Set default behaviour, in case users don't have core.autocrlf set. * text=auto # Explicitly declare text files we want to always be normalized and converted # to native line endings on checkout. *.c text *.cc text *.cpp text *.h text *.hpp test *.txt text *.htm text *.html text *.py text *.pyw text *.cmake text CMakeLists.txt text # batch files are specific to windows and always crlf *.bat eol=crlf # Denote all files that are truly binary and should not be modified. *.pem binary *.a binary *.so binary *.dll binary *.dylib binary *.lib binary restbed-4.0/.gitignore000066400000000000000000000006721270400672200150020ustar00rootroot00000000000000# Project Files build/ distribution/ # Compiled source *.com *.class *.dll *.exe *.o *.so *.dylib *.pyc *.cab *.msi *.msm *.msp *.slo *.lo *.obj *.lai *.la *.a *.lib *.exe *.out *.app *.gch *.pch # Packages *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip # Logs and databases *.log *.sql *.sqlite *.bak *.swp # OS generated files .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db Desktop.ini $RECYCLE.BIN/ *.lnk restbed-4.0/.gitmodules000066400000000000000000000006131270400672200151620ustar00rootroot00000000000000[submodule "dependency/asio"] path = dependency/asio url = https://github.com/corvusoft/asio-dependency branch = master [submodule "dependency/catch"] path = dependency/catch url = https://github.com/corvusoft/catch-dependency branch = master [submodule "dependency/openssl"] path = dependency/openssl url = https://github.com/corvusoft/openssl-dependency branch = OpenSSL_1_0_2-stable restbed-4.0/.travis.yml000066400000000000000000000010661270400672200151210ustar00rootroot00000000000000language: cpp addons: apt: sources: - ubuntu-toolchain-r-test - kubuntu-backports packages: - cmake - g++-5 - gcc-5 compiler: - gcc install: - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi before_script: - mkdir build - cd build - cmake -DBUILD_EXAMPLES=YES -DBUILD_TESTS=YES -DBUILD_SSL=YES .. script: make -j5 && make test notifications: recipients: - support@corvusoft.co.uk email: on_success: change on_failure: always restbed-4.0/CMakeLists.txt000066400000000000000000000041031270400672200155430ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "restbed" ) cmake_minimum_required( VERSION 2.8.10 ) # # Build Options # option( BUILD_SHARED "Build shared library." OFF ) option( BUILD_EXAMPLES "Build examples applications." OFF ) option( BUILD_TESTS "Build all available test suites." OFF ) option( BUILD_SSL "Build secure socket layer support." ON ) # # Configuration # include( "${CMAKE_SOURCE_DIR}/cmake/build_configuration.cmake" ) message( " ${Blue}Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved.${Reset}" ) # # Dependencies # set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules" ) find_package( asio REQUIRED ) include_directories( SYSTEM ${asio_INCLUDE} ) if ( BUILD_SSL ) find_package( openssl REQUIRED ) include_directories( SYSTEM ${ssl_INCLUDE} ) endif ( ) # # Build # include( "${CMAKE_SOURCE_DIR}/cmake/build_manifest.cmake" ) include_directories( ${INCLUDE_DIR} ) add_library( ${PROJECT_NAME} ${BUILD_MANIFEST} ) if ( BUILD_SSL ) target_link_libraries( ${PROJECT_NAME} LINK_PRIVATE ${ssl_LIBRARY} ${crypto_LIBRARY} ) else ( ) target_link_libraries( ${PROJECT_NAME} ) endif ( ) if ( BUILD_EXAMPLES ) find_package( pam ) find_package( syslog ) add_subdirectory( "${CMAKE_SOURCE_DIR}/example" ) endif ( ) if ( BUILD_TESTS ) find_package( catch REQUIRED ) enable_testing( ) add_subdirectory( "${CMAKE_SOURCE_DIR}/test/unit" "unit-tests" ) add_subdirectory( "${CMAKE_SOURCE_DIR}/test/acceptance" "acceptance-tests" ) add_subdirectory( "${CMAKE_SOURCE_DIR}/test/regression" "regression-tests" ) add_subdirectory( "${CMAKE_SOURCE_DIR}/test/integration" "integration-tests" ) endif ( ) # # Install # include( "${CMAKE_SOURCE_DIR}/cmake/build_artifacts.cmake" ) install( FILES "${INCLUDE_DIR}/${PROJECT_NAME}" DESTINATION "include" ) install( FILES ${BUILD_ARTIFACTS} DESTINATION "include/corvusoft/${PROJECT_NAME}" ) install( TARGETS ${PROJECT_NAME} LIBRARY DESTINATION "library" ARCHIVE DESTINATION "library" COMPONENT library ) restbed-4.0/LICENSE000066400000000000000000000027471270400672200140240ustar00rootroot00000000000000 Copyright 2013-2016 Corvusoft Limited, United Kingdom. All rights reserved. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This work is part of Corvusoft's Restbed Framework (the "Software"). You may at your option receive a license to the Software under either the terms of the GNU Affero General Public License (AGPL) or the Corvusoft Permissive License (CPL), as explained in the note below. The Software may be used under the terms of the GNU Affero General Public License version 3 or (at your option) any later version as published by the Free Software Foundation and appearing in the file LICENSE.AGPL included in the packaging of the Software. The Software is provided AS IS WITHOUT WARRANTY OF ANY KIND; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. **NOTE:** Using the AGPL requires that your work based on the Software must be licensed under the AGPL. If you wish to develop a work based on the Software but desire to license it under your own terms, e.g. a closed source license, you MUST purchase a Corvusoft Permissive License. Purchasing the CPL, gives you -- under certain conditions -- the rights appearing in the file LICENSE.CPL included in the packaging of the Software. For full terms and conditions of the CPL, please contact sales@corvusoft.co.uk. restbed-4.0/README.md000066400000000000000000000320471270400672200142720ustar00rootroot00000000000000# Restbed [![Build Status](https://travis-ci.org/Corvusoft/restbed.svg?branch=master)](https://travis-ci.org/Corvusoft/restbed) ---------- Restbed is a comprehensive and consistent programming model for building applications that require seamless and secure communication over HTTP, with the ability to model a range of business processes, designed to target mobile, tablet, desktop and embedded production environments. > It's akin to embedding NGINX into your companies own product line. > -- Solutions Architect, Bellrock Technology ## Features | Feature | Description | |-------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| | [Comet](https://github.com/Corvusoft/restbed/blob/master/example/persistent_connection/source/example.cpp) | Long polling model to allow long-held HTTP requests for pushing data from the server to client. | | [SSL/TLS](https://github.com/Corvusoft/restbed/blob/master/example/https_service/source/example.cpp) | Secure over the wire communication allowing you to transmit private data online. | | [Session Management](https://github.com/Corvusoft/restbed/blob/master/example/session_manager/source/example.cpp) | Create custom HTTP session persistence and management logic. | | [Path Parameters](https://github.com/Corvusoft/restbed/blob/master/example/path_parameters/source/example.cpp) | Annotate URIs with custom path parameters such as resource keys, revisions, etc... | | Query Parameters | Automated query parameter parsing. | | [Header Filters](https://github.com/Corvusoft/restbed/blob/master/example/resource_filtering/source/example.cpp) | Filter incoming HTTP requests by headers. | | [Logging](https://github.com/Corvusoft/restbed/blob/master/example/logging/source/example.cpp) | Customise how and where log entries are created. | | [Multi-Path Resources](https://github.com/Corvusoft/restbed/blob/master/example/publishing_multipath_resources/source/example.cpp) | Give a resource multiple paths for improved readability. | | [Customisable Methods](https://github.com/Corvusoft/restbed/blob/master/example/custom_methods/source/example.cpp) | Add your own custom HTTP methods. | | [Compression](https://github.com/Corvusoft/restbed/blob/master/example/compression/source/example.cpp) | Adaptability to address any form of compression GZip, Deflate, etc... | | Encoding | Adaptability to address any form of encoding UTF-32, ASCII, etc... | | [Rules Engine](https://github.com/Corvusoft/restbed/blob/master/example/rules_engine/source/example.cpp) | Reduce complexity by processing incoming requests with readable units of code. | | [HTTP](https://github.com/Corvusoft/restbed/blob/master/example/http_client/source/example.cpp)/[HTTPS](https://github.com/Corvusoft/restbed/blob/master/example/https_client_verify_none/source/example.cpp) | Built in client capabilities with optional SSL peer certificate verification. | | IPv4/IPv6 | Internet Protocol Version 4/6 Network Support. | | Architecture | Asynchronous [single](https://github.com/Corvusoft/restbed/blob/master/example/publishing_resources/source/example.cpp) or [multi-threaded](https://github.com/Corvusoft/restbed/blob/master/example/multithreaded_service/source/example.cpp) architecture, capable of addressing the C10K problem. | | Converters | Built-in Path, Query, and Header conversions for string, int, long, and float data types. | | [Authentication](https://github.com/Corvusoft/restbed/blob/master/example/authentication/source/example.cpp) | Seperate Service and/or Resource level authentication. | | [Error Handling](https://github.com/Corvusoft/restbed/blob/master/example/error_handling/source/example.cpp) | Seperate Service and/or Resource level error handling. | | [Address Binding](https://github.com/Corvusoft/restbed/blob/master/example/bind_service_address/source/example.cpp) | Bind HTTP and/or HTTPS services to separate IP addresses. | | [Signal Handling](https://github.com/Corvusoft/restbed/blob/master/example/signal_handling/source/example.cpp) | Capture OS generated process signals. | | Compliance | Flexibility to address HTTP 1.0/1.1+ compliance. | | Mature | Secure, Stable, and extensively tested since 2013. | | Community | Active, vibrant and energetic open source community. | | Support | Commercial support is available from [Corvusoft](http://www.corvusoft.co.uk). | ## Example ```C++ #include #include #include using namespace std; using namespace restbed; void post_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); int content_length = 0; request->get_header( "Content-Length", content_length ); session->fetch( content_length, [ ]( const shared_ptr< Session > session, const Bytes & body ) { fprintf( stdout, "%.*s\n", ( int ) body.size( ), body.data( ) ); session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "POST", post_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } ``` More in-depth examples can be found [here](https://github.com/corvusoft/restbed/tree/master/example). To see Restbed used in anger, please visit Corvusoft's [RestQ](https://github.com/corvusoft/restq) project. ## License © 2013-2016 Corvusoft Limited, United Kingdom. All rights reserved. The Restbed framework is dual licensed; See [LICENSE](LICENSE) for full details. ## Support Please contact sales@corvusoft.co.uk, for support and licensing options including bespoke software development, testing, design consultation, training, mentoring and code review. ## Build ```bash git clone --recursive https://github.com/corvusoft/restbed.git mkdir restbed/build cd restbed/build cmake [-DBUILD_TESTS=YES] [-DBUILD_EXAMPLES=YES] [-DBUILD_SSL=NO] [-DBUILD_SHARED=YES] [-DCMAKE_INSTALL_PREFIX=/output-directory] .. make [-j CPU_CORES+1] install make test ``` You will now find all required components installed in the distribution folder. Please submit all enhancements, proposals, and defects via the [issue](http://github.com/corvusoft/restbed/issues) tracker; Alternatively ask a question on [StackOverflow](http://stackoverflow.com/questions/ask) tagged [#restbed](http://stackoverflow.com/questions/tagged/restbed). For Microsoft Visual Studio instructions please see feature [#17](https://github.com/Corvusoft/restbed/issues/17). ## Documentation This codebase is intented to be as self documenting as possible. We have supplied many [examples](https://github.com/corvusoft/restbed/tree/master/example) and [test suites](https://github.com/corvusoft/restbed/tree/master/test) to help aid developers. We are currently working on textual API documentation [here](https://github.com/ben-crowhurst/restbed/blob/master/DOCUMENTATION.md), however they are subject to eratice and frequent alteration; be warned. ## Minimum Requirements | Resource | Requirement | |:--------------:|:-----------------------------------------------:| | Compiler | C++11 compliant or above | | OS | BSD, Linux, Mac OSX, Solaris, Windows, Raspbian | ## Road Map | Milestone | Feature | Status | |:-------------:|:-----------------------------------------------:|:---------------:| | 0.0 | Asynchrounous HTTP Service | complete | | 1.0 | HTTP 1.0 Compliance | complete | | 2.0 | HTTP 1.1 Compliance | complete | | 2.5 | Secure Socket Layer | complete | | 2.5 | Simultaneous Network Ports (HTTP/HTTPS) | complete | | 3.0 | Rules Engine | complete | | [3.5](https://github.com/Corvusoft/restbed/issues?utf8=%E2%9C%93&q=milestone%3A3.5) | Schedule Tasks on Service run-loop | complete | | [3.5](https://github.com/Corvusoft/restbed/issues?utf8=%E2%9C%93&q=milestone%3A3.5) | Multi-Threaded service capability | complete | | [3.5](https://github.com/Corvusoft/restbed/issues?utf8=%E2%9C%93&q=milestone%3A3.5) | Bind Service to specific Address | complete | | [3.5](https://github.com/Corvusoft/restbed/issues?utf8=%E2%9C%93&q=milestone%3A3.5) | Session Management | complete | | [4.0](https://github.com/Corvusoft/restbed/milestones/4.0) | HTTP Client | complete | | [4.0](https://github.com/Corvusoft/restbed/milestones/4.0) | Signal Handling | complete | | [4.5](https://github.com/Corvusoft/restbed/milestones/4.5) | API Documentation | development | | [4.5](https://github.com/Corvusoft/restbed/milestones/4.5) | Web Sockets | pending | | [4.5](https://github.com/Corvusoft/restbed/milestones/4.5) | Client-side SSL certificates | pending | | [4.5](https://github.com/Corvusoft/restbed/milestones/4.5) | Resource Caching | pending | | [4.5](https://github.com/Corvusoft/restbed/milestones/4.5) | Runtime Modifications | pending | | [5.0](https://github.com/Corvusoft/restbed/milestones/5.0) | HTTP 2 compliance | pending | | [5.0](https://github.com/Corvusoft/restbed/milestones/5.0) | Refactor, Reduce, Reuse | pending | ## Contact | Method | Description | |:--------------|:-----------------------------------------------| | [Twitter](http://www.twitter.com/corvusoft) | Tweet us your questions & feature requests. | | support@corvusoft.co.uk | Support related queries. | | sales@corvusoft.co.uk | Sale related queries. | restbed-4.0/STANDARDS.md000066400000000000000000000130011270400672200146450ustar00rootroot00000000000000**most importantly:** "know when to be inconsistent -- sometimes the style guide just doesn't apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!" -- Guido van Rossum, Barry Warsaw, Nick Coghlan. **Brackets** 1. Allman style broken braces, and avoid unbraced one line conditional statements. ``` C++ if ( condition == true ) { ... } else { ... } ``` **Indentation** 1. Do **not** use tabs. All indentation is of 4 spaces. 2. Class, Namespace, Struct, If, For, Switch, etc.. statements **must** be indented. ``` C++ namespace transport { class Car( void ) { public: Car( const int number_of_wheels ) : m_number_of_wheels { switch( m_number_of_wheels ) { case 1: break; default: break; } } private: uint8_t m_number_of_wheels; } } ``` **Comments** 1. Comments **should** be avoided as they add an additional layer of management and/or technical-debt. 2. Commenting is just as ineffective as poorly readable code. 3. Code should be self documenting with descriptive naming conventions applied to functions, variables, files, directories, etc... 4. Commenting may be a sign you need to split the logic into multiple manageable blocks. **Properties/Variables** 1. Property and Variables names **must** follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case). 2. Property and Variables **must** be initialised to a known state on declaration. ``` C++ int limit = 0; int person_age = 21; string name = String::empty; ``` **Classes** 1. Class property names **must** be prefixed with `m_` and follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case). 2. Class properties **must** be initialised to a known state on instance creation. 3. Class properties **must** be private in scope. 4. Class getter/setter accessor methods **must not** return non-const pointers/references. 5. There **must** be **no** using namespace declarations in class header files. 6. Forward declarations are favoured over ```#include``` within class header files; with the exception of the standard template library. 7. Empty method bodies (when unavoidable) shall be marked with a single return. 8. Public classes **must** implement an [opaque pointer](http://en.wikipedia.org/wiki/Opaque_pointer). 9. Class names **must** start each word boundary with an UPPERCASED letter. ``` C++ class Person { public: Person( void ) : m_age( 0 ) { return; } int get_age( void ) const { return m_age; } void set_age( const int value ) { m_age = value; } private: int m_age;. } ``` **Enumerations** 1. Enumerations **must** be strongly typed. 2. Enumeration fields **must** be UPPERCASED. 3. Enumeration fields **must** be initialised to a known state. 4. Enumeration names **must** start each word boundary with an UPPERCASED letter. ``` C++ enum LogLevel : int { INFO = 0000, DEBUG = 1000, FATAL = 2000, ERROR = 3000, WARNING = 4000, SECURITY = 5000 } ``` **Structures** 1. Structure property names **must** follow the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case). 2. Structure properties **must** be initialised to a known state on instance creation. 3. Structure names **must** start each word boundary with an UPPERCASED letter. ``` C++ struct HttpRequest { Bytes body = { }; uint16_t port = 80; double version = 1.1; std::string host = ""; std::string path = "/"; std::string method = "GET"; std::multimap< std::string, std::string > headers { }; } ``` **Methods/Functions** 1. Functions and Methods **must** use the [Snake-case naming convention](https://en.wikipedia.org/wiki/snake_case). 2. Functions and Methods **should** perform one mental operation which is reflected in their name. 3. Function and Method declarations **should** avoid similar argument types. 4. It is recommended that Functions and Methods are no greater than 70 lines of code. If you find that the LOC exceed this limit, it may be an indication that it is performing more than one mental operation; see rule 2. ``` C++ int ping( Hostname hostname, Interface interface, int16_t port ) { auto port = port; auto hostname = hostname.to_string( ); auto interface = hostname.to_string( ) ... return ttl; } ``` **Whitespace** 1. Whitespace is free, don't be scared to use it. ``` C++ int process_exit_status = 0; const string filename = "/bin/ls"; do { Process reaction = load_process( filename ); reaction.run( ); process_exit_status = reaction.get_exit_status( ); } while ( process_exit_status not_eq -1 ); ``` **Pointers/References** 1. Pointers and References **must** be aligned to the data type (left). ``` C++ int* age = nullptr; char* forename = nullptr; string& surname = m_surname; ``` **Exceptions** 1. Logic within catch-blocks **must** be short & sweet. 2. Name exceptions per Java styling. ``` C++ try { start( ); } catch ( const invalid_argument& ia ) { stop( ); } catch ( const runtime_error& re ) { stop( ); } catch ( const exception& ex ) { stop( ); } ``` **Namespaces** 1. **Do not** include entire Namespaces, import only the artifact you're interested in. 2. Namespace names **must only** contain lowercased letters. ``` C++ using std::mutex; using std::string; using std::thread; using namespace restq { ... } ``` **Optimisation** 1. Avoid premature optimisation, **readable, maintainable code is more important**. restbed-4.0/cmake/000077500000000000000000000000001270400672200140655ustar00rootroot00000000000000restbed-4.0/cmake/build_artifacts.cmake000066400000000000000000000012271270400672200202300ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. set( BUILD_ARTIFACTS ${SOURCE_DIR}/http.hpp ${SOURCE_DIR}/rule.hpp ${SOURCE_DIR}/settings.hpp ${SOURCE_DIR}/ssl_settings.hpp ${SOURCE_DIR}/status_code.hpp ${SOURCE_DIR}/resource.hpp ${SOURCE_DIR}/request.hpp ${SOURCE_DIR}/response.hpp ${SOURCE_DIR}/byte.hpp ${SOURCE_DIR}/uri.hpp ${SOURCE_DIR}/string.hpp ${SOURCE_DIR}/logger.hpp ${SOURCE_DIR}/service.hpp ${SOURCE_DIR}/session.hpp ${SOURCE_DIR}/session_manager.hpp ${SOURCE_DIR}/context_value.hpp ${SOURCE_DIR}/context_placeholder.hpp ${SOURCE_DIR}/context_placeholder_base.hpp ) restbed-4.0/cmake/build_configuration.cmake000066400000000000000000000047601270400672200211240ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. if( NOT WIN32 ) string( ASCII 27 Esc ) set( Reset "${Esc}[m" ) set( Red "${Esc}[31m" ) set( Blue "${Esc}[34m" ) set( Green "${Esc}[32m" ) set( Yellow "${Esc}[33m" ) endif( ) if ( BUILD_SHARED ) set( BUILD_SHARED_LIBS ON ) endif ( ) set( INCLUDE_DIR "${CMAKE_SOURCE_DIR}/source" ) set( SOURCE_DIR "${INCLUDE_DIR}/corvusoft/${PROJECT_NAME}" ) if ( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT ) set( CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/distribution" CACHE PATH "Install path prefix" FORCE ) endif ( ) if ( ${CMAKE_CXX_COMPILER_ID} STREQUAL GNU ) if ( ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.9 ) message( FATAL_ERROR "\nGCC version < 4.9\nYour systems default compiler is GCC. This project makes use of c++11 features present only in versions of gcc >= 4.9. You can use a different compiler by re-running cmake with the command switch \"-D CMAKE_CXX_COMPILER=\" " ) else ( ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor" ) endif ( ) elseif( ${CMAKE_CXX_COMPILER_ID} STREQUAL Clang ) if ( ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.3 ) message( FATAL_ERROR "\nClang version < 3.3\nYour systems default compiler is clang. This project makes use of c++11 features present only in versions of clang >= 3.3. You can use a different compiler by re-running cmake with the command switch \"-D CMAKE_CXX_COMPILER=\" " ) else ( ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++" ) endif ( ) elseif( ${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC ) if ( ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 19.00.23026.0 ) message( WARNING "\nMSVC compiler version < 19.00.23026.0" ) endif ( ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_WIN32_WINNT=0x0601 /W4 /wd4068 /wd4702" ) else ( ) message( FATAL_ERROR "Compiler not supported.") endif ( ) if( NOT WIN32 ) if ( CMAKE_BUILD_TYPE MATCHES Debug ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O0 -Wall -Wextra -Weffc++ -pedantic -Wno-unknown-pragmas" ) else ( ) string( REPLACE "-O3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -Wall -Wextra -Weffc++ -pedantic -Wno-unknown-pragmas" ) endif ( ) endif ( ) if ( UNIX AND NOT APPLE ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread" ) endif ( ) if ( APPLE ) set( CMAKE_MACOSX_RPATH ON ) endif ( ) restbed-4.0/cmake/build_manifest.cmake000066400000000000000000000011431270400672200200530ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. set( BUILD_MANIFEST ${SOURCE_DIR}/uri.cpp ${SOURCE_DIR}/rule.cpp ${SOURCE_DIR}/http.cpp ${SOURCE_DIR}/string.cpp ${SOURCE_DIR}/request.cpp ${SOURCE_DIR}/service.cpp ${SOURCE_DIR}/session.cpp ${SOURCE_DIR}/resource.cpp ${SOURCE_DIR}/response.cpp ${SOURCE_DIR}/settings.cpp ${SOURCE_DIR}/ssl_settings.cpp ${SOURCE_DIR}/session_manager.cpp ${SOURCE_DIR}/detail/http_impl.cpp ${SOURCE_DIR}/detail/socket_impl.cpp ${SOURCE_DIR}/detail/service_impl.cpp ${SOURCE_DIR}/detail/session_impl.cpp ) restbed-4.0/cmake/modules/000077500000000000000000000000001270400672200155355ustar00rootroot00000000000000restbed-4.0/cmake/modules/Findasio.cmake000066400000000000000000000007671270400672200203050ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. find_path( asio_INCLUDE asio.hpp HINTS "${CMAKE_SOURCE_DIR}/dependency/asio/asio/include" "/usr/include" "/usr/local/include" "/opt/local/include" ) if ( asio_INCLUDE ) set( ASIO_FOUND TRUE ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DASIO_STANDALONE=YES" ) message( STATUS "${Green}Found ASIO include at: ${asio_INCLUDE}${Reset}" ) else ( ) message( FATAL_ERROR "${Red}Failed to locate ASIO dependency.${Reset}" ) endif ( ) restbed-4.0/cmake/modules/Findcatch.cmake000066400000000000000000000006641270400672200204300ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. find_path( catch_INCLUDE catch.hpp HINTS "${CMAKE_SOURCE_DIR}/dependency/catch/include" "/usr/include" "/usr/local/include" "/opt/local/include" ) if ( catch_INCLUDE ) set( CATCH_FOUND TRUE ) message( STATUS "${Green}Found Catch include at: ${catch_INCLUDE}${Reset}" ) else ( ) message( FATAL_ERROR "${Red}Failed to locate Catch dependency.${Reset}" ) endif ( ) restbed-4.0/cmake/modules/Findopenssl.cmake000066400000000000000000000025331270400672200210260ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. find_library( ssl_LIBRARY ssl ssleay32 HINTS "${CMAKE_SOURCE_DIR}/dependency/openssl/out32dll" "${CMAKE_SOURCE_DIR}/dependency/openssl" "/usr/local/opt/openssl/lib" "/usr/lib" "/usr/local/lib" "/opt/local/lib" ) find_library( crypto_LIBRARY crypto libeay32 HINTS "${CMAKE_SOURCE_DIR}/dependency/openssl/out32dll" "${CMAKE_SOURCE_DIR}/dependency/openssl" "/usr/local/opt/openssl/lib" "/usr/lib" "/usr/local/lib" "/opt/local/lib" ) find_path( ssl_INCLUDE openssl/ssl.h HINTS "${CMAKE_SOURCE_DIR}/dependency/openssl/inc32" "${CMAKE_SOURCE_DIR}/dependency/openssl/include" "/usr/local/opt/openssl/lib" "/usr/include" "/usr/local/include" "/opt/local/include" ) if ( ssl_INCLUDE AND ssl_LIBRARY AND crypto_LIBRARY ) set( OPENSSL_FOUND TRUE ) add_definitions( -DBUILD_SSL=TRUE ) if ( APPLE AND BUILD_SSL ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations" ) endif( ) message( STATUS "${Green}Found OpenSSL library at: ${ssl_LIBRARY}${Reset}" ) message( STATUS "${Green}Found OpenSSL include at: ${ssl_INCLUDE}${Reset}" ) message( STATUS "${Green}Found Crypto library at: ${crypto_LIBRARY}${Reset}" ) else ( ) message( FATAL_ERROR "${Red}Failed to locate OpenSSL dependency. see restbed/dependency/openssl; ./config shared; make all${Reset}" ) endif ( ) restbed-4.0/cmake/modules/Findpam.cmake000066400000000000000000000010701270400672200201130ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. find_library( pam_LIBRARY pam HINTS "/usr/lib" "/usr/local/lib" "/opt/local/lib" ) find_path( pam_INCLUDE security/pam_appl.h HINTS "/usr/include" "/usr/local/include" "/opt/local/include" ) if ( pam_INCLUDE AND pam_LIBRARY ) set( PAM_FOUND TRUE ) message( STATUS "${Green}Found PAM include at: ${pam_INCLUDE}${Reset}" ) message( STATUS "${Green}Found PAM library at: ${pam_LIBRARY}${Reset}" ) else ( ) message( STATUS "${Yellow}Failed to locate PAM, skipping example.${Reset}" ) endif ( ) restbed-4.0/cmake/modules/Findsyslog.cmake000066400000000000000000000006171270400672200206640ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. find_path( syslog_INCLUDE syslog.h HINTS "/usr/include" "/usr/local/include" "/opt/local/include" ) if ( syslog_INCLUDE ) set( SYSLOG_FOUND TRUE ) message( STATUS "${Green}Found Syslog include at: ${syslog_INCLUDE}${Reset}" ) else ( ) message( STATUS "${Yellow}Failed to locate Syslog, skipping example.${Reset}" ) endif ( ) restbed-4.0/dependency/000077500000000000000000000000001270400672200151235ustar00rootroot00000000000000restbed-4.0/dependency/asio/000077500000000000000000000000001270400672200160565ustar00rootroot00000000000000restbed-4.0/dependency/catch/000077500000000000000000000000001270400672200162055ustar00rootroot00000000000000restbed-4.0/dependency/openssl/000077500000000000000000000000001270400672200166065ustar00rootroot00000000000000restbed-4.0/example/000077500000000000000000000000001270400672200144405ustar00rootroot00000000000000restbed-4.0/example/CMakeLists.txt000066400000000000000000000124171270400672200172050ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "example" ) cmake_minimum_required( VERSION 2.8.10 ) # # Configuration # set( EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/example" ) # # Build # add_executable( basic_authentication basic_authentication/source/example.cpp ) target_link_libraries( basic_authentication ${CMAKE_PROJECT_NAME} ) add_executable( authentication authentication/source/example.cpp ) target_link_libraries( authentication ${CMAKE_PROJECT_NAME} ) add_executable( custom_status_codes custom_status_codes/source/example.cpp ) target_link_libraries( custom_status_codes ${CMAKE_PROJECT_NAME} ) add_executable( digest_authentication digest_authentication/source/example.cpp ) target_link_libraries( digest_authentication ${CMAKE_PROJECT_NAME} ) add_executable( error_handling error_handling/source/example.cpp ) target_link_libraries( error_handling ${CMAKE_PROJECT_NAME} ) add_executable( logging logging/source/example.cpp ) target_link_libraries( logging ${CMAKE_PROJECT_NAME} ) add_executable( path_parameters path_parameters/source/example.cpp ) target_link_libraries( path_parameters ${CMAKE_PROJECT_NAME} ) add_executable( publishing_resources publishing_resources/source/example.cpp ) target_link_libraries( publishing_resources ${CMAKE_PROJECT_NAME} ) add_executable( custom_methods custom_methods/source/example.cpp ) target_link_libraries( custom_methods ${CMAKE_PROJECT_NAME} ) add_executable( publishing_multipath_resources publishing_multipath_resources/source/example.cpp ) target_link_libraries( publishing_multipath_resources ${CMAKE_PROJECT_NAME} ) add_executable( resource_filtering resource_filtering/source/example.cpp ) target_link_libraries( resource_filtering ${CMAKE_PROJECT_NAME} ) add_executable( serving_html serving_html/source/example.cpp ) target_link_libraries( serving_html ${CMAKE_PROJECT_NAME} ) add_executable( transfer_encoding_request transfer_encoding_request/source/example.cpp ) target_link_libraries( transfer_encoding_request ${CMAKE_PROJECT_NAME} ) add_executable( transfer_encoding_response transfer_encoding_response/source/example.cpp ) target_link_libraries( transfer_encoding_response ${CMAKE_PROJECT_NAME} ) add_executable( persistent_connection persistent_connection/source/example.cpp ) target_link_libraries( persistent_connection ${CMAKE_PROJECT_NAME} ) add_executable( compression compression/source/example.cpp ) target_link_libraries( compression ${CMAKE_PROJECT_NAME} ) add_executable( http_service http_service/source/example.cpp ) target_link_libraries( http_service ${CMAKE_PROJECT_NAME} ) add_executable( service_ready_handler service_ready_handler/source/example.cpp ) target_link_libraries( service_ready_handler ${CMAKE_PROJECT_NAME} ) add_executable( session_data session_data/source/example.cpp ) target_link_libraries( session_data ${CMAKE_PROJECT_NAME} ) add_executable( rules_engine rules_engine/source/example.cpp ) target_link_libraries( rules_engine ${CMAKE_PROJECT_NAME} ) add_executable( multithreaded_service multithreaded_service/source/example.cpp ) target_link_libraries( multithreaded_service ${CMAKE_PROJECT_NAME} ) add_executable( schedule_work_on_service_runloop schedule_work_on_service_runloop/source/example.cpp ) target_link_libraries( schedule_work_on_service_runloop ${CMAKE_PROJECT_NAME} ) add_executable( bind_service_address bind_service_address/source/example.cpp ) target_link_libraries( bind_service_address ${CMAKE_PROJECT_NAME} ) add_executable( session_manager session_manager/source/example.cpp ) target_link_libraries( session_manager ${CMAKE_PROJECT_NAME} ) add_executable( http_client http_client/source/example.cpp ) target_link_libraries( http_client ${CMAKE_PROJECT_NAME} ) add_executable( signal_handling signal_handling/source/example.cpp ) target_link_libraries( signal_handling ${CMAKE_PROJECT_NAME} ) if ( PAM_FOUND ) add_executable( pam_authentication pam_authentication/source/example.cpp ) target_link_libraries( pam_authentication ${CMAKE_PROJECT_NAME} pam ) endif ( ) if ( SYSLOG_FOUND ) add_executable( syslog_logging syslog_logging/source/example.cpp ) target_link_libraries( syslog_logging ${CMAKE_PROJECT_NAME} ) endif ( ) if ( BUILD_SSL ) add_executable( https_service https_service/source/example.cpp ) target_link_libraries( https_service ${CMAKE_PROJECT_NAME} ) add_executable( https_client_verify_none https_client/source/verify_none.cpp ) target_link_libraries( https_client_verify_none ${CMAKE_PROJECT_NAME} ) add_executable( https_client_verify_peer https_client/source/verify_peer.cpp ) target_link_libraries( https_client_verify_peer ${CMAKE_PROJECT_NAME} ) endif ( ) # # Install # install( DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example" DESTINATION ${CMAKE_INSTALL_PREFIX} FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE ) install( DIRECTORY "${CMAKE_SOURCE_DIR}/example/https_client/resource/certificates" DESTINATION "${CMAKE_INSTALL_PREFIX}/resource" FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE ) install( FILES "${CMAKE_SOURCE_DIR}/example/serving_html/resource/index.html" DESTINATION "resource" ) install( FILES "${CMAKE_SOURCE_DIR}/example/compression/resource/data.zlib" DESTINATION "resource" ) install( FILES "${CMAKE_SOURCE_DIR}/example/transfer_encoding_request/resource/request.txt" DESTINATION "resource" ) restbed-4.0/example/authentication/000077500000000000000000000000001270400672200174575ustar00rootroot00000000000000restbed-4.0/example/authentication/source/000077500000000000000000000000001270400672200207575ustar00rootroot00000000000000restbed-4.0/example/authentication/source/example.cpp000066400000000000000000000053641270400672200231260ustar00rootroot00000000000000/* * Example illustrating service and resource authentication. * * Server Usage: * ./distribution/example/authentication * * Client Usage: * curl -w'\n' -v -XGET 'http://ben:1234@localhost:1984/ben' * curl -w'\n' -v -XGET 'http://laura:4321@localhost:1984/laura' */ #include #include #include #include #include using namespace std; using namespace restbed; void service_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic YmVuOjEyMzQ=" and authorisation not_eq "Basic bGF1cmE6NDMyMQ==" ) { session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } ); } else { callback( session ); } } void ben_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic YmVuOjEyMzQ=" ) { session->close( FORBIDDEN ); } else { callback( session ); } } void laura_authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic bGF1cmE6NDMyMQ==" ) { session->close( FORBIDDEN ); } else { callback( session ); } } void get_ben_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hi, Ben.", { { "Content-Length", "8" } } ); } void get_laura_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hi, Laura.", { { "Content-Length", "10" } } ); } int main( const int, const char** ) { auto ben = make_shared< Resource >( ); ben->set_path( "/ben" ); ben->set_method_handler( "GET", get_ben_method_handler ); ben->set_authentication_handler( ben_authentication_handler ); auto laura = make_shared< Resource >( ); laura->set_path( "/laura" ); laura->set_method_handler( "GET", get_laura_method_handler ); laura->set_authentication_handler( laura_authentication_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( ben ); service.publish( laura ); service.set_authentication_handler( service_authentication_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/basic_authentication/000077500000000000000000000000001270400672200206205ustar00rootroot00000000000000restbed-4.0/example/basic_authentication/source/000077500000000000000000000000001270400672200221205ustar00rootroot00000000000000restbed-4.0/example/basic_authentication/source/example.cpp000066400000000000000000000030061270400672200242560ustar00rootroot00000000000000/* * Example illustrating HTTP Basic service authentication. * * Server Usage: * ./distribution/example/basic_authentication * * Client Usage: * curl -w'\n' -v -XGET 'http://Corvusoft:Glasgow@localhost:1984/resource' */ #include #include #include #include #include using namespace std; using namespace restbed; void authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" ) { session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } ); } else { callback( session ); } } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_authentication_handler( authentication_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/bind_service_address/000077500000000000000000000000001270400672200206015ustar00rootroot00000000000000restbed-4.0/example/bind_service_address/source/000077500000000000000000000000001270400672200221015ustar00rootroot00000000000000restbed-4.0/example/bind_service_address/source/example.cpp000066400000000000000000000017071270400672200242450ustar00rootroot00000000000000/* * Example illustrating binding service to specific address. * * Server Usage: * ./distribution/example/bind_service_address * * Client Usage: * curl -w'\n' -v -XGET 'http://127.0.0.1:1984/resource' */ #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_bind_address( "127.0.0.1" ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/custom_methods/000077500000000000000000000000001270400672200174755ustar00rootroot00000000000000restbed-4.0/example/custom_methods/source/000077500000000000000000000000001270400672200207755ustar00rootroot00000000000000restbed-4.0/example/custom_methods/source/example.cpp000066400000000000000000000015231270400672200231350ustar00rootroot00000000000000/* * Example illustrating custom HTTP methods. * * Server Usage: * ./distribution/example/custom_methods * * Client Usage: * curl -w'\n' -v -X NOP 'http://localhost:1984/resource' */ #include #include #include using namespace std; using namespace restbed; void nop_method_handler( const shared_ptr< Session > session ) { session->close( 666 ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "NOP", nop_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/custom_status_codes/000077500000000000000000000000001270400672200205325ustar00rootroot00000000000000restbed-4.0/example/custom_status_codes/source/000077500000000000000000000000001270400672200220325ustar00rootroot00000000000000restbed-4.0/example/custom_status_codes/source/example.cpp000066400000000000000000000016261270400672200241760ustar00rootroot00000000000000/* * Example illustrating custom HTTP status codes. * * Server Usage: * ./distribution/example/custom_status_codes * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource' */ #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( 418 ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); settings->set_status_message( 418, "I'm a teapot" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/digest_authentication/000077500000000000000000000000001270400672200210165ustar00rootroot00000000000000restbed-4.0/example/digest_authentication/source/000077500000000000000000000000001270400672200223165ustar00rootroot00000000000000restbed-4.0/example/digest_authentication/source/example.cpp000066400000000000000000000036451270400672200244650ustar00rootroot00000000000000/* * Example illustrating HTTP Digest service authentication. * * Server Usage: * ./distribution/example/digest_authentication * * Client Usage: * curl -w'\n' -v --digest -XGET 'http://Corvusoft:Glasgow@localhost:1984/resource' */ #include #include #include #include #include using namespace std; using namespace restbed; string build_authenticate_header( void ) { string header = "Digest realm=\"Restbed\","; header += "algorithm=\"MD5\","; header += "stale=false,"; header += "opaque=\"0000000000000000\","; header += "nonce=\"Ny8yLzIwMDIgMzoyNjoyNCBQTQ\""; return header; } void authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { const auto request = session->get_request( ); auto authorisation = request->get_header( "Authorization" ); bool authorised = regex_match( authorisation, regex( ".*response=\"02863beb15feb659dfe4703d610d1b73\".*" ) ); if ( authorised ) { callback( session ); } else { session->close( UNAUTHORIZED, { { "WWW-Authenticate", build_authenticate_header( ) } } ); } } void get_method_handler( const shared_ptr< Session > session ) { return session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_authentication_handler( authentication_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/error_handling/000077500000000000000000000000001270400672200174355ustar00rootroot00000000000000restbed-4.0/example/error_handling/source/000077500000000000000000000000001270400672200207355ustar00rootroot00000000000000restbed-4.0/example/error_handling/source/example.cpp000066400000000000000000000036561270400672200231060ustar00rootroot00000000000000/* * Example illustrating custom service and resource error hanlding. * * Server Usage: * ./distribution/example/error_handling * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resources/1' * curl -w'\n' -v -XGET 'http://localhost:1984/resources/2' */ #include #include #include #include using namespace std; using namespace restbed; void faulty_method_handler( const shared_ptr< Session > ) { throw SERVICE_UNAVAILABLE; } void resource_error_handler( const int, const exception&, const shared_ptr< Session > session ) { if ( session->is_open( ) ) { session->close( 6000, "Custom Resource Internal Server Error", { { "Content-Length", "37" } } ); } else { session->close( ); fprintf( stderr, "Custom Resource Internal Server Error\n" ); } } void service_error_handler( const int, const exception&, const shared_ptr< Session > session ) { if ( session->is_open( ) ) { session->close( 5000, "Custom Service Internal Server Error", { { "Content-Length", "36" } } ); } else { session->close( ); fprintf( stderr, "Custom Service Internal Server Error\n" ); } } int main( const int, const char** ) { auto one = make_shared< Resource >( ); one->set_path( "/resources/1" ); one->set_method_handler( "GET", faulty_method_handler ); auto two = make_shared< Resource >( ); two->set_path( "/resources/2" ); two->set_method_handler( "GET", faulty_method_handler ); two->set_error_handler( &resource_error_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( one ); service.publish( two ); service.set_error_handler( service_error_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/http_client/000077500000000000000000000000001270400672200167555ustar00rootroot00000000000000restbed-4.0/example/http_client/source/000077500000000000000000000000001270400672200202555ustar00rootroot00000000000000restbed-4.0/example/http_client/source/example.cpp000066400000000000000000000031531270400672200224160ustar00rootroot00000000000000/* * Example illustrating a HTTP client. * * Usage: * ./distribution/example/http_client */ #include #include #include #include #include using namespace std; using namespace restbed; void print( const shared_ptr< Response >& response ) { fprintf( stderr, "*** Response ***\n" ); fprintf( stderr, "Status Code: %i\n", response->get_status_code( ) ); fprintf( stderr, "Status Message: %s\n", response->get_status_message( ).data( ) ); fprintf( stderr, "HTTP Version: %.1f\n", response->get_version( ) ); fprintf( stderr, "HTTP Protocol: %s\n", response->get_protocol( ).data( ) ); for ( const auto header : response->get_headers( ) ) { fprintf( stderr, "Header '%s' > '%s'\n", header.first.data( ), header.second.data( ) ); } auto length = 0; response->get_header( "Content-Length", length ); Http::fetch( length, response ); fprintf( stderr, "Body: %.*s...\n\n", 25, response->get_body( ).data( ) ); } int main( const int, const char** ) { auto request = make_shared< Request >( Uri( "http://www.corvusoft.co.uk:80/?query=search%20term" ) ); request->set_header( "Accept", "*/*" ); request->set_header( "Host", "www.corvusoft.co.uk" ); auto response = Http::sync( request ); print( response ); auto future = Http::async( request, [ ]( const shared_ptr< Request >, const shared_ptr< Response > response ) { fprintf( stderr, "Printing async response\n" ); print( response ); } ); future.wait( ); return EXIT_SUCCESS; } restbed-4.0/example/http_service/000077500000000000000000000000001270400672200171375ustar00rootroot00000000000000restbed-4.0/example/http_service/source/000077500000000000000000000000001270400672200204375ustar00rootroot00000000000000restbed-4.0/example/http_service/source/example.cpp000066400000000000000000000021671270400672200226040ustar00rootroot00000000000000/* * Example illustrating HTTP service. * * Server Usage: * sudo ./distribution/example/http_service * * Client Usage: * curl -w'\n' -v -X POST --data 'Hello, Restbed' 'http://localhost/resource' */ #include #include #include using namespace std; using namespace restbed; void post_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); size_t content_length = 0; request->get_header( "Content-Length", content_length ); session->fetch( content_length, [ request ]( const shared_ptr< Session > session, const Bytes & body ) { fprintf( stdout, "%.*s\n", ( int ) body.size( ), body.data( ) ); session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "POST", post_method_handler ); Service service; service.publish( resource ); service.start( ); return EXIT_SUCCESS; } restbed-4.0/example/https_client/000077500000000000000000000000001270400672200171405ustar00rootroot00000000000000restbed-4.0/example/https_client/resource/000077500000000000000000000000001270400672200207675ustar00rootroot00000000000000restbed-4.0/example/https_client/resource/certificates/000077500000000000000000000000001270400672200234345ustar00rootroot00000000000000restbed-4.0/example/https_client/resource/certificates/578d5c04.0000077700000000000000000000000001270400672200345602Equifax_Secure_Certificate_Authority.pemustar00rootroot00000000000000restbed-4.0/example/https_client/resource/certificates/Equifax_Secure_Certificate_Authority.pem000066400000000000000000000022101270400672200334140ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 -----END CERTIFICATE-----restbed-4.0/example/https_client/source/000077500000000000000000000000001270400672200204405ustar00rootroot00000000000000restbed-4.0/example/https_client/source/verify_none.cpp000066400000000000000000000027611270400672200234750ustar00rootroot00000000000000/* * Example illustrating a HTTPS client with no peer verification. * * Usage: * ./distribution/example/https_client_verify_none */ #include #include #include #include using namespace std; using namespace restbed; int main( const int, const char** ) { auto request = make_shared< Request >( Uri( "https://www.google.co.nz/" ) ); request->set_header( "Accept", "*/*" ); request->set_header( "Host", "www.google.co.nz" ); request->set_query_parameter( "query", "search term" ); auto response = Http::sync( request ); fprintf( stderr, "*** Response ***\n" ); fprintf( stderr, "Status Code: %i\n", response->get_status_code( ) ); fprintf( stderr, "Status Message: %s\n", response->get_status_message( ).data( ) ); fprintf( stderr, "HTTP Version: %.1f\n", response->get_version( ) ); fprintf( stderr, "HTTP Protocol: %s\n", response->get_protocol( ).data( ) ); for ( const auto header : response->get_headers( ) ) { fprintf( stderr, "Header '%s' > '%s'\n", header.first.data( ), header.second.data( ) ); } if ( response->has_header( "Transfer-Encoding" ) ) { Http::fetch( "\r\n", response ); } else { auto length = 0; response->get_header( "Content-Length", length ); Http::fetch( length, response ); } fprintf( stderr, "Body: %.*s...\n", 3, response->get_body( ).data( ) ); return EXIT_SUCCESS; } restbed-4.0/example/https_client/source/verify_peer.cpp000066400000000000000000000050351270400672200234660ustar00rootroot00000000000000/* * Example illustrating a HTTPS client with peer verification. * * 1. Download the certificate authority for the desired peer. * You can find this information via 'openssl s_client -connect www.google.com:443 -showcerts' * 2. Test you have the correct certificate 'openssl s_client -connect www.google.com:443 -showcerts -CAfile Equifax_Secure_Certificate_Authority.pem' * 3. OpenSSL requires a link to the certiciate using the subject hash and a '.0' extension. * The hash can be generated using 'openssl x509 -in Equifax_Secure_Certificate_Authority.pem -noout -subject_hash' * 4. ln -s Equifax_Secure_Certificate_Authority.pem 578d5c04.0 * 5. Verify your setup 'openssl s_client -showcerts -CApath ./DIRECTORY_WHERE_CERTFICIATE_LIVES -connect google.com:443' * 6. Make sure the file permissions are correct for the CA certificate * * Usage: * ./distribution/example/https_client_verify_peer */ #include #include #include #include using namespace std; using namespace restbed; int main( const int, const char** ) { auto request = make_shared< Request >( Uri( "https://www.google.com" ) ); request->set_header( "Accept", "*/*" ); request->set_header( "Host", "www.google.com" ); request->set_query_parameter( "query", "search term" ); auto ssl_settings = make_shared< SSLSettings >( ); ssl_settings->set_certificate_authority_pool( Uri( "file://distribution/resource/certificates", Uri::Relative ) ); auto settings = make_shared< Settings >( ); settings->set_ssl_settings( ssl_settings ); auto response = Http::sync( request, settings ); fprintf( stderr, "*** Response ***\n" ); fprintf( stderr, "Status Code: %i\n", response->get_status_code( ) ); fprintf( stderr, "Status Message: %s\n", response->get_status_message( ).data( ) ); fprintf( stderr, "HTTP Version: %.1f\n", response->get_version( ) ); fprintf( stderr, "HTTP Protocol: %s\n", response->get_protocol( ).data( ) ); for ( const auto header : response->get_headers( ) ) { fprintf( stderr, "Header '%s' > '%s'\n", header.first.data( ), header.second.data( ) ); } if ( response->has_header( "Transfer-Encoding" ) ) { Http::fetch( "\r\n", response ); } else { auto length = 0; response->get_header( "Content-Length", length ); Http::fetch( length, response ); } fprintf( stderr, "Body: %.*s...\n", 3, response->get_body( ).data( ) ); return EXIT_SUCCESS; } restbed-4.0/example/https_service/000077500000000000000000000000001270400672200173225ustar00rootroot00000000000000restbed-4.0/example/https_service/source/000077500000000000000000000000001270400672200206225ustar00rootroot00000000000000restbed-4.0/example/https_service/source/example.cpp000066400000000000000000000026761270400672200227740ustar00rootroot00000000000000/* * Example illustrating HTTPS service. * * Create Certificate * cd /tmp * openssl genrsa -out server.key 1024 * openssl req -new -key server.key -out server.csr * openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt * openssl dhparam -out dh768.pem 768 * * Server Usage: * sudo ./distribution/example/https_service * * Client Usage: * curl -k -v -w'\n' -X GET 'https://localhost/resource' */ #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto ssl_settings = make_shared< SSLSettings >( ); ssl_settings->set_http_disabled( true ); ssl_settings->set_private_key( Uri( "file:///tmp/server.key" ) ); ssl_settings->set_certificate( Uri( "file:///tmp/server.crt" ) ); ssl_settings->set_temporary_diffie_hellman( Uri( "file:///tmp/dh768.pem" ) ); auto settings = make_shared< Settings >( ); settings->set_ssl_settings( ssl_settings ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/logging/000077500000000000000000000000001270400672200160665ustar00rootroot00000000000000restbed-4.0/example/logging/source/000077500000000000000000000000001270400672200173665ustar00rootroot00000000000000restbed-4.0/example/logging/source/custom_logger.hpp000066400000000000000000000017401270400672200227520ustar00rootroot00000000000000#include #include #include #include using namespace std; using namespace restbed; class CustomLogger : public Logger { public: void stop( void ) { return; } void start( const shared_ptr< const Settings >& ) { return; } void log( const Level, const char* format, ... ) { va_list arguments; va_start( arguments, format ); vfprintf( stderr, format, arguments ); fprintf( stderr, "\n" ); va_end( arguments ); } void log_if( bool expression, const Level level, const char* format, ... ) { if ( expression ) { va_list arguments; va_start( arguments, format ); log( level, format, arguments ); va_end( arguments ); } } }; restbed-4.0/example/logging/source/example.cpp000066400000000000000000000017231270400672200215300ustar00rootroot00000000000000/* * Example illustrating custom logging. * * Server Usage: * ./distribution/example/logging * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resource' */ #include #include #include #include "custom_logger.hpp" using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_logger( make_shared< CustomLogger >( ) ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/multithreaded_service/000077500000000000000000000000001270400672200210135ustar00rootroot00000000000000restbed-4.0/example/multithreaded_service/source/000077500000000000000000000000001270400672200223135ustar00rootroot00000000000000restbed-4.0/example/multithreaded_service/source/example.cpp000066400000000000000000000021621270400672200244530ustar00rootroot00000000000000/* * Example illustrating multithreaded services. * * Server Usage: * ./distribution/example/multithreaded_service * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource' */ #include #include #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { stringstream id; id << ::this_thread::get_id( ); auto body = String::format( "Hello From Thread %s\n", id.str( ).data( ) ); session->close( OK, body, { { "Content-Length", ::to_string( body.length( ) ) } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_worker_limit( 4 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/pam_authentication/000077500000000000000000000000001270400672200203145ustar00rootroot00000000000000restbed-4.0/example/pam_authentication/source/000077500000000000000000000000001270400672200216145ustar00rootroot00000000000000restbed-4.0/example/pam_authentication/source/base64.h000066400000000000000000000074411270400672200230570ustar00rootroot00000000000000/* base64.cpp and base64.h Copyright (C) 2004-2008 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. René Nyffenegger rene.nyffenegger@adp-gmbh.ch */ static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; static inline bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { std::string ret; int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (in_len--) { char_array_3[i++] = *(bytes_to_encode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } if (i) { for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } return ret; } std::string base64_decode(std::string const& encoded_string) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) ret += char_array_3[i]; i = 0; } } if (i) { for (j = i; j <4; j++) char_array_4[j] = 0; for (j = 0; j <4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; } return ret; } restbed-4.0/example/pam_authentication/source/example.cpp000066400000000000000000000037341270400672200237620ustar00rootroot00000000000000/* * Example illustrating PAM (Portable Authentication Module) authentication. * * Server Usage: * ./distribution/example/pam_authentication * * Client Usage: * curl -w'\n' -v -XGET 'http://:@localhost:1984/resource' */ #include #include #include #include #include #include "pam.h" #include "base64.h" using namespace std; using namespace restbed; pair< string, string > decode_header( const string& value ) { auto data = base64_decode( value.substr( 6 ) ); auto delimiter = data.find_first_of( ':' ); auto username = data.substr( 0, delimiter ); auto password = data.substr( delimiter + 1 ); return make_pair( username, password ); } void authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { const auto request = session->get_request( ); const auto credentials = decode_header( request->get_header( "Authorization" ) ); bool authorised = pam_authorisation( credentials.first, credentials.second ); if ( not authorised ) { session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"Restbed\"" } } ); } else { callback( session ); } } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_authentication_handler( authentication_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/pam_authentication/source/pam.h000066400000000000000000000024101270400672200225370ustar00rootroot00000000000000#include #include #include #include using namespace std; #ifdef __linux__ #define SERVICE "system-auth" #else #define SERVICE "chkpasswd" #endif struct pam_response *response; int null_conv( int, const struct pam_message**, struct pam_response** reply, void* ) { *reply = response; return PAM_SUCCESS; } bool pam_authorisation( const string& username, const string& password ) { pam_handle_t* handle = nullptr; struct pam_conv conversation = { null_conv, nullptr }; int status = pam_start( SERVICE, username.data( ), &conversation, &handle ); if ( status == PAM_SUCCESS ) { response = new pam_response; response[ 0 ].resp_retcode = 0; char* pass = new char[ password.length( ) ]; response[ 0 ].resp = strncpy( pass, password.data( ), password.length( ) ); status = pam_authenticate( handle, 0 ); pam_end( handle, PAM_SUCCESS ); } if ( status not_eq PAM_SUCCESS ) { fprintf( stderr, "PAM Error: status=%i, message=%s\n", status, pam_strerror( handle, status ) ); fprintf( stderr, "Credentials: username='%s', password='%s'\n\n", username.data( ), password.data( ) ); return false; } return true; } restbed-4.0/example/path_parameters/000077500000000000000000000000001270400672200176175ustar00rootroot00000000000000restbed-4.0/example/path_parameters/source/000077500000000000000000000000001270400672200211175ustar00rootroot00000000000000restbed-4.0/example/path_parameters/source/example.cpp000066400000000000000000000020741270400672200232610ustar00rootroot00000000000000/* * Example illustrating path parameters. * * Server Usage: * ./distribution/example/path_parameters * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resource/' */ #include #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { const auto& request = session->get_request( ); const string body = "Hello, " + request->get_path_parameter( "name" ); session->close( OK, body, { { "Content-Length", ::to_string( body.size( ) ) } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource/{name: .*}" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/persistent_connection/000077500000000000000000000000001270400672200210575ustar00rootroot00000000000000restbed-4.0/example/persistent_connection/source/000077500000000000000000000000001270400672200223575ustar00rootroot00000000000000restbed-4.0/example/persistent_connection/source/example.cpp000066400000000000000000000026431270400672200245230ustar00rootroot00000000000000/* * Example illustrating persistent connections. * * Server Usage: * ./distribution/example/persistent_connection * * Client Usage: * curl -w'\n' -v 'http://localhost:1984/resources/persistent' 'http://localhost:1984/resources/intermittent' */ #include #include #include #include using namespace std; using namespace restbed; void get_intermittent_method_handler( const shared_ptr< Session > session ) { session->close( OK, "intermittent resource request", { { "Content-Length", "29" }, { "Connection", "close" } } ); } void get_persistent_method_handler( const shared_ptr< Session > session ) { session->yield( OK, "persistent resource request", { { "Content-Length", "27" }, { "Connection", "keep-alive" } } ); } int main( const int, const char** ) { auto persistent = make_shared< Resource >( ); persistent->set_path( "/resources/persistent" ); persistent->set_method_handler( "GET", get_persistent_method_handler ); auto intermittent = make_shared< Resource >( ); intermittent->set_path( "/resources/intermittent" ); intermittent->set_method_handler( "GET", get_intermittent_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); Service service; service.publish( persistent ); service.publish( intermittent ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/publishing_multipath_resources/000077500000000000000000000000001270400672200227655ustar00rootroot00000000000000restbed-4.0/example/publishing_multipath_resources/source/000077500000000000000000000000001270400672200242655ustar00rootroot00000000000000restbed-4.0/example/publishing_multipath_resources/source/example.cpp000066400000000000000000000017631270400672200264330ustar00rootroot00000000000000/* * Example illustrating multi-path resources. * * Server Usage: * ./distribution/example/publishing_multipath_resources * * Client Usage: * curl -w'\n' -v 'http://localhost:1984/messages' * curl -w'\n' -v 'http://localhost:1984/queues/12/messages' */ #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/messages", "/queues/{id: [0-9]*}/messages" } ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/publishing_resources/000077500000000000000000000000001270400672200206765ustar00rootroot00000000000000restbed-4.0/example/publishing_resources/source/000077500000000000000000000000001270400672200221765ustar00rootroot00000000000000restbed-4.0/example/publishing_resources/source/example.cpp000066400000000000000000000024001270400672200243310ustar00rootroot00000000000000/* * Example illustrating simple POST method handler. * * Server Usage: * ./distribution/example/publishing_resources * * Client Usage: * curl -w'\n' -v -X POST --data 'Hello, Restbed' 'http://localhost:1984/resource' */ #include #include #include using namespace std; using namespace restbed; void post_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); int content_length = 0; request->get_header( "Content-Length", content_length ); session->fetch( content_length, [ ]( const shared_ptr< Session > session, const Bytes & body ) { fprintf( stdout, "%.*s\n", ( int ) body.size( ), body.data( ) ); session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "POST", post_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/resource_filtering/000077500000000000000000000000001270400672200203325ustar00rootroot00000000000000restbed-4.0/example/resource_filtering/source/000077500000000000000000000000001270400672200216325ustar00rootroot00000000000000restbed-4.0/example/resource_filtering/source/example.cpp000066400000000000000000000045311270400672200237740ustar00rootroot00000000000000/* * Example illustrating resource request filteration. * * Server Usage: * ./distribution/example/resource_filtering * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json' * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml' * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json' -H'Content-Type: application/json' * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml' -H'Content-Type: application/xml' * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/json' -H'Content-Type: application/xml' * curl -w'\n' -v -XGET 'http://localhost:1984/resource' -H'Accept: application/xml' -H'Content-Type: application/json' */ #include #include #include #include using namespace std; using namespace restbed; void get_xml_method_handler( const shared_ptr< Session > session ) { const multimap< string, string > headers { { "Content-Length", "30" }, { "Content-Type", "application/xml" } }; session->close( 200, "", headers ); } void get_json_method_handler( const shared_ptr< Session > session ) { const multimap< string, string > headers { { "Content-Length", "23" }, { "Content-Type", "application/json" } }; session->close( 200, "{ \"Hello\": \", World!\" }", headers ); } void failed_filter_validation_handler( const shared_ptr< Session > session ) { session->close( 400 ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_failed_filter_validation_handler( failed_filter_validation_handler ); resource->set_method_handler( "GET", { { "Accept", "application/xml" }, { "Content-Type", "application/xml" } }, &get_xml_method_handler ); resource->set_method_handler( "GET", { { "Accept", "application/json" }, { "Content-Type", "application/json" } }, &get_json_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/rules_engine/000077500000000000000000000000001270400672200171175ustar00rootroot00000000000000restbed-4.0/example/rules_engine/source/000077500000000000000000000000001270400672200204175ustar00rootroot00000000000000restbed-4.0/example/rules_engine/source/accept_rule.hpp000066400000000000000000000023301270400672200234140ustar00rootroot00000000000000#include #include #include #include #include using namespace std; using namespace restbed; class AcceptRule : public Rule { public: AcceptRule( void ) : Rule( ) { return; } virtual ~AcceptRule( void ) { return; } bool condition( const shared_ptr< Session > session ) final override { return session->get_request( )->has_header( "Accept" ); } void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override { const auto request = session->get_request( ); const auto type = request->get_header( "Accept", String::lowercase ); if ( type not_eq "text/plain" and type not_eq "*/*" ) { session->close( NOT_ACCEPTABLE, "Not Acceptable, must be 'text/plain' or '*/*'.", { { "Content-Length", "46" }, { "Content-Type", "text/plain" } } ); } else { callback( session ); } } }; restbed-4.0/example/rules_engine/source/example.cpp000066400000000000000000000023341270400672200225600ustar00rootroot00000000000000/* * Example illustrating rule engine. * * Server Usage: * ./distribution/example/rules_engine * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource' * curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"Host:" * curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"Accept: application/json" */ #include #include #include #include "host_rule.hpp" #include "accept_rule.hpp" using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Content-Type", "text/plain" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->add_rule( make_shared< AcceptRule >( ) ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.add_rule( make_shared< HostRule >( ) ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/rules_engine/source/host_rule.hpp000066400000000000000000000020011270400672200231250ustar00rootroot00000000000000#include #include #include #include #include using namespace std; using namespace restbed; class HostRule : public Rule { public: HostRule( void ) : Rule( ) { return; } virtual ~HostRule( void ) { return; } bool condition( const shared_ptr< Session > ) final override { return true; } void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override { const auto request = session->get_request( ); if ( not request->has_header( "Host" ) ) { session->close( BAD_REQUEST, "Bad Request! Host header required.", { { "Content-Length", "34" }, { "Content-Type", "text/plain" } } ); } else { callback( session ); } } }; restbed-4.0/example/schedule_work_on_service_runloop/000077500000000000000000000000001270400672200232705ustar00rootroot00000000000000restbed-4.0/example/schedule_work_on_service_runloop/source/000077500000000000000000000000001270400672200245705ustar00rootroot00000000000000restbed-4.0/example/schedule_work_on_service_runloop/source/example.cpp000066400000000000000000000022541270400672200267320ustar00rootroot00000000000000/* * Example illustrating how to schedule work on the service runloop. * * Server Usage: * ./distribution/example/schedule_work_on_service_runloop * * Client Usage: * Watch service console output to ensure tasks run. */ #include #include #include #include #include #include using namespace std; using namespace restbed; void multi_run_task( void ) { fprintf( stderr, "multi run task executed.\n" ); } void single_run_task( void ) { fprintf( stderr, "single run task executed.\n" ); } void get_method_handler( const shared_ptr< Session > session ) { session->close( 200 ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/api" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); auto service = make_shared< Service >( ); service->publish( resource ); service->schedule( single_run_task ); service->schedule( multi_run_task, chrono::milliseconds( 1000 ) ); service->start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/service_ready_handler/000077500000000000000000000000001270400672200207615ustar00rootroot00000000000000restbed-4.0/example/service_ready_handler/source/000077500000000000000000000000001270400672200222615ustar00rootroot00000000000000restbed-4.0/example/service_ready_handler/source/example.cpp000066400000000000000000000021301270400672200244140ustar00rootroot00000000000000/* * Example illustrating service initialisation callback. * * Server Usage: * ./distribution/example/service_ready_handler * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource' */ #include #include #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } void service_ready_handler( Service& ) { fprintf( stderr, "Hey! The service is up and running." ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); auto service = make_shared< Service >( ); service->publish( resource ); service->set_ready_handler( service_ready_handler ); service->start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/serving_html/000077500000000000000000000000001270400672200171415ustar00rootroot00000000000000restbed-4.0/example/serving_html/resource/000077500000000000000000000000001270400672200207705ustar00rootroot00000000000000restbed-4.0/example/serving_html/resource/index.html000066400000000000000000000002451270400672200227660ustar00rootroot00000000000000 Corvusoft - Restbed

Hello, World!

restbed-4.0/example/serving_html/source/000077500000000000000000000000001270400672200204415ustar00rootroot00000000000000restbed-4.0/example/serving_html/source/example.cpp000066400000000000000000000030531270400672200226010ustar00rootroot00000000000000/* * Example illustrating the display of HTML webpages. * * Server Usage: * ./distribution/example/serving_html * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/static/index.html' */ #include #include #include #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); const string filename = request->get_path_parameter( "filename" ); ifstream stream( "./distribution/resource/" + filename, ifstream::in ); if ( stream.is_open( ) ) { const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) ); const multimap< string, string > headers { { "Content-Type", "text/html" }, { "Content-Length", ::to_string( body.length( ) ) } }; session->close( OK, body, headers ); } else { session->close( NOT_FOUND ); } } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/static/{filename: [a-z]*\\.html}" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/session_data/000077500000000000000000000000001270400672200171145ustar00rootroot00000000000000restbed-4.0/example/session_data/source/000077500000000000000000000000001270400672200204145ustar00rootroot00000000000000restbed-4.0/example/session_data/source/example.cpp000066400000000000000000000023611270400672200225550ustar00rootroot00000000000000/* * Example illustrating session data. * * Server Usage: * ./distribution/example/session_data * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource?styled=true' */ #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); string styled = request->get_query_parameter( "styled", "false" ); session->set( "styled", styled ); //pause, for example backend processing... session->sleep_for( chrono::milliseconds( 500 ), [ ]( const shared_ptr< Session > session ) { string value = session->get( "styled" ); session->close( 200, "styled response body == " + value ); } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/session_manager/000077500000000000000000000000001270400672200176155ustar00rootroot00000000000000restbed-4.0/example/session_manager/source/000077500000000000000000000000001270400672200211155ustar00rootroot00000000000000restbed-4.0/example/session_manager/source/example.cpp000066400000000000000000000101141270400672200232510ustar00rootroot00000000000000/* * Example illustrating session management. * * Server Usage: * ./distribution/example/session_manager * * Client Usage: * curl -w'\n' -v -X GET 'http://localhost:1984/resource?location=UK' * curl -w'\n' -v -X GET 'http://localhost:1984/resource' -H"SessionID: " */ #include #include #include #include #include #include #include #include using namespace std; using namespace restbed; class InMemorySessionManager : public SessionManager { public: InMemorySessionManager( void ) : m_sessions_lock( ), m_sessions( ) { return; } ~InMemorySessionManager( void ) { return; } void start( const shared_ptr< const Settings >& ) { return; } void create( const function< void ( const shared_ptr< Session > ) >& callback ) { static const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static uniform_int_distribution< > selector( 0, 35 ); auto seed = static_cast< unsigned int >( chrono::high_resolution_clock::now( ).time_since_epoch( ).count( ) ); static mt19937 generator( seed ); string key = ""; for ( int index = 0; index < 32; index++ ) { key += charset.at( selector( generator ) ); } callback( make_shared< Session >( key ) ); } void load( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { const auto request = session->get_request( ); unique_lock< mutex > lock( m_sessions_lock ); auto previous_session = m_sessions.find( request->get_header( "SessionID" ) ); if ( previous_session not_eq m_sessions.end( ) ) { const auto id = previous_session->second->get_id( ); session->set_id( id ); for ( const auto key : previous_session->second->keys( ) ) { session->set( key, previous_session->second->get( key ) ); } } lock.unlock( ); const auto key = session->get_id( ); session->set_header( "SessionID", key ); callback( session ); } void save( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { unique_lock< mutex > lock( m_sessions_lock ); m_sessions[ session->get_id( ) ] = session; lock.unlock( ); callback( session ); } void stop( void ) { return; } private: mutex m_sessions_lock; map< string, shared_ptr< Session > > m_sessions; }; void get_method_handler( const shared_ptr< Session > session ) { string body = "Previous Session Data\n"; for ( const auto key : session->keys( ) ) { string value = session->get( key ); body += key + "=" + value + "\n"; } const auto request = session->get_request( ); for ( const auto query_parameter : request->get_query_parameters( ) ) { session->set( query_parameter.first, query_parameter.second ); } session->close( OK, body, { { "Connection", "close" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); Service service; service.publish( resource ); service.set_session_manager( make_shared< InMemorySessionManager >( ) ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/signal_handling/000077500000000000000000000000001270400672200175615ustar00rootroot00000000000000restbed-4.0/example/signal_handling/source/000077500000000000000000000000001270400672200210615ustar00rootroot00000000000000restbed-4.0/example/signal_handling/source/example.cpp000066400000000000000000000021011270400672200232120ustar00rootroot00000000000000/* * Example illustrating signal handling. * * Server Usage: * ./distribution/example/signal_handling * * Client Usage: * kill -s SIGHUP * kill -s SIGTERM */ #include #include #include #include #include #include using namespace std; using namespace restbed; void sighup_handler( const int signal_number ) { fprintf( stderr, "Received SIGHUP signal number '%i'.\n", signal_number ); } void sigterm_handler( const int signal_number ) { fprintf( stderr, "Received SIGTERM signal number '%i'.\n", signal_number ); } void ready_handler( Service& ) { fprintf( stderr, "Service PID is '%i'.\n", getpid( ) ); } int main( const int, const char** ) { auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); Service service; service.set_ready_handler( ready_handler ); service.set_signal_handler( SIGHUP, sighup_handler ); service.set_signal_handler( SIGTERM, sigterm_handler ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/syslog_logging/000077500000000000000000000000001270400672200174665ustar00rootroot00000000000000restbed-4.0/example/syslog_logging/source/000077500000000000000000000000001270400672200207665ustar00rootroot00000000000000restbed-4.0/example/syslog_logging/source/example.cpp000066400000000000000000000017461270400672200231350ustar00rootroot00000000000000/* * Example illustrating custom logging with syslog. * * Server Usage: * ./distribution/example/syslog_logging * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resource' */ #include #include #include #include "syslog_logger.hpp" using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_logger( make_shared< SyslogLogger >( ) ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/example/syslog_logging/source/syslog_logger.hpp000066400000000000000000000037241270400672200243640ustar00rootroot00000000000000#include #include #include #include #include #include #include using namespace std; using namespace restbed; class SyslogLogger : public Logger { public: void stop( void ) { return; } void start( const shared_ptr< const Settings >& ) { return; } void log( const Level level, const char* format, ... ) { setlogmask( LOG_UPTO( LOG_DEBUG ) ); openlog( "Corvusoft Restbed", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1 ); int priority = 0; switch ( level ) { case FATAL: priority = LOG_CRIT; break; case ERROR: priority = LOG_ERR; break; case WARNING: priority = LOG_WARNING; break; case SECURITY: priority = LOG_ALERT; break; case INFO: case DEBUG: default: priority = LOG_NOTICE; } va_list arguments; va_start( arguments, format ); vsyslog( priority, format, arguments ); va_end( arguments ); closelog( ); } void log_if( bool expression, const Level level, const char* format, ... ) { if ( expression ) { va_list arguments; va_start( arguments, format ); log( level, format, arguments ); va_end( arguments ); } } }; restbed-4.0/example/transfer_encoding_response/000077500000000000000000000000001270400672200220505ustar00rootroot00000000000000restbed-4.0/example/transfer_encoding_response/source/000077500000000000000000000000001270400672200233505ustar00rootroot00000000000000restbed-4.0/example/transfer_encoding_response/source/example.cpp000066400000000000000000000025021270400672200255060ustar00rootroot00000000000000/* * Example illustrating Transfer-Encoding response processing. * * Server Usage: * ./distribution/example/transfer_encoding_response * * Client Usage: * curl -w'\n' -v -XGET 'http://localhost:1984/resources/item' */ #include #include #include #include #include using namespace std; using namespace restbed; void get_method_handler( const shared_ptr< Session > session ) { session->yield( OK, "8\r\nrestbed \r\n", { { "Transfer-Encoding", "chunked" } }, [ ]( const shared_ptr< Session > session ) { session->sleep_for( chrono::milliseconds( 500 ), [ ]( const shared_ptr< Session > session ) { session->yield( "10\r\nchunked encoding\r\n", [ ]( const shared_ptr< Session > session ) { session->close( "0\r\n\r\n" ); } ); } ); } ); } int main( const int, const char** ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/item" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.start( settings ); return EXIT_SUCCESS; } restbed-4.0/legal/000077500000000000000000000000001270400672200140715ustar00rootroot00000000000000restbed-4.0/legal/LICENSE.AGPL000066400000000000000000000771201270400672200156270ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. restbed-4.0/legal/LICENSE.CPL000066400000000000000000000026731270400672200155230ustar00rootroot00000000000000 Corvusoft Permissive License Version 1.2, 10 March 2016 Copyright (c) 2016 Corvusoft Ltd. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Permission is hereby granted, to any person, that incurs a fee, obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software with limited restriction, including without limitation the rights to use, copy, modify, and merge copies of the Software, subject to the following conditions: a) You are not permitted to resell, relicense, and/or redistribute the Software as-is, or with modification, in either binary or source form, it must be incorporated within a larger body of work. b) The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. c) You MUST purchase a Corvusoft Permissive License. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. restbed-4.0/source/000077500000000000000000000000001270400672200143055ustar00rootroot00000000000000restbed-4.0/source/corvusoft/000077500000000000000000000000001270400672200163375ustar00rootroot00000000000000restbed-4.0/source/corvusoft/restbed/000077500000000000000000000000001270400672200177675ustar00rootroot00000000000000restbed-4.0/source/corvusoft/restbed/byte.hpp000066400000000000000000000006251270400672200214460ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_BYTE_H #define _RESTBED_BYTE_H 1 //System Includes #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { typedef uint8_t Byte; typedef std::vector< Byte > Bytes; } #endif /* _RESTBED_BYTE_H */ restbed-4.0/source/corvusoft/restbed/context_placeholder.hpp000066400000000000000000000043121270400672200245260ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_CONTEXT_PLACEHOLDER_H #define _RESTBED_CONTEXT_PLACEHOLDER_H 1 //System Includes #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations template< typename Type > class ContextPlaceholder : public ContextPlaceholderBase { public: //Friends //Definitions //Constructor ContextPlaceholder( const Type& value ) : ContextPlaceholderBase( ), m_value( value ) { return; } virtual ~ContextPlaceholder( void ) { return; } //Functionality //Getters const std::type_info& type( void ) const { return typeid( Type ); } //Setters //Operators operator Type( void ) { return m_value; } //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors ContextPlaceholder( void ) = delete; ContextPlaceholder( const ContextPlaceholder& original ) = delete; //Functionality //Getters //Setters //Operators ContextPlaceholder& operator =( const ContextPlaceholder& value ) = delete; //Properties const Type m_value; }; } #endif /* _RESTBED_CONTEXT_PLACEHOLDER_H */ restbed-4.0/source/corvusoft/restbed/context_placeholder_base.hpp000066400000000000000000000033551270400672200255260ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_CONTEXT_PLACEHOLDER_BASE_H #define _RESTBED_CONTEXT_PLACEHOLDER_BASE_H 1 //System Includes #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class ContextPlaceholderBase { public: //Friends //Definitions //Constructor //Functionality //Getters virtual const std::type_info& type( void ) const = 0; //Setters //Operators //Properties protected: //Friends //Definitions //Constructors ContextPlaceholderBase( void ) = default; virtual ~ContextPlaceholderBase( void ) = default; //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors ContextPlaceholderBase( const ContextPlaceholderBase& original ) = delete; //Functionality //Getters //Setters //Operators ContextPlaceholderBase& operator =( const ContextPlaceholderBase& value ) = delete; //Properties }; } #endif /* _RESTBED_CONTEXT_PLACEHOLDER_BASE_H */ restbed-4.0/source/corvusoft/restbed/context_value.hpp000066400000000000000000000050241270400672200233610ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_CONTEXT_VALUE_H #define _RESTBED_CONTEXT_VALUE_H 1 //System Includes #include #include #include #include //Project Includes #include #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class ContextValue { public: //Friends //Definitions //Constructors template< typename Type > ContextValue( const Type& value ) : m_placeholder( new ContextPlaceholder< Type >( value ) ) { return; } ContextValue( const ContextValue& original ) : m_placeholder( original.m_placeholder ) { return; } virtual ~ContextValue( void ) { return; } //Functionality //Getters //Setters //Operators template< typename Type > operator Type( void ) const { if ( typeid( Type ) not_eq m_placeholder->type( ) ) { throw std::bad_cast( ); } auto placeholder = static_cast< ContextPlaceholder< Type >* >( m_placeholder.get( ) ); return *placeholder; } //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors ContextValue( void ) = delete; //Functionality //Getters //Setters //Operators ContextValue& operator =( const ContextValue& value ) = delete; //Properties const std::shared_ptr< ContextPlaceholderBase > m_placeholder; }; } #endif /* _RESTBED_CONTEXT_VALUE_H */ restbed-4.0/source/corvusoft/restbed/detail/000077500000000000000000000000001270400672200212315ustar00rootroot00000000000000restbed-4.0/source/corvusoft/restbed/detail/http_impl.cpp000066400000000000000000000260451270400672200237440ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/detail/http_impl.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" //External Includes #include #include #include #include #ifdef BUILD_SSL #include #endif //System Namespaces using std::bind; using std::stod; using std::regex; using std::smatch; using std::string; using std::istream; using std::function; using std::multimap; using std::to_string; using std::shared_ptr; using std::error_code; using std::make_shared; using std::placeholders::_1; using std::placeholders::_2; //Project Namespaces //External Namespaces using asio::buffer; using asio::ip::tcp; using asio::streambuf; using asio::io_service; #ifdef BUILD_SSL using asio::ssl::stream; #endif namespace restbed { namespace detail { Bytes HttpImpl::to_bytes( const shared_ptr< Request >& request ) { auto path = request->get_path( ); auto parameters = request->get_query_parameters( ); if ( not parameters.empty( ) ) { string query = String::empty; for ( const auto parameter : parameters ) { query += Uri::encode_parameter( parameter.first ) + "=" + Uri::encode_parameter( parameter.second ) + "&"; } path += "?" + query.substr( 0, query.length( ) - 1 ); } auto uri = request->m_pimpl->m_uri; if ( uri not_eq nullptr and not uri->get_fragment( ).empty( ) ) { path += "#" + uri->get_fragment( ); } auto protocol = request->get_protocol( ); if ( String::uppercase( protocol ) == "HTTPS" ) { protocol = "HTTP"; } auto data = String::format( "%s %s %s/%.1f\r\n", request->get_method( ).data( ), path.data( ), protocol.data( ), request->get_version( ) ); auto headers = request->get_headers( ); if ( not headers.empty( ) ) { data += String::join( headers, ": ", "\r\n" ) + "\r\n"; } data += "\r\n"; auto bytes = String::to_bytes( data ); auto body = request->get_body( ); if ( not body.empty( ) ) { bytes.insert( bytes.end( ), body.begin( ), body.end( ) ); } return bytes; } void HttpImpl::socket_setup( const shared_ptr< Request >& request, const shared_ptr< const Settings >& settings ) { if ( request->m_pimpl->m_socket == nullptr ) { if ( request->m_pimpl->m_io_service == nullptr ) { request->m_pimpl->m_io_service = make_shared< asio::io_service >( ); } #ifdef BUILD_SSL if ( String::uppercase( request->m_pimpl->m_protocol ) == "HTTPS" ) { ssl_socket_setup( request, settings->get_ssl_settings( ) ); } else { #endif auto socket = make_shared< tcp::socket >( *request->m_pimpl->m_io_service ); request->m_pimpl->m_socket = make_shared< SocketImpl >( socket ); #ifdef BUILD_SSL } #endif } request->m_pimpl->m_socket->set_timeout( settings->get_connection_timeout( ) ); } #ifdef BUILD_SSL void HttpImpl::ssl_socket_setup( const shared_ptr< Request >& request, const shared_ptr< const SSLSettings >& settings ) { asio::ssl::context context( asio::ssl::context::sslv23 ); shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > > socket = nullptr; if ( settings not_eq nullptr ) { const auto pool = settings->get_certificate_authority_pool( ); if ( pool.empty( ) ) { context.set_default_verify_paths( ); } else { context.add_verify_path( settings->get_certificate_authority_pool( ) ); } socket = make_shared< asio::ssl::stream< asio::ip::tcp::socket > >( *request->m_pimpl->m_io_service, context ); socket->set_verify_mode( asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert ); } else { socket = make_shared< asio::ssl::stream< asio::ip::tcp::socket > >( *request->m_pimpl->m_io_service, context ); socket->set_verify_mode( asio::ssl::verify_none ); } socket->set_verify_callback( asio::ssl::rfc2818_verification( request->get_host( ) ) ); request->m_pimpl->m_socket = make_shared< SocketImpl >( socket ); } #endif void HttpImpl::request_handler( const error_code& error, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback ) { if ( error ) { const auto body = String::format( "Failed to locate HTTP endpoint: %s", error.message( ).data( ) ); return callback( request, create_error_response( request, body ) ); } request->m_pimpl->m_socket->write( to_bytes( request ), bind( write_handler, _1, _2, request, callback ) ); } void HttpImpl::write_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback ) { if ( error ) { const auto body = String::format( "Socket write failed: %s", error.message( ).data( ) ); return callback( request, create_error_response( request, body ) ); } request->m_pimpl->m_buffer = make_shared< asio::streambuf >( ); request->m_pimpl->m_socket->read( request->m_pimpl->m_buffer, "\r\n", bind( read_status_handler, _1, _2, request, callback ) ); } const shared_ptr< Response > HttpImpl::create_error_response( const shared_ptr< Request >& request, const string& message ) { auto response = request->m_pimpl->m_response; response->set_protocol( request->get_protocol( ) ); response->set_version( request->get_version( ) ); response->set_status_code( 0 ); response->set_status_message( "Error" ); response->set_header( "Content-Type", "text/plain; utf-8" ); response->set_header( "Content-Length", ::to_string( message.length( ) ) ); response->set_body( message ); return response; } void HttpImpl::read_status_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback ) { if ( error ) { const auto body = String::format( "Failed to read HTTP response status line: %s", error.message( ).data( ) ); return callback( request, create_error_response( request, body ) ); } istream response_stream( request->m_pimpl->m_buffer.get( ) ); string status_line = String::empty; getline( response_stream, status_line ); smatch matches; static const regex status_line_pattern( "^([a-zA-Z]+)\\/(\\d*\\.?\\d*) (-?\\d+) (.+)\\r$" ); if ( not regex_match( status_line, matches, status_line_pattern ) or matches.size( ) not_eq 5 ) { const auto body = String::format( "HTTP response status line malformed: '%s'", status_line.data( ) ); return callback( request, create_error_response( request, body ) ); } auto response = request->m_pimpl->m_response; response->set_protocol( matches[ 1 ].str( ) ); response->set_version( stod( matches[ 2 ].str( ) ) ); response->set_status_code( stoi( matches[ 3 ].str( ) ) ); response->set_status_message( matches[ 4 ].str( ) ); request->m_pimpl->m_socket->read( request->m_pimpl->m_buffer, "\r\n\r\n", bind( read_headers_handler, _1, _2, request, callback ) ); } void HttpImpl::read_headers_handler( const error_code& error, const size_t, const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback ) { if ( error == asio::error::eof ) { return callback( request, request->m_pimpl->m_response ); } if ( error ) { const auto body = String::format( "Failed to read HTTP response status headers: '%s'", error.message( ).data( ) ); return callback( request, create_error_response( request, body ) ); } string header = String::empty; multimap< string, string > headers = { }; istream response_stream( request->m_pimpl->m_buffer.get( ) ); while ( getline( response_stream, header ) and header not_eq "\r" ) { static const regex header_pattern( "^([^:.]*): *(.*)\\s*$" ); smatch matches; if ( not regex_match( header, matches, header_pattern ) or matches.size( ) not_eq 3 ) { const auto body = String::format( "Malformed HTTP response header: '%s'", header.data( ) ); return callback( request, create_error_response( request, body ) ); } headers.insert( make_pair( matches[ 1 ], matches[ 2 ] ) ); } auto response = request->m_pimpl->m_response; response->set_headers( headers ); callback( request, response ); } } } restbed-4.0/source/corvusoft/restbed/detail/http_impl.hpp000066400000000000000000000071551270400672200237520ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_HTTP_H #define _RESTBED_DETAIL_HTTP_H 1 //System Includes #include #include #include #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Fordward Declarations class Request; class Response; class Settings; class SSLSettings; namespace detail { //Forward Declarations class HttpImpl { public: //Friends //Definitions //Constructors //Functionality static Bytes to_bytes( const std::shared_ptr< Request >& value ); static void socket_setup( const std::shared_ptr< Request >& request, const std::shared_ptr< const Settings >& settings ); #ifdef BUILD_SSL static void ssl_socket_setup( const std::shared_ptr< Request >& request, const std::shared_ptr< const SSLSettings >& settings ); #endif static void request_handler( const std::error_code& error, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback ); static void write_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback ); //Getters //Setters //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors HttpImpl( void ) = delete; virtual ~HttpImpl( void ) = delete; HttpImpl( const HttpImpl& original ) = delete; //Functionality static const std::shared_ptr< Response > create_error_response( const std::shared_ptr< Request >& request, const std::string& message ); static void read_status_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback ); static void read_headers_handler( const std::error_code& error, const std::size_t length, const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback ); //Getters //Setters //Operators HttpImpl& operator =( const HttpImpl& value ) = delete; //Properties }; } } #endif /* _RESTBED_DETAIL_HTTP_H */ restbed-4.0/source/corvusoft/restbed/detail/request_impl.hpp000066400000000000000000000032501270400672200244530ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_REQUEST_IMPL_H #define _RESTBED_DETAIL_REQUEST_IMPL_H 1 //System Includes #include #include #include #include //Project Includes #include //External Includes #include #include //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Uri; class Response; namespace detail { //Forward Declarations class SocketImpl; struct RequestImpl { Bytes m_body { }; uint16_t m_port = 80; double m_version = 1.1; std::string m_host = ""; std::string m_path = "/"; std::string m_method = "GET"; std::string m_protocol = "HTTP"; std::shared_ptr< Uri > m_uri = nullptr; std::shared_ptr< Response > m_response = nullptr; std::multimap< std::string, std::string > m_headers { }; std::map< std::string, std::string > m_path_parameters { }; std::multimap< std::string, std::string > m_query_parameters { }; std::shared_ptr< SocketImpl > m_socket = nullptr; std::shared_ptr< asio::streambuf > m_buffer = nullptr; std::shared_ptr< asio::io_service > m_io_service = nullptr; }; } } #endif /* _RESTBED_DETAIL_REQUEST_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/resource_impl.hpp000066400000000000000000000030261270400672200246130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_RESOURCE_IMPL_H #define _RESTBED_DETAIL_RESOURCE_IMPL_H 1 //System Includes #include #include #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Rule; class Session; namespace detail { //Forward Declarations struct ResourceImpl { std::set< std::string > m_paths { }; std::set< std::string > m_methods { }; std::vector< std::shared_ptr< Rule > > m_rules { }; std::multimap< std::string, std::string > m_default_headers { }; std::function< void ( const std::shared_ptr< Session > ) > m_failed_filter_validation_handler = nullptr; std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler = nullptr; std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) > m_authentication_handler = nullptr; std::multimap< std::string, std::pair< std::multimap< std::string, std::string >, std::function< void ( const std::shared_ptr< Session > ) > > > m_method_handlers { }; }; } } #endif /* _RESTBED_DETAIL_RESOURCE_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/response_impl.hpp000066400000000000000000000020021270400672200246130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_RESPONSE_IMPL_H #define _RESTBED_DETAIL_RESPONSE_IMPL_H 1 //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/byte.hpp" //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Request; namespace detail { //Forward Declarations struct ResponseImpl { Bytes m_body { }; double m_version = 1.1; int m_status_code = 0; std::string m_protocol = "HTTP"; std::string m_status_message = ""; std::multimap< std::string, std::string > m_headers { }; std::weak_ptr< Request > m_request = std::weak_ptr< Request >( ); }; } } #endif /* _RESTBED_DETAIL_RESPONSE_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/rule_engine_impl.hpp000066400000000000000000000025451270400672200252650ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_RULE_ENGINE_H #define _RESTBED_DETAIL_RULE_ENGINE_H 1 //System Includes #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Rule; class Session; namespace detail { //Forward Declarations static void rule_engine( const std::shared_ptr< Session > session, const std::vector< std::shared_ptr< Rule > >& rules, const std::function< void ( const std::shared_ptr< Session > ) >& callback, std::size_t index = 0 ) { while ( index not_eq rules.size( ) ) { auto rule = rules.at( index ); index++; if ( rule->condition( session ) ) { rule->action( session, bind( rule_engine, session, rules, callback, index ) ); return; } } if ( index == rules.size( ) ) { callback( session ); } } } } #endif /* _RESTBED_DETAIL_RULE_ENGINE_H */ restbed-4.0/source/corvusoft/restbed/detail/rule_impl.hpp000066400000000000000000000010261270400672200237310ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_RULE_IMPL_H #define _RESTBED_DETAIL_RULE_IMPL_H 1 //System Includes #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations namespace detail { //Forward Declarations struct RuleImpl { int m_priority = 0; }; } } #endif /* _RESTBED_DETAIL_RULE_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/service_impl.cpp000066400000000000000000001023631270400672200244230ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/rule.hpp" #include "corvusoft/restbed/logger.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/resource.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/status_code.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/session_manager.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" #include "corvusoft/restbed/detail/service_impl.hpp" #include "corvusoft/restbed/detail/session_impl.hpp" #include "corvusoft/restbed/detail/resource_impl.hpp" #include "corvusoft/restbed/detail/rule_engine_impl.hpp" //External Includes //System Namespaces using std::set; using std::map; using std::pair; using std::bind; using std::regex; using std::string; using std::smatch; using std::istream; using std::find_if; using std::function; using std::multimap; using std::to_string; using std::exception; using std::shared_ptr; using std::error_code; using std::regex_error; using std::make_shared; using std::runtime_error; using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; using std::current_exception; using std::rethrow_exception; using std::regex_constants::icase; //Project Namespaces //External Namespaces using asio::ip::tcp; using asio::io_service; using asio::signal_set; using asio::ip::address; using asio::socket_base; using asio::system_error; namespace restbed { namespace detail { ServiceImpl::ServiceImpl( void ) : m_is_running( false ), m_logger( nullptr ), m_supported_methods( ), m_settings( nullptr ), m_io_service( make_shared< ::io_service >( ) ), m_signal_set( nullptr ), m_session_manager( nullptr ), m_rules( ), m_workers( ), #ifdef BUILD_SSL m_ssl_settings( nullptr ), m_ssl_context( nullptr ), m_ssl_acceptor( nullptr ), #endif m_acceptor( nullptr ), m_resource_paths( ), m_resource_routes( ), m_ready_handler( nullptr ), m_signal_handlers( ), m_not_found_handler( nullptr ), m_method_not_allowed_handler( nullptr ), m_method_not_implemented_handler( nullptr ), m_failed_filter_validation_handler( nullptr ), m_error_handler( ServiceImpl::default_error_handler ), m_authentication_handler( nullptr ) { return; } ServiceImpl::~ServiceImpl( void ) { return; } void ServiceImpl::http_start( void ) { #ifdef BUILD_SSL if ( m_ssl_settings == nullptr or m_ssl_settings->has_disabled_http( ) == false ) { #endif if ( not m_settings->get_bind_address( ).empty( ) ) { const auto address = address::from_string( m_settings->get_bind_address( ) ); m_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( address, m_settings->get_port( ) ) ); } else { m_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( tcp::v6( ), m_settings->get_port( ) ) ); } m_acceptor->set_option( socket_base::reuse_address( true ) ); m_acceptor->listen( m_settings->get_connection_limit( ) ); http_listen( ); const auto location = get_http_uri( )->to_string( ); log( Logger::INFO, String::format( "Service accepting HTTP connections at '%s'.", location.data( ) ) ); #ifdef BUILD_SSL } #endif } void ServiceImpl::http_listen( void ) const { auto socket = make_shared< tcp::socket >( m_acceptor->get_io_service( ) ); m_acceptor->async_accept( *socket, bind( &ServiceImpl::create_session, this, socket, _1 ) ); } void ServiceImpl::setup_signal_handler( void ) { if ( m_signal_handlers.empty( ) ) { return; } m_signal_set = make_shared< signal_set >( *m_io_service ); for ( const auto signal_handler : m_signal_handlers ) { m_signal_set->add( signal_handler.first ); } m_signal_set->async_wait( bind( &ServiceImpl::signal_handler, this, _1, _2 ) ); } void ServiceImpl::signal_handler( const error_code& error, const int signal_number ) const { if ( error ) { log( Logger::WARNING, String::format( "Failed to process signal '%i', '%s'.", signal_number, error.message( ).data( ) ) ); return; } if ( m_signal_handlers.count( signal_number ) ) { m_signal_handlers.at( signal_number )( signal_number ); } m_signal_set->async_wait( bind( &ServiceImpl::signal_handler, this, _1, _2 ) ); } #ifdef BUILD_SSL void ServiceImpl::https_start( void ) { if ( m_ssl_settings not_eq nullptr ) { m_ssl_context = make_shared< asio::ssl::context >( asio::ssl::context::sslv23 ); m_ssl_context->set_default_verify_paths( ); auto passphrase = m_ssl_settings->get_passphrase( ); m_ssl_context->set_password_callback( [ passphrase ]( size_t, asio::ssl::context::password_purpose ) { return passphrase; } ); auto filename = m_ssl_settings->get_temporary_diffie_hellman( ); if ( not filename.empty( ) ) { m_ssl_context->use_tmp_dh_file( filename ); } filename = m_ssl_settings->get_certificate_authority_pool( ); if ( not filename.empty( ) ) { m_ssl_context->add_verify_path( filename ); } filename = m_ssl_settings->get_certificate_chain( ); if ( not filename.empty( ) ) { m_ssl_context->use_certificate_chain_file( filename ); } filename = m_ssl_settings->get_certificate( ); if ( not filename.empty( ) ) { m_ssl_context->use_certificate_file( filename, asio::ssl::context::pem ); } filename = m_ssl_settings->get_private_key( ); if ( not filename.empty( ) ) { m_ssl_context->use_private_key_file( filename, asio::ssl::context::pem ); } filename = m_ssl_settings->get_private_rsa_key( ); if ( not filename.empty( ) ) { m_ssl_context->use_rsa_private_key_file( filename, asio::ssl::context::pem ); } asio::ssl::context::options options = 0; options = ( m_ssl_settings->has_enabled_tlsv1( ) ) ? options : options | asio::ssl::context::no_tlsv1; options = ( m_ssl_settings->has_enabled_sslv2( ) ) ? options : options | asio::ssl::context::no_sslv2; options = ( m_ssl_settings->has_enabled_sslv3( ) ) ? options : options | asio::ssl::context::no_sslv3; options = ( m_ssl_settings->has_enabled_tlsv11( ) ) ? options : options | asio::ssl::context::no_tlsv1_1; options = ( m_ssl_settings->has_enabled_tlsv12( ) ) ? options : options | asio::ssl::context::no_tlsv1_2; options = ( m_ssl_settings->has_enabled_compression( ) ) ? options : options | asio::ssl::context::no_compression; options = ( m_ssl_settings->has_enabled_default_workarounds( ) ) ? options | asio::ssl::context::default_workarounds : options; options = ( m_ssl_settings->has_enabled_single_diffie_hellman_use( ) ) ? options | asio::ssl::context::single_dh_use : options; m_ssl_context->set_options( options ); if ( not m_ssl_settings->get_bind_address( ).empty( ) ) { const auto address = address::from_string( m_ssl_settings->get_bind_address( ) ); m_ssl_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( address, m_ssl_settings->get_port( ) ) ); } else { m_ssl_acceptor = make_shared< tcp::acceptor >( *m_io_service, tcp::endpoint( tcp::v6( ), m_ssl_settings->get_port( ) ) ); } m_ssl_acceptor->set_option( socket_base::reuse_address( true ) ); m_ssl_acceptor->listen( m_settings->get_connection_limit( ) ); https_listen( ); const auto location = get_https_uri( )->to_string( ); log( Logger::INFO, String::format( "Service accepting HTTPS connections at '%s'.", location.data( ) ) ); } } void ServiceImpl::https_listen( void ) const { auto socket = make_shared< asio::ssl::stream< tcp::socket > >( m_ssl_acceptor->get_io_service( ), *m_ssl_context ); m_ssl_acceptor->async_accept( socket->lowest_layer( ), bind( &ServiceImpl::create_ssl_session, this, socket, _1 ) ); } void ServiceImpl::create_ssl_session( const shared_ptr< asio::ssl::stream< tcp::socket > >& socket, const error_code& error ) const { if ( not error ) { socket->async_handshake( asio::ssl::stream_base::server, [ this, socket ]( const error_code & error ) { if ( error ) { log( Logger::SECURITY, String::format( "Failed SSL handshake, '%s'.", error.message( ).data( ) ) ); return; } auto connection = make_shared< SocketImpl >( socket, m_logger ); connection->set_timeout( m_settings->get_connection_timeout( ) ); m_session_manager->create( [ this, connection ]( const shared_ptr< Session > session ) { session->m_pimpl->m_settings = m_settings; session->m_pimpl->m_manager = m_session_manager; session->m_pimpl->m_error_handler = m_error_handler; session->m_pimpl->m_request = make_shared< Request >( ); session->m_pimpl->m_request->m_pimpl->m_socket = connection; session->m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( ); session->m_pimpl->m_keep_alive_callback = bind( &ServiceImpl::parse_request, this, _1, _2, _3 ); session->m_pimpl->m_request->m_pimpl->m_socket->read( session->m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", bind( &ServiceImpl::parse_request, this, _1, _2, session ) ); } ); } ); } else { if ( socket not_eq nullptr and socket->lowest_layer( ).is_open( ) ) { socket->lowest_layer( ).close( ); } log( Logger::WARNING, String::format( "Failed to create session, '%s'.", error.message( ).data( ) ) ); } https_listen( ); } #endif string ServiceImpl::sanitise_path( const string& path ) const { if ( path == "/" ) { return path; } smatch matches; string sanitised_path = ""; static const regex pattern( "^\\{[a-zA-Z0-9]+: ?(.*)\\}$" ); for ( auto folder : String::split( path, '/' ) ) { if ( folder.front( ) == '{' and folder.back( ) == '}' ) { if ( not regex_match( folder, matches, pattern ) or matches.size( ) not_eq 2 ) { throw runtime_error( String::format( "Resource path parameter declaration is malformed '%s'.", folder.data( ) ) ); } sanitised_path += '/' + matches[ 1 ].str( ); } else { sanitised_path += '/' + folder; } } if ( path.back( ) == '/' ) { sanitised_path += '/'; } return sanitised_path; } void ServiceImpl::not_found( const shared_ptr< Session > session ) const { log( Logger::INFO, String::format( "'%s' resource route not found '%s'.", session->get_origin( ).data( ), session->get_request( )->get_path( ).data( ) ) ); if ( m_not_found_handler not_eq nullptr ) { m_not_found_handler( session ); } else { session->close( NOT_FOUND ); } } bool ServiceImpl::has_unique_paths( const set< string >& paths ) const { if ( paths.empty( ) ) { return false; } for ( const auto& path : paths ) { if ( m_resource_routes.count( path ) ) { return false; } } return true; } void ServiceImpl::log( const Logger::Level level, const string& message ) const { if ( m_logger not_eq nullptr ) { try { m_logger->log( level, "%s", message.data( ) ); } catch ( ... ) { fprintf( stderr, "Failed to create log entry: %s", message.data( ) ); } } } void ServiceImpl::method_not_allowed( const shared_ptr< Session > session ) const { log( Logger::INFO, String::format( "'%s' '%s' method not allowed '%s'.", session->get_origin( ).data( ), session->get_request( )->get_method( ).data( ), session->get_request( )->get_path( ).data( ) ) ); if ( m_method_not_allowed_handler not_eq nullptr ) { m_method_not_allowed_handler( session ); } else { session->close( METHOD_NOT_ALLOWED ); } } void ServiceImpl::method_not_implemented( const shared_ptr< Session > session ) const { log( Logger::INFO, String::format( "'%s' '%s' method not implemented '%s'.", session->get_origin( ).data( ), session->get_request( )->get_method( ).data( ), session->get_request( )->get_path( ).data( ) ) ); if ( m_method_not_implemented_handler not_eq nullptr ) { m_method_not_implemented_handler( session ); } else { session->close( NOT_IMPLEMENTED ); } } void ServiceImpl::failed_filter_validation( const shared_ptr< Session > session ) const { log( Logger::INFO, String::format( "'%s' failed filter validation '%s'.", session->get_origin( ).data( ), session->get_request( )->get_path( ).data( ) ) ); if ( m_failed_filter_validation_handler not_eq nullptr ) { m_failed_filter_validation_handler( session ); } else { session->close( BAD_REQUEST, { { "Connection", "close" } } ); } } void ServiceImpl::router( const shared_ptr< Session > session ) const { log( Logger::INFO, String::format( "Incoming '%s' request from '%s' for route '%s'.", session->get_request( )->get_method( ).data( ), session->get_origin( ).data( ), session->get_request( )->get_path( ).data( ) ) ); if ( session->is_closed( ) ) { return; } rule_engine( session, m_rules, [ this ]( const shared_ptr< Session > session ) { const auto resource_route = find_if( m_resource_routes.begin( ), m_resource_routes.end( ), bind( &ServiceImpl::resource_router, this, session, _1 ) ); if ( resource_route == m_resource_routes.end( ) ) { return not_found( session ); } const auto path = resource_route->first; session->m_pimpl->m_resource = resource_route->second; const auto request = session->get_request( ); extract_path_parameters( path, request ); const auto callback = [ this ]( const shared_ptr< Session > session ) { rule_engine( session, session->m_pimpl->m_resource->m_pimpl->m_rules, [ this ]( const shared_ptr< Session > session ) { if ( session->is_closed( ) ) { return; } const auto request = session->get_request( ); auto method_handler = find_method_handler( session ); if ( method_handler == nullptr ) { if ( m_supported_methods.count( request->get_method( ) ) == 0 ) { method_handler = bind( &ServiceImpl::method_not_implemented, this, _1 ); } else { method_handler = bind( &ServiceImpl::method_not_allowed, this, _1 ); } } method_handler( session ); } ); }; if ( session->m_pimpl->m_resource->m_pimpl->m_authentication_handler not_eq nullptr ) { session->m_pimpl->m_resource->m_pimpl->m_authentication_handler( session, callback ); } else { callback( session ); } } ); } void ServiceImpl::create_session( const shared_ptr< tcp::socket >& socket, const error_code& error ) const { if ( not error ) { auto connection = make_shared< SocketImpl >( socket, m_logger ); connection->set_timeout( m_settings->get_connection_timeout( ) ); m_session_manager->create( [ this, connection ]( const shared_ptr< Session > session ) { session->m_pimpl->m_settings = m_settings; session->m_pimpl->m_manager = m_session_manager; session->m_pimpl->m_error_handler = m_error_handler; session->m_pimpl->m_request = make_shared< Request >( ); session->m_pimpl->m_request->m_pimpl->m_socket = connection; session->m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( ); session->m_pimpl->m_keep_alive_callback = bind( &ServiceImpl::parse_request, this, _1, _2, _3 ); session->m_pimpl->m_request->m_pimpl->m_socket->read( session->m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", bind( &ServiceImpl::parse_request, this, _1, _2, session ) ); } ); } else { if ( socket not_eq nullptr and socket->is_open( ) ) { socket->close( ); } log( Logger::WARNING, String::format( "Failed to create session, '%s'.", error.message( ).data( ) ) ); } http_listen( ); } void ServiceImpl::extract_path_parameters( const string& sanitised_path, const shared_ptr< const Request >& request ) const { smatch matches; static const regex pattern( "^\\{([a-zA-Z0-9]+): ?.*\\}$" ); const auto folders = String::split( request->get_path( ), '/' ); const auto declarations = String::split( m_resource_paths.at( sanitised_path ), '/' ); for ( size_t index = 0; index < folders.size( ) and index < declarations.size( ); index++ ) { const auto declaration = declarations[ index ]; if ( declaration.front( ) == '{' and declaration.back( ) == '}' ) { regex_match( declaration, matches, pattern ); request->m_pimpl->m_path_parameters.insert( make_pair( matches[ 1 ].str( ), folders[ index ] ) ); } } } function< void ( const shared_ptr< Session > ) > ServiceImpl::find_method_handler( const shared_ptr< Session > session ) const { const auto request = session->get_request( ); const auto resource = session->get_resource( ); const auto method_handlers = resource->m_pimpl->m_method_handlers.equal_range( request->get_method( ) ); bool failed_filter_validation = false; function< void ( const shared_ptr< Session > ) > method_handler = nullptr; for ( auto handler = method_handlers.first; handler not_eq method_handlers.second and method_handler == nullptr; handler++ ) { method_handler = handler->second.second; for ( const auto& filter : handler->second.first ) { for ( const auto& header : request->get_headers( filter.first ) ) { if ( not regex_match( header.second, regex( filter.second ) ) ) { method_handler = nullptr; failed_filter_validation = true; } } } } if ( failed_filter_validation and method_handler == nullptr ) { const auto handler = resource->m_pimpl->m_failed_filter_validation_handler; method_handler = ( handler == nullptr ) ? bind( &ServiceImpl::failed_filter_validation, this, _1 ) : handler; } return method_handler; } void ServiceImpl::authenticate( const shared_ptr< Session > session ) const { if ( m_authentication_handler not_eq nullptr ) { m_authentication_handler( session, [ this ]( const shared_ptr< Session > session ) { m_session_manager->load( session, bind( &ServiceImpl::router, this, _1 ) ); } ); } else { m_session_manager->load( session, bind( &ServiceImpl::router, this, _1 ) ); } } bool ServiceImpl::resource_router( const shared_ptr< Session > session, const pair< string, shared_ptr< const Resource > >& route ) const { const auto request = session->get_request( ); const auto path_folders = String::split( request->get_path( ), '/' ); const auto route_folders = String::split( m_settings->get_root( ) + "/" + route.first, '/' ); if ( path_folders.empty( ) and route_folders.empty( ) ) { return true; } bool match = false; if ( path_folders.size( ) == route_folders.size( ) ) { for ( size_t index = 0; index < path_folders.size( ); index++ ) { if ( m_settings->get_case_insensitive_uris( ) ) { match = regex_match( path_folders[ index ], regex( route_folders[ index ], icase ) ); } else { match = regex_match( path_folders[ index ], regex( route_folders[ index ] ) ); } if ( not match ) { break; } } } return match; } void ServiceImpl::default_error_handler( const int status, const exception& error, const shared_ptr< Session > session ) { if ( session not_eq nullptr and session->is_open( ) ) { string body = error.what( ); session->close( status, body, { { "Content-Type", "text/plain" }, { "Content-Length", ::to_string( body.length( ) ) } } ); } } const map< string, string > ServiceImpl::parse_request_line( istream& stream ) { smatch matches; static const regex pattern( "^([0-9a-zA-Z]*) ([a-zA-Z0-9:@_~!,;=#%&'\\-\\.\\/\\?\\$\\(\\)\\*\\+]+) (HTTP\\/[0-9]\\.[0-9])\\s*$" ); string data = ""; getline( stream, data ); if ( not regex_match( data, matches, pattern ) or matches.size( ) not_eq 4 ) { throw runtime_error( "Your client has issued a malformed or illegal request status line. That’s all we know." ); } const string protocol = matches[ 3 ].str( ); const auto delimiter = protocol.find_first_of( "/" ); return map< string, string > { { "path", matches[ 2 ].str( ) }, { "method", matches[ 1 ].str( ) }, { "version", protocol.substr( delimiter + 1 ) }, { "protocol", protocol.substr( 0, delimiter ) } }; } const multimap< string, string > ServiceImpl::parse_request_headers( istream& stream ) { smatch matches; string data = ""; multimap< string, string > headers; static const regex pattern( "^([^:.]*): *(.*)\\s*$" ); while ( getline( stream, data ) and data not_eq "\r" ) { if ( not regex_match( data, matches, pattern ) or matches.size( ) not_eq 3 ) { throw runtime_error( "Your client has issued a malformed or illegal request header. That’s all we know." ); } headers.insert( make_pair( matches[ 1 ].str( ), matches[ 2 ].str( ) ) ); } return headers; } void ServiceImpl::parse_request( const error_code& error, size_t, const shared_ptr< Session > session ) const try { if ( error ) { throw runtime_error( error.message( ) ); } istream stream( session->m_pimpl->m_request->m_pimpl->m_buffer.get( ) ); const auto items = parse_request_line( stream ); const auto uri = Uri::parse( "http://localhost" + items.at( "path" ) ); session->m_pimpl->m_request->m_pimpl->m_path = Uri::decode( uri.get_path( ) ); session->m_pimpl->m_request->m_pimpl->m_method = items.at( "method" ); session->m_pimpl->m_request->m_pimpl->m_version = stod( items.at( "version" ) ); session->m_pimpl->m_request->m_pimpl->m_headers = parse_request_headers( stream ); session->m_pimpl->m_request->m_pimpl->m_query_parameters = uri.get_query_parameters( ); authenticate( session ); } catch ( const int status_code ) { const auto error_handler = get_error_handler( session ); error_handler( status_code, runtime_error( m_settings->get_status_message( status_code ) ), session ); } catch ( const regex_error& re ) { const auto error_handler = get_error_handler( session ); error_handler( 500, re, session ); } catch ( const runtime_error& re ) { const auto error_handler = get_error_handler( session ); error_handler( 400, re, session ); } catch ( const exception& ex ) { const auto error_handler = get_error_handler( session ); error_handler( 500, ex, session ); } catch ( ... ) { auto cex = current_exception( ); if ( cex not_eq nullptr ) { try { rethrow_exception( cex ); } catch ( const exception& ex ) { const auto error_handler = get_error_handler( session ); error_handler( 500, ex, session ); } catch ( ... ) { const auto error_handler = get_error_handler( session ); error_handler( 500, runtime_error( "Internal Server Error" ), session ); } } else { const auto error_handler = get_error_handler( session ); error_handler( 500, runtime_error( "Internal Server Error" ), session ); } } const shared_ptr< const Uri > ServiceImpl::get_http_uri( void ) const { if ( m_acceptor == nullptr ) { return nullptr; } auto endpoint = m_acceptor->local_endpoint( ); auto address = endpoint.address( ); auto uri = String::empty; if ( address.is_v6( ) ) { uri = String::format( "http://[%s]:%u", address.to_string( ).data( ), endpoint.port( ) ); } else { uri = String::format( "http://%s:%u", address.to_string( ).data( ), endpoint.port( ) ); } return make_shared< const Uri >( uri ); } const shared_ptr< const Uri > ServiceImpl::get_https_uri( void ) const { #ifdef BUILD_SSL if ( m_ssl_acceptor == nullptr ) { return nullptr; } auto endpoint = m_ssl_acceptor->local_endpoint( ); auto address = endpoint.address( ); auto uri = String::empty; if ( address.is_v6( ) ) { uri = String::format( "https://[%s]:%u", address.to_string( ).data( ), endpoint.port( ) ); } else { uri = String::format( "https://%s:%u", address.to_string( ).data( ), endpoint.port( ) ); } return make_shared< const Uri >( uri ); #else throw runtime_error( "Not Implemented! Rebuild Restbed with SSL funcationality enabled." ); #endif } const function< void ( const int, const exception&, const shared_ptr< Session > ) > ServiceImpl::get_error_handler( const shared_ptr< Session >& session ) const { return ( session->m_pimpl->m_resource not_eq nullptr and session->m_pimpl->m_resource->m_pimpl->m_error_handler not_eq nullptr ) ? session->m_pimpl->m_resource->m_pimpl->m_error_handler : m_error_handler; } } } restbed-4.0/source/corvusoft/restbed/detail/service_impl.hpp000066400000000000000000000170001270400672200244210ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_SERVICE_IMPL_H #define _RESTBED_DETAIL_SERVICE_IMPL_H 1 //System Includes #include #include #include #include #include #include #include #include #include //Project Includes //External Includes #include #include #include #ifdef BUILD_SSL #include #endif //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Uri; class Rule; class Logger; class Session; class Resource; class Settings; class SessionManager; class SSLSettings; namespace detail { //Forward Declarations class ServiceImpl { public: //Friends //Definitions //Constructors ServiceImpl( void ); virtual ~ServiceImpl( void ); //Functionality void http_start( void ); void http_listen( void ) const; #ifdef BUILD_SSL void https_start( void ); void https_listen( void ) const; void create_ssl_session( const std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > >& socket, const std::error_code& error ) const; #endif void setup_signal_handler( ); void signal_handler( const std::error_code& error, const int signal_number ) const; std::string sanitise_path( const std::string& path ) const; void not_found( const std::shared_ptr< Session > session ) const; bool has_unique_paths( const std::set< std::string >& paths ) const; void log( const Logger::Level level, const std::string& message ) const; void method_not_allowed( const std::shared_ptr< Session > session ) const; void method_not_implemented( const std::shared_ptr< Session > session ) const; void failed_filter_validation( const std::shared_ptr< Session > session ) const; void router( const std::shared_ptr< Session > session ) const; void create_session( const std::shared_ptr< asio::ip::tcp::socket >& socket, const std::error_code& error ) const; void extract_path_parameters( const std::string& sanitised_path, const std::shared_ptr< const Request >& request ) const; std::function< void ( const std::shared_ptr< Session > ) > find_method_handler( const std::shared_ptr< Session > session ) const; void authenticate( const std::shared_ptr< Session > session ) const; bool resource_router( const std::shared_ptr< Session > session, const std::pair< std::string, std::shared_ptr< const Resource > >& route ) const; static void default_error_handler( const int status, const std::exception& error, const std::shared_ptr< Session > session ); static const std::map< std::string, std::string > parse_request_line( std::istream& stream ); static const std::multimap< std::string, std::string > parse_request_headers( std::istream& stream ); void parse_request( const std::error_code& error, std::size_t length, const std::shared_ptr< Session > session ) const; //Getters const std::shared_ptr< const Uri > get_http_uri( void ) const; const std::shared_ptr< const Uri > get_https_uri( void ) const; const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > get_error_handler( const std::shared_ptr< Session >& session ) const; //Setters //Operators //Properties bool m_is_running; std::shared_ptr< Logger > m_logger; std::set< std::string > m_supported_methods; std::shared_ptr< const Settings > m_settings; std::shared_ptr< asio::io_service > m_io_service; std::shared_ptr< asio::signal_set > m_signal_set; std::shared_ptr< SessionManager > m_session_manager; std::vector< std::shared_ptr< Rule > > m_rules; std::vector< std::shared_ptr< std::thread > > m_workers; #ifdef BUILD_SSL std::shared_ptr< const SSLSettings > m_ssl_settings; std::shared_ptr< asio::ssl::context > m_ssl_context; std::shared_ptr< asio::ip::tcp::acceptor > m_ssl_acceptor; #endif std::shared_ptr< asio::ip::tcp::acceptor > m_acceptor; std::map< std::string, std::string > m_resource_paths; std::map< std::string, std::shared_ptr< const Resource > > m_resource_routes; std::function< void ( void ) > m_ready_handler; std::map< int, std::function< void ( const int ) > > m_signal_handlers; std::function< void ( const std::shared_ptr< Session > ) > m_not_found_handler; std::function< void ( const std::shared_ptr< Session > ) > m_method_not_allowed_handler; std::function< void ( const std::shared_ptr< Session > ) > m_method_not_implemented_handler; std::function< void ( const std::shared_ptr< Session > ) > m_failed_filter_validation_handler; std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler; std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) > m_authentication_handler; protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors ServiceImpl( const ServiceImpl& original ) = delete; //Functionality //Getters //Setters //Operators ServiceImpl& operator =( const ServiceImpl& value ) = delete; //Properties }; } } #endif /* _RESTBED_DETAIL_SERVICE_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/session_impl.cpp000066400000000000000000000113011270400672200244350ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/http.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/resource.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/context_value.hpp" #include "corvusoft/restbed/session_manager.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" #include "corvusoft/restbed/detail/session_impl.hpp" #include "corvusoft/restbed/detail/resource_impl.hpp" //External Includes //System Namespaces using std::map; using std::set; using std::regex; using std::smatch; using std::string; using std::getline; using std::istream; using std::function; using std::multimap; using std::make_pair; using std::exception; using std::to_string; using std::ssub_match; using std::shared_ptr; using std::error_code; using std::make_shared; using std::regex_match; using std::regex_error; using std::runtime_error; using std::placeholders::_1; using std::rethrow_exception; using std::current_exception; using std::chrono::milliseconds; //Project Namespaces using restbed::detail::SessionImpl; //External Namespaces using asio::buffer; using asio::streambuf; namespace restbed { namespace detail { SessionImpl::SessionImpl( void ) : m_id( String::empty ), m_request( nullptr ), m_resource( nullptr ), m_settings( nullptr ), m_manager( nullptr ), m_headers( ), m_context( ), m_error_handler( nullptr ), m_keep_alive_callback( nullptr ) { return; } SessionImpl::~SessionImpl( void ) { return; } void SessionImpl::fetch_body( const size_t length, const shared_ptr< Session > session, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback ) const { const auto data_ptr = asio::buffer_cast< const Byte* >( session->m_pimpl->m_request->m_pimpl->m_buffer->data( ) ); const auto data = Bytes( data_ptr, data_ptr + length ); session->m_pimpl->m_request->m_pimpl->m_buffer->consume( length ); auto& body = m_request->m_pimpl->m_body; if ( body.empty( ) ) { body = data; } else { body.insert( body.end( ), data.begin( ), data.end( ) ); } callback( session, data ); } void SessionImpl::transmit( const Response& response, const function< void ( const error_code&, size_t ) >& callback ) const { auto hdrs = m_settings->get_default_headers( ); if ( m_resource not_eq nullptr ) { const auto m_resource_headers = m_resource->m_pimpl->m_default_headers; hdrs.insert( m_resource_headers.begin( ), m_resource_headers.end( ) ); } hdrs.insert( m_headers.begin( ), m_headers.end( ) ); auto response_headers = response.get_headers( ); hdrs.insert( response_headers.begin( ), response_headers.end( ) ); auto payload = make_shared< Response >( ); payload->set_headers( hdrs ); payload->set_body( response.get_body( ) ); payload->set_version( response.get_version( ) ); payload->set_protocol( response.get_protocol( ) ); payload->set_status_code( response.get_status_code( ) ); payload->set_status_message( response.get_status_message( ) ); if ( payload->get_status_message( ).empty( ) ) { payload->set_status_message( m_settings->get_status_message( payload->get_status_code( ) ) ); } m_request->m_pimpl->m_socket->write( Http::to_bytes( payload ), callback ); } const function< void ( const int, const exception&, const shared_ptr< Session > ) > SessionImpl::get_error_handler( void ) const { auto error_handler = ( m_resource not_eq nullptr and m_resource->m_pimpl->m_error_handler not_eq nullptr ) ? m_resource->m_pimpl->m_error_handler : m_error_handler; if ( error_handler == nullptr ) { return [ ]( const int, const exception&, const shared_ptr< Session > ) { return; }; } return error_handler; } } } restbed-4.0/source/corvusoft/restbed/detail/session_impl.hpp000066400000000000000000000056161270400672200244560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_SESSION_IMPL_H #define _RESTBED_DETAIL_SESSION_IMPL_H 1 //System Includes #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/byte.hpp" //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Session; class Request; class Response; class Resource; class Settings; class SessionManager; namespace detail { //Forward Declarations class SessionImpl { public: //Friends //Definitions //Constructors SessionImpl( void ); SessionImpl( const SessionImpl& original ) = delete; virtual ~SessionImpl( void ); //Functionality void fetch_body( const std::size_t length, const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback ) const; void transmit( const Response& response, const std::function< void ( const std::error_code&, std::size_t ) >& callback ) const; //Getters const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > get_error_handler( void ) const; //Setters //Operators SessionImpl& operator =( const SessionImpl& value ) = delete; //Properties std::string m_id; std::shared_ptr< const Request > m_request; std::shared_ptr< const Resource > m_resource; std::shared_ptr< const Settings > m_settings; std::shared_ptr< SessionManager > m_manager; std::multimap< std::string, std::string > m_headers; std::map< std::string, const ContextValue > m_context; std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) > m_error_handler; std::function< void ( const std::error_code& error, std::size_t length, const std::shared_ptr< Session > ) > m_keep_alive_callback; protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties }; } } #endif /* _RESTBED_DETAIL_SESSION_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/settings_impl.hpp000066400000000000000000000076171270400672200246360ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_SETTINGS_IMPL_H #define _RESTBED_DETAIL_SETTINGS_IMPL_H 1 //System Includes #include #include #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class SSLSettings; namespace detail { //Forward Declarations struct SettingsImpl { uint16_t m_port = 80; std::string m_root = "/"; unsigned int m_worker_limit = 0; unsigned int m_connection_limit = 128; std::string m_bind_address = ""; bool m_case_insensitive_uris = true; std::map< std::string, std::string > m_properties { }; std::shared_ptr< const SSLSettings > m_ssl_settings = nullptr; std::multimap< std::string, std::string > m_default_headers { }; std::chrono::milliseconds m_connection_timeout = std::chrono::milliseconds( 5000 ); std::map< int, std::string > m_status_messages { { 100, "Continue" }, { 101, "Switching Protocols" }, { 102, "Processing" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, { 203, "Non-Authoritative Information" }, { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 207, "Multi-Status" }, { 208, "Already Reported" }, { 226, "IM Used" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Found" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 306, "Reserved" }, { 307, "Temporary Redirect" }, { 308, "Permanent Redirect" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Timeout" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request URI Too Long" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 422, "Unprocessable Entity" }, { 423, "Locked" }, { 424, "Failed Dependency" }, { 426, "Upgrade Required" }, { 428, "Precondition Required" }, { 429, "Too Many Requests" }, { 431, "Request Header Fields Too Large" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Timeout" }, { 505, "HTTP Version Not Supported" }, { 506, "Variant Also Negotiates" }, { 507, "Insufficient Storage" }, { 508, "Loop Detected" }, { 510, "Not Extended" }, { 511, "Network Authentication Required" } }; }; } } #endif /* _RESTBED_DETAIL_SETTINGS_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/socket_impl.cpp000066400000000000000000000326031270400672200242520ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include "corvusoft/restbed/logger.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" //External Includes #include #include #include #include #include //System Namespaces using std::bind; using std::size_t; using std::string; using std::function; using std::to_string; using std::error_code; using std::shared_ptr; using std::make_shared; using std::placeholders::_1; using std::chrono::milliseconds; using std::chrono::steady_clock; //Project Namespaces using restbed::detail::SocketImpl; //External Namespaces using asio::ip::tcp; using asio::io_service; using asio::steady_timer; #ifdef BUILD_SSL using asio::ssl::stream; #endif namespace restbed { namespace detail { SocketImpl::SocketImpl( const shared_ptr< tcp::socket >& socket, const shared_ptr< Logger >& logger ) : m_is_open( socket->is_open( ) ), m_buffer( nullptr ), m_logger( logger ), m_timeout( 0 ), m_timer( make_shared< asio::steady_timer >( socket->get_io_service( ) ) ), m_resolver( nullptr ), m_socket( socket ) #ifdef BUILD_SSL , m_ssl_socket( nullptr ) #endif { return; } #ifdef BUILD_SSL SocketImpl::SocketImpl( const shared_ptr< asio::ssl::stream< tcp::socket > >& socket, const shared_ptr< Logger >& logger ) : m_is_open( socket->lowest_layer( ).is_open( ) ), m_buffer( nullptr ), m_logger( logger ), m_timeout( 0 ), m_timer( make_shared< asio::steady_timer >( socket->lowest_layer( ).get_io_service( ) ) ), m_resolver( nullptr ), m_socket( nullptr ), m_ssl_socket( socket ) { return; } #endif SocketImpl::~SocketImpl( void ) { return; } void SocketImpl::close( void ) { m_is_open = false; if ( m_timer not_eq nullptr ) { m_timer->cancel( ); } if ( m_socket not_eq nullptr ) { m_socket->close( ); } #ifdef BUILD_SSL if ( m_ssl_socket not_eq nullptr ) { m_ssl_socket->lowest_layer( ).close( ); } #endif } bool SocketImpl::is_open( void ) const { return m_is_open; } bool SocketImpl::is_closed( void ) const { return not m_is_open; } void SocketImpl::connect( const string& hostname, const uint16_t port, const function< void ( const error_code& ) >& callback ) { #ifdef BUILD_SSL auto& io_service = ( m_socket not_eq nullptr ) ? m_socket->get_io_service( ) : m_ssl_socket->lowest_layer( ).get_io_service( ); #else auto& io_service = m_socket->get_io_service( ); #endif m_resolver = make_shared< tcp::resolver >( io_service ); tcp::resolver::query query( hostname, ::to_string( port ) ); m_resolver->async_resolve( query, [ this, callback ]( const error_code & error, tcp::resolver::iterator endpoint_iterator ) { if ( not error ) { #ifdef BUILD_SSL auto& socket = ( m_socket not_eq nullptr ) ? *m_socket : m_ssl_socket->lowest_layer( ); #else auto& socket = *m_socket; #endif asio::async_connect( socket, endpoint_iterator, [ this, callback ]( const error_code & error, tcp::resolver::iterator ) { #ifdef BUILD_SSL if ( m_ssl_socket not_eq nullptr ) { m_ssl_socket->handshake( asio::ssl::stream_base::client ); } #endif m_is_open = true; callback( error ); } ); } } ); } void SocketImpl::sleep_for( const milliseconds& delay, const function< void ( const error_code& ) >& callback ) { m_timer->cancel( ); m_timer->expires_from_now( delay ); m_timer->async_wait( callback ); } void SocketImpl::write( const Bytes& data, const function< void ( const error_code&, size_t ) >& callback ) { m_buffer = make_shared< Bytes >( data ); m_timer->cancel( ); m_timer->expires_from_now( m_timeout ); m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, _1 ) ); #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif asio::async_write( *m_socket, asio::buffer( m_buffer->data( ), m_buffer->size( ) ), [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } m_buffer.reset( ); if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); #ifdef BUILD_SSL } else { asio::async_write( *m_ssl_socket, asio::buffer( m_buffer->data( ), m_buffer->size( ) ), [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } m_buffer.reset( ); if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); } #endif } size_t SocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, error_code& error ) { m_timer->cancel( ); m_timer->expires_from_now( m_timeout ); m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, _1 ) ); size_t size = 0; #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif size = asio::read( *m_socket, *data, asio::transfer_at_least( length ), error ); #ifdef BUILD_SSL } else { size = asio::read( *m_ssl_socket, *data, asio::transfer_at_least( length ), error ); } #endif m_timer->cancel( ); if ( error ) { m_is_open = false; } return size; } void SocketImpl::read( const shared_ptr< asio::streambuf >& data, const size_t length, const function< void ( const error_code&, size_t ) >& callback ) { m_timer->cancel( ); m_timer->expires_from_now( m_timeout ); m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, _1 ) ); #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif asio::async_read( *m_socket, *data, asio::transfer_at_least( length ), [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); #ifdef BUILD_SSL } else { asio::async_read( *m_ssl_socket, *data, asio::transfer_at_least( length ), [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); } #endif } size_t SocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, error_code& error ) { m_timer->cancel( ); m_timer->expires_from_now( m_timeout ); m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, _1 ) ); size_t length = 0; #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif length = asio::read_until( *m_socket, *data, delimiter, error ); #ifdef BUILD_SSL } else { length = asio::read_until( *m_ssl_socket, *data, delimiter, error ); } #endif m_timer->cancel( ); if ( error ) { m_is_open = false; } return length; } void SocketImpl::read( const shared_ptr< asio::streambuf >& data, const string& delimiter, const function< void ( const error_code&, size_t ) >& callback ) { m_timer->cancel( ); m_timer->expires_from_now( m_timeout ); m_timer->async_wait( bind( &SocketImpl::connection_timeout_handler, this, _1 ) ); #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif asio::async_read_until( *m_socket, *data, delimiter, [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); #ifdef BUILD_SSL } else { asio::async_read_until( *m_ssl_socket, *data, delimiter, [ this, callback ]( const error_code & error, size_t length ) { m_timer->cancel( ); if ( error ) { m_is_open = false; } if ( error not_eq asio::error::operation_aborted ) { callback( error, length ); } } ); } #endif } string SocketImpl::get_local_endpoint( void ) { error_code error; tcp::endpoint endpoint; #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif endpoint = m_socket->local_endpoint( error ); #ifdef BUILD_SSL } else { endpoint = m_ssl_socket->lowest_layer( ).local_endpoint( error ); } #endif if ( error ) { m_is_open = false; } auto address = endpoint.address( ); auto local = address.is_v4( ) ? address.to_string( ) : "[" + address.to_string( ) + "]:"; local += ::to_string( endpoint.port( ) ); return local; } string SocketImpl::get_remote_endpoint( void ) { error_code error; tcp::endpoint endpoint; #ifdef BUILD_SSL if ( m_socket not_eq nullptr ) { #endif endpoint = m_socket->remote_endpoint( error ); #ifdef BUILD_SSL } else { endpoint = m_ssl_socket->lowest_layer( ).remote_endpoint( error ); } #endif if ( error ) { m_is_open = false; } auto address = endpoint.address( ); auto remote = address.is_v4( ) ? address.to_string( ) : "[" + address.to_string( ) + "]:"; remote += ::to_string( endpoint.port( ) ); return remote; } void SocketImpl::set_timeout( const milliseconds& value ) { m_timeout = value; } void SocketImpl::connection_timeout_handler( const error_code& error ) { if ( error or m_timer->expires_at( ) > steady_clock::now( ) ) { return; } close( ); } } } restbed-4.0/source/corvusoft/restbed/detail/socket_impl.hpp000066400000000000000000000111031270400672200242470ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_SOCKET_IMPL_H #define _RESTBED_DETAIL_SOCKET_IMPL_H 1 //System Includes #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/byte.hpp" //External Includes #include #include #include #ifdef BUILD_SSL #include #endif //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Logger; namespace detail { //Forward Declarations class SocketImpl { public: //Friends //Definitions //Constructors SocketImpl( const std::shared_ptr< asio::ip::tcp::socket >& socket, const std::shared_ptr< Logger >& logger = nullptr ); #ifdef BUILD_SSL SocketImpl( const std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > >& socket, const std::shared_ptr< Logger >& logger = nullptr ); #endif virtual ~SocketImpl( void ); //Functionality void close( void ); bool is_open( void ) const; bool is_closed( void ) const; void connect( const std::string& hostname, const uint16_t port, const std::function< void ( const std::error_code& ) >& callback ); void sleep_for( const std::chrono::milliseconds& delay, const std::function< void ( const std::error_code& ) >& callback ); void write( const Bytes& data, const std::function< void ( const std::error_code&, std::size_t ) >& callback ); size_t read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, std::error_code& error ); void read( const std::shared_ptr< asio::streambuf >& data, const std::size_t length, const std::function< void ( const std::error_code&, std::size_t ) >& callback ); size_t read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, std::error_code& error ); void read( const std::shared_ptr< asio::streambuf >& data, const std::string& delimiter, const std::function< void ( const std::error_code&, std::size_t ) >& callback ); //Getters std::string get_local_endpoint( void ); std::string get_remote_endpoint( void ); //Setters void set_timeout( const std::chrono::milliseconds& value ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors SocketImpl( const SocketImpl& original ) = delete; //Functionality void connection_timeout_handler( const std::error_code& error ); //Getters //Setters //Operators SocketImpl& operator =( const SocketImpl& value ) = delete; //Properties bool m_is_open; std::shared_ptr< Bytes > m_buffer; std::shared_ptr< Logger > m_logger; std::chrono::milliseconds m_timeout; std::shared_ptr< asio::steady_timer > m_timer; std::shared_ptr< asio::ip::tcp::resolver > m_resolver; std::shared_ptr< asio::ip::tcp::socket > m_socket; #ifdef BUILD_SSL std::shared_ptr< asio::ssl::stream< asio::ip::tcp::socket > > m_ssl_socket; #endif }; } } #endif /* _RESTBED_DETAIL_SOCKET_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/ssl_settings_impl.hpp000066400000000000000000000031341270400672200255050ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_SSL_SETTINGS_IMPL_H #define _RESTBED_DETAIL_SSL_SETTINGS_IMPL_H 1 //System Includes #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations namespace detail { //Forward Declarations struct SSLSettingsImpl { uint16_t m_port = 443; bool m_http_disabled = false; bool m_sslv2_enabled = true; bool m_sslv3_enabled = true; bool m_tlsv1_enabled = true; bool m_tlsv11_enabled = true; bool m_tlsv12_enabled = true; bool m_compression_enabled = true; bool m_default_workarounds_enabled = true; bool m_single_diffie_hellman_use_enabled = true; std::string m_bind_address = ""; std::string m_passphrase = ""; std::string m_private_key = ""; std::string m_private_rsa_key = ""; std::string m_certificate = ""; std::string m_certificate_chain = ""; std::string m_certificate_authority_pool = ""; std::string m_temporary_diffie_hellman = ""; }; } } #endif /* _RESTBED_DETAIL_SSL_SETTINGS_IMPL_H */ restbed-4.0/source/corvusoft/restbed/detail/uri_impl.hpp000066400000000000000000000011101270400672200235530ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_DETAIL_URI_IMPL_H #define _RESTBED_DETAIL_URI_IMPL_H 1 //System Includes #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations namespace detail { //Forward Declarations struct UriImpl { std::string m_uri = ""; bool m_relative = false; }; } } #endif /* _RESTBED_DETAIL_URI_IMPL_H */ restbed-4.0/source/corvusoft/restbed/http.cpp000066400000000000000000000220001270400672200214440ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/http.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/detail/http_impl.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" #include "corvusoft/restbed/detail/response_impl.hpp" //External Includes #include #include //System Namespaces using std::bind; using std::string; using std::future; using std::function; using std::error_code; using std::shared_ptr; using std::make_shared; using std::runtime_error; using std::invalid_argument; using std::placeholders::_1; using std::placeholders::_2; //Project Namespaces using restbed::detail::HttpImpl; using restbed::detail::SocketImpl; using restbed::detail::RequestImpl; using restbed::detail::ResponseImpl; //External Namespaces using asio::buffer; namespace restbed { Bytes Http::to_bytes( const shared_ptr< Request >& request ) { return HttpImpl::to_bytes( request ); } Bytes Http::to_bytes( const shared_ptr< Response >& response ) { auto data = String::format( "%s/%.1f %i %s\r\n", response->get_protocol( ).data( ), response->get_version( ), response->get_status_code( ), response->get_status_message( ).data( ) ); auto headers = response->get_headers( ); if ( not headers.empty( ) ) { data += String::join( headers, ": ", "\r\n" ) + "\r\n"; } data += "\r\n"; auto bytes = String::to_bytes( data ); auto body = response->get_body( ); if ( not body.empty( ) ) { bytes.insert( bytes.end( ), body.begin( ), body.end( ) ); } return bytes; } void Http::close( const shared_ptr< Request >& request ) { if ( request not_eq nullptr and request->m_pimpl->m_socket not_eq nullptr ) { request->m_pimpl->m_socket->close( ); } } void Http::close( const shared_ptr< Response >& response ) { if ( response not_eq nullptr ) { close( response->m_pimpl->m_request.lock( ) ); } } bool Http::is_open( const shared_ptr< Request >& request ) { if ( request not_eq nullptr and request->m_pimpl->m_socket not_eq nullptr ) { return request->m_pimpl->m_socket->is_open( ); } return false; } bool Http::is_open( const shared_ptr< Response >& response ) { if ( response not_eq nullptr ) { return is_open( response->m_pimpl->m_request.lock( ) ); } return false; } bool Http::is_closed( const shared_ptr< Request >& request ) { return not is_open( request ); } bool Http::is_closed( const shared_ptr< Response >& response ) { return not is_open( response ); } const shared_ptr< Response > Http::sync( const shared_ptr< Request >& request, const shared_ptr< const Settings >& settings ) { static const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) > ignored = nullptr; Http::async( request, ignored, settings ); return request->m_pimpl->m_response; } future< shared_ptr< Response > > Http::async( const shared_ptr< Request >& request, const function< void ( const shared_ptr< Request >, const shared_ptr< Response > ) >& callback, const shared_ptr< const Settings >& settings ) { #ifndef BUILD_SSL if ( settings->get_ssl_settings( ) not_eq nullptr ) { throw runtime_error( "Not Implemented! Rebuild Restbed with SSL funcationality enabled." ); } #endif bool finished = true; auto completion_handler = callback; if ( completion_handler == nullptr ) { finished = false; completion_handler = [ &finished ]( const shared_ptr< Request >, const shared_ptr< Response > ) { finished = true; }; } HttpImpl::socket_setup( request, settings ); auto response = make_shared< Response >( ); response->m_pimpl->m_request = request; request->m_pimpl->m_response = response; if ( request->m_pimpl->m_socket not_eq nullptr and request->m_pimpl->m_socket->is_closed( ) ) { request->m_pimpl->m_socket->connect( request->get_host( ), request->get_port( ), bind( HttpImpl::request_handler, _1, request, completion_handler ) ); } else { request->m_pimpl->m_socket->write( HttpImpl::to_bytes( request ), bind( HttpImpl::write_handler, _1, _2, request, completion_handler ) ); } if ( finished ) { return std::async( std::launch::async, [ ]( const shared_ptr< Request > request ) -> shared_ptr< Response > { request->m_pimpl->m_io_service->run( ); return request->m_pimpl->m_response; }, request ); } else { do { request->m_pimpl->m_io_service->run_one( ); } while ( finished == false and not request->m_pimpl->m_io_service->stopped( ) ); std::promise< shared_ptr< Response > > result; result.set_value( request->m_pimpl->m_response ); return result.get_future( ); } } Bytes Http::fetch( const size_t length, const shared_ptr< Response >& response ) { if ( response == nullptr ) { throw invalid_argument( String::empty ); } auto request = response->get_request( ); if ( request == nullptr or request->m_pimpl->m_buffer == nullptr or request->m_pimpl->m_socket == nullptr ) { throw invalid_argument( String::empty ); } Bytes data = { }; if ( length > request->m_pimpl->m_buffer->size( ) ) { error_code error; const size_t size = length - request->m_pimpl->m_buffer->size( ); request->m_pimpl->m_socket->read( request->m_pimpl->m_buffer, size, error ); if ( error and error not_eq asio::error::eof ) { throw runtime_error( String::format( "Socket receive failed: '%s'", error.message( ).data( ) ) ); } const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) ); data = Bytes( data_ptr, data_ptr + length ); request->m_pimpl->m_buffer->consume( length ); } else { const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) ); data = Bytes( data_ptr, data_ptr + length ); request->m_pimpl->m_buffer->consume( length ); } auto& body = response->m_pimpl->m_body; if ( body.empty( ) ) { body = data; } else { body.insert( body.end( ), data.begin( ), data.end( ) ); } return data; } Bytes Http::fetch( const string& delimiter, const shared_ptr< Response >& response ) { if ( response == nullptr ) { throw invalid_argument( String::empty ); } auto request = response->get_request( ); if ( request == nullptr or request->m_pimpl->m_buffer == nullptr or request->m_pimpl->m_socket == nullptr ) { throw invalid_argument( String::empty ); } error_code error; const size_t size = request->m_pimpl->m_socket->read( request->m_pimpl->m_buffer, delimiter, error ); if ( error ) { throw runtime_error( String::format( "Socket receive failed: '%s'", error.message( ).data( ) ) ); } const auto data_ptr = asio::buffer_cast< const Byte* >( request->m_pimpl->m_buffer->data( ) ); const Bytes data( data_ptr, data_ptr + size ); request->m_pimpl->m_buffer->consume( size ); auto& body = response->m_pimpl->m_body; if ( body.empty( ) ) { body = data; } else { body.insert( body.end( ), data.begin( ), data.end( ) ); } return data; } } restbed-4.0/source/corvusoft/restbed/http.hpp000066400000000000000000000061701270400672200214630ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_HTTP_H #define _RESTBED_HTTP_H 1 //System Includes #include #include #include #include #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Request; class Response; class Settings; class Http { public: //Friends //Definitions //Constructors //Functionality static Bytes to_bytes( const std::shared_ptr< Request >& value ); static Bytes to_bytes( const std::shared_ptr< Response >& value ); static void close( const std::shared_ptr< Request >& request ); static void close( const std::shared_ptr< Response >& response ); static bool is_open( const std::shared_ptr< Request >& request ); static bool is_open( const std::shared_ptr< Response >& response ); static bool is_closed( const std::shared_ptr< Request >& request ); static bool is_closed( const std::shared_ptr< Response >& response ); static const std::shared_ptr< Response > sync( const std::shared_ptr< Request >& request, const std::shared_ptr< const Settings >& settings = std::make_shared< Settings >( ) ); static std::future< std::shared_ptr< Response > > async( const std::shared_ptr< Request >& request, const std::function< void ( const std::shared_ptr< Request >, const std::shared_ptr< Response > ) >& callback, const std::shared_ptr< const Settings >& settings = std::make_shared< Settings >( ) ); static Bytes fetch( const std::size_t length, const std::shared_ptr< Response >& response ); static Bytes fetch( const std::string& delimiter, const std::shared_ptr< Response >& response ); //Getters //Setters //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors Http( void ) = delete; virtual ~Http( void ) = delete; Http( const Http& original ) = delete; //Functionality //Getters //Setters //Operators Http& operator =( const Http& value ) = delete; //Properties }; } #endif /* _RESTBED_HTTP_H */ restbed-4.0/source/corvusoft/restbed/logger.hpp000066400000000000000000000042761270400672200217700ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_LOGGER_H #define _RESTBED_LOGGER_H 1 #if defined(WIN32) #undef ERROR #endif //System Includes #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Settings; class Logger { public: //Friends //Definitions enum Level : int { INFO = 0000, DEBUG = 1000, FATAL = 2000, ERROR = 3000, WARNING = 4000, SECURITY = 5000 }; //Constructors //Functionality virtual void stop( void ) = 0; virtual void start( const std::shared_ptr< const Settings >& settings ) = 0; virtual void log( const Level level, const char* format, ... ) = 0; virtual void log_if( bool expression, const Level level, const char* format, ... ) = 0; //Getters //Setters //Operators //Properties protected: //Friends //Definitions //Constructors Logger( void ) = default; explicit Logger( const Logger& original ) = default; virtual ~Logger( void ) = default; //Functionality //Getters //Setters //Operators Logger& operator =( const Logger& value ) = default; //Properties private: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties }; } #endif /* _RESTBED_LOGGER_H */ restbed-4.0/source/corvusoft/restbed/request.cpp000066400000000000000000000464251270400672200221760ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" #include "corvusoft/restbed/detail/response_impl.hpp" //External Includes //System Namespaces using std::map; using std::pair; using std::string; using std::function; using std::multimap; using std::shared_ptr; using std::make_shared; using std::invalid_argument; //Project Namespaces using restbed::detail::RequestImpl; //External Namespaces namespace restbed { Request::Request( void ) : m_pimpl( new detail::RequestImpl ) { return; } Request::Request( const Uri& uri ) : m_pimpl( new detail::RequestImpl ) { m_pimpl->m_uri = make_shared< Uri >( uri ); m_pimpl->m_path = uri.get_path( ); m_pimpl->m_port = uri.get_port( ); m_pimpl->m_host = uri.get_authority( ); m_pimpl->m_query_parameters = uri.get_query_parameters( ); m_pimpl->m_protocol = String::uppercase( uri.get_scheme( ) ); if ( m_pimpl->m_path.empty( ) ) { m_pimpl->m_path = "/"; } if ( m_pimpl->m_port == 0 ) { m_pimpl->m_port = ( m_pimpl->m_protocol == "HTTPS" ) ? 443 : 80; } } Request::~Request( void ) { delete m_pimpl; } bool Request::has_header( const string& name ) const { const auto key = String::lowercase( name ); auto iterator = find_if( m_pimpl->m_headers.begin( ), m_pimpl->m_headers.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return iterator not_eq m_pimpl->m_headers.end( ); } bool Request::has_path_parameter( const string& name, const String::Option option ) const { if ( option == String::Option::CASE_SENSITIVE ) { return m_pimpl->m_path_parameters.find( name ) not_eq m_pimpl->m_path_parameters.end( ); } const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_path_parameters.begin( ), m_pimpl->m_path_parameters.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return iterator not_eq m_pimpl->m_path_parameters.end( ); } bool Request::has_query_parameter( const string& name, const String::Option option ) const { if ( option == String::Option::CASE_SENSITIVE ) { return m_pimpl->m_query_parameters.find( name ) not_eq m_pimpl->m_query_parameters.end( ); } const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_query_parameters.begin( ), m_pimpl->m_query_parameters.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return iterator not_eq m_pimpl->m_query_parameters.end( ); } uint16_t Request::get_port( void ) const { return m_pimpl->m_port; } double Request::get_version( void ) const { return m_pimpl->m_version; } const Bytes& Request::get_body( void ) const { return m_pimpl->m_body; } const shared_ptr< const Response > Request::get_response( void ) const { return m_pimpl->m_response; } string Request::get_host( const function< string ( const string& ) >& transform ) const { return ( transform == nullptr ) ? m_pimpl->m_host : transform( m_pimpl->m_host ); } string Request::get_path( const function< string ( const string& ) >& transform ) const { return ( transform == nullptr ) ? m_pimpl->m_path : transform( m_pimpl->m_path ); } string Request::get_method( const function< string ( const string& ) >& transform ) const { return ( transform == nullptr ) ? m_pimpl->m_method : transform( m_pimpl->m_method ); } string Request::get_protocol( const function< string ( const string& ) >& transform ) const { return ( transform == nullptr ) ? m_pimpl->m_protocol : transform( m_pimpl->m_protocol ); } void Request::get_body( string& body, const function< string ( const Bytes& ) >& transform ) const { body = ( transform == nullptr ) ? string( m_pimpl->m_body.begin( ), m_pimpl->m_body.end( ) ) : transform( m_pimpl->m_body ); } void Request::get_header( const string& name, int& value, const int default_value ) const { try { value = stoi( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, long& value, const long default_value ) const { try { value = stol( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, float& value, const float default_value ) const { try { value = stof( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, double& value, const double default_value ) const { try { value = stod( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, long long& value, const long long default_value ) const { try { value = stoll( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, unsigned int& value, const unsigned int default_value ) const { try { value = stoul( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, unsigned long& value, const unsigned long default_value ) const { try { value = stoul( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_header( const string& name, unsigned long long& value, const unsigned long long default_value ) const { try { value = stoull( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } multimap< string, string > Request::get_headers( const string& name ) const { if ( name.empty( ) ) { return m_pimpl->m_headers; } decltype( m_pimpl->m_headers ) headers; const auto key = String::lowercase( name ); for ( const auto& header : m_pimpl->m_headers ) { if ( key == String::lowercase( header.first ) ) { headers.insert( header ); } } return headers; } string Request::get_header( const string& name, const string& default_value ) const { const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_headers.begin( ), m_pimpl->m_headers.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return ( iterator == m_pimpl->m_headers.end( ) ) ? default_value : iterator->second; } string Request::get_header( const string& name, const function< string ( const string& ) >& transform ) const { const string header = get_header( name ); return ( transform == nullptr ) ? header : transform( header ); } void Request::get_query_parameter( const string& name, int& value, const int default_value ) const { try { value = stoi( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, long& value, const long default_value ) const { try { value = stol( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, float& value, const float default_value ) const { try { value = stof( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, double& value, const double default_value ) const { try { value = stod( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, long long& value, const long long default_value ) const { try { value = stoll( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, unsigned int& value, const unsigned int default_value ) const { try { value = stoul( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, unsigned long& value, const unsigned long default_value ) const { try { value = stoul( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_query_parameter( const string& name, unsigned long long& value, const unsigned long long default_value ) const { try { value = stoull( get_query_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } string Request::get_query_parameter( const string& name, const String::Option option ) const { return get_query_parameter( name, string( "" ), option ); } string Request::get_query_parameter( const string& name, const string& default_value, const String::Option option ) const { if ( option == String::Option::CASE_SENSITIVE ) { const auto iterator = m_pimpl->m_query_parameters.find( name ); return ( iterator == m_pimpl->m_query_parameters.end( ) ) ? default_value : iterator->second; } const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_query_parameters.begin( ), m_pimpl->m_query_parameters.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return ( iterator == m_pimpl->m_query_parameters.end( ) ) ? default_value : iterator->second; } string Request::get_query_parameter( const string& name, const function< string ( const string& ) >& transform, const String::Option option ) const { const auto parameter = get_query_parameter( name, string( "" ), option ); return ( transform == nullptr ) ? parameter : transform( parameter ); } multimap< string, string > Request::get_query_parameters( const string& name, const String::Option option ) const { if ( name.empty( ) ) { return m_pimpl->m_query_parameters; } if ( option == String::Option::CASE_SENSITIVE ) { const auto iterators = m_pimpl->m_query_parameters.equal_range( name ); return decltype( m_pimpl->m_query_parameters )( iterators.first, iterators.second ); } const auto key = String::lowercase( name ); decltype( m_pimpl->m_query_parameters ) parameters; for ( const auto& parameter : m_pimpl->m_query_parameters ) { if ( key == String::lowercase( parameter.first ) ) { parameters.insert( parameter ); } } return parameters; } void Request::get_path_parameter( const string& name, int& value, const int default_value ) const { try { value = stoi( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, long& value, const long default_value ) const { try { value = stol( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, float& value, const float default_value ) const { try { value = stof( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, double& value, const double default_value ) const { try { value = stod( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, long long& value, const long long default_value ) const { try { value = stoll( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, unsigned int& value, const unsigned int default_value ) const { try { value = stoul( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, unsigned long& value, const unsigned long default_value ) const { try { value = stoul( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Request::get_path_parameter( const string& name, unsigned long long& value, const unsigned long long default_value ) const { try { value = stoull( get_path_parameter( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } string Request::get_path_parameter( const string& name, const String::Option option ) const { return get_path_parameter( name, string( "" ), option ); } string Request::get_path_parameter( const string& name, const string& default_value, const String::Option option ) const { if ( option == String::Option::CASE_SENSITIVE ) { const auto iterator = m_pimpl->m_path_parameters.find( name ); return ( iterator == m_pimpl->m_path_parameters.end( ) ) ? default_value : iterator->second; } const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_path_parameters.begin( ), m_pimpl->m_path_parameters.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return ( iterator == m_pimpl->m_path_parameters.end( ) ) ? default_value : iterator->second; } string Request::get_path_parameter( const string& name, const function< string ( const string& ) >& transform, const String::Option option ) const { const auto parameter = get_path_parameter( name, string( "" ), option ); return ( transform == nullptr ) ? parameter : transform( parameter ); } map< string, string > Request::get_path_parameters( const string& name, const String::Option option ) const { if ( name.empty( ) ) { return m_pimpl->m_path_parameters; } if ( option == String::Option::CASE_SENSITIVE ) { const auto iterators = m_pimpl->m_path_parameters.equal_range( name ); return decltype( m_pimpl->m_path_parameters )( iterators.first, iterators.second ); } const auto key = String::lowercase( name ); decltype( m_pimpl->m_path_parameters ) parameters; for ( const auto& parameter : m_pimpl->m_path_parameters ) { if ( key == String::lowercase( parameter.first ) ) { parameters.insert( parameter ); } } return parameters; } void Request::set_body( const Bytes& value ) { m_pimpl->m_body = value; } void Request::set_body( const string& value ) { m_pimpl->m_body = String::to_bytes( value ); } void Request::set_port( const uint16_t value ) { if ( m_pimpl->m_socket not_eq nullptr ) { m_pimpl->m_socket->close( ); } m_pimpl->m_port = value; } void Request::set_version( const double value ) { m_pimpl->m_version = value; } void Request::set_path( const string& value ) { m_pimpl->m_path = value; } void Request::set_host( const string& value ) { if ( m_pimpl->m_socket not_eq nullptr ) { m_pimpl->m_socket->close( ); } m_pimpl->m_host = value; } void Request::set_method( const string& value ) { m_pimpl->m_method = value; } void Request::set_protocol( const string& value ) { m_pimpl->m_protocol = value; } void Request::set_header( const string& name, const string& value ) { m_pimpl->m_headers.insert( make_pair( name, value ) ); } void Request::set_headers( const multimap< string, string >& values ) { m_pimpl->m_headers = values; } void Request::set_query_parameter( const string& name, const string& value ) { m_pimpl->m_query_parameters.insert( make_pair( name, value ) ); } void Request::set_query_parameters( const multimap< string, string >& values ) { m_pimpl->m_query_parameters = values; } } restbed-4.0/source/corvusoft/restbed/request.hpp000066400000000000000000000223111270400672200221670ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_REQUEST_H #define _RESTBED_REQUEST_H 1 //System Includes #include #include #include #include #include //Project Includes #include #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Uri; class Http; class Session; class Response; namespace detail { class HttpImpl; class SessionImpl; class ServiceImpl; struct RequestImpl; } class Request { public: //Friends //Definitions //Constructors Request( void ); Request( const Uri& uri ); virtual ~Request( void ); //Functionality bool has_header( const std::string& name ) const; bool has_path_parameter( const std::string& name, const String::Option option = String::CASE_INSENSITIVE ) const; bool has_query_parameter( const std::string& name, const String::Option option = String::CASE_INSENSITIVE ) const; //Getters uint16_t get_port( void ) const; double get_version( void ) const; const Bytes& get_body( void ) const; const std::shared_ptr< const Response > get_response( void ) const; std::string get_host( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const; std::string get_path( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const; std::string get_method( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const; std::string get_protocol( const std::function< std::string ( const std::string& ) >& transform = nullptr ) const; void get_body( std::string& body, const std::function< std::string ( const Bytes& ) >& transform = nullptr ) const; void get_header( const std::string& name, int& value, const int default_value = 0 ) const; void get_header( const std::string& name, long& value, const long default_value = 0 ) const; void get_header( const std::string& name, float& value, const float default_value = 0 ) const; void get_header( const std::string& name, double& value, const double default_value = 0 ) const; void get_header( const std::string& name, long long& value, const long long default_value = 0 ) const; void get_header( const std::string& name, unsigned int& value, const unsigned int default_value = 0 ) const; void get_header( const std::string& name, unsigned long& value, const unsigned long default_value = 0 ) const; void get_header( const std::string& name, unsigned long long& value, const unsigned long long default_value = 0 ) const; std::multimap< std::string, std::string > get_headers( const std::string& name = "" ) const; std::string get_header( const std::string& name, const std::string& default_value = "" ) const; std::string get_header( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const; void get_query_parameter( const std::string& name, int& value, const int default_value = 0 ) const; void get_query_parameter( const std::string& name, long& value, const long default_value = 0 ) const; void get_query_parameter( const std::string& name, float& value, const float default_value = 0 ) const; void get_query_parameter( const std::string& name, double& value, const double default_value = 0 ) const; void get_query_parameter( const std::string& name, long long& value, const long long default_value = 0 ) const; void get_query_parameter( const std::string& name, unsigned int& value, const unsigned int default_value = 0 ) const; void get_query_parameter( const std::string& name, unsigned long& value, const unsigned long default_value = 0 ) const; void get_query_parameter( const std::string& name, unsigned long long& value, const unsigned long long default_value = 0 ) const; std::string get_query_parameter( const std::string& name, const String::Option option = String::CASE_INSENSITIVE ) const; std::string get_query_parameter( const std::string& name, const std::string& default_value, const String::Option option = String::CASE_INSENSITIVE ) const; std::string get_query_parameter( const std::string& name, const std::function< std::string ( const std::string& ) >& transform, const String::Option option = String::CASE_INSENSITIVE ) const; std::multimap< std::string, std::string > get_query_parameters( const std::string& name = "", const String::Option option = String::CASE_INSENSITIVE ) const; void get_path_parameter( const std::string& name, int& value, const int default_value = 0 ) const; void get_path_parameter( const std::string& name, long& value, const long default_value = 0 ) const; void get_path_parameter( const std::string& name, float& value, const float default_value = 0 ) const; void get_path_parameter( const std::string& name, double& value, const double default_value = 0 ) const; void get_path_parameter( const std::string& name, long long& value, const long long default_value = 0 ) const; void get_path_parameter( const std::string& name, unsigned int& value, const unsigned int default_value = 0 ) const; void get_path_parameter( const std::string& name, unsigned long& value, const unsigned long default_value = 0 ) const; void get_path_parameter( const std::string& name, unsigned long long& value, const unsigned long long default_value = 0 ) const; std::string get_path_parameter( const std::string& name, const String::Option option = String::CASE_INSENSITIVE ) const; std::string get_path_parameter( const std::string& name, const std::string& default_value, const String::Option option = String::CASE_INSENSITIVE ) const; std::string get_path_parameter( const std::string& name, const std::function< std::string ( const std::string& ) >& transform, const String::Option option = String::CASE_INSENSITIVE ) const; std::map< std::string, std::string > get_path_parameters( const std::string& name = "", const String::Option option = String::CASE_INSENSITIVE ) const; //Setters void set_body( const Bytes& value ); void set_body( const std::string& value ); void set_port( const uint16_t value ); void set_version( const double value ); void set_path( const std::string& value ); void set_host( const std::string& value ); void set_method( const std::string& value ); void set_protocol( const std::string& value ); void set_header( const std::string& name, const std::string& value ); void set_headers( const std::multimap< std::string, std::string >& values ); void set_query_parameter( const std::string& name, const std::string& value ); void set_query_parameters( const std::multimap< std::string, std::string >& values ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends friend Http; friend Session; friend detail::HttpImpl; friend detail::SessionImpl; friend detail::ServiceImpl; //Definitions //Constructors Request( const Request& original ) = delete; //Functionality //Getters //Setters //Operators Request& operator =( const Request& value ) = delete; //Properties detail::RequestImpl* m_pimpl; }; } #endif /* _RESTBED_REQUEST_H */ restbed-4.0/source/corvusoft/restbed/resource.cpp000066400000000000000000000067341270400672200223340ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/rule.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/resource.hpp" #include "corvusoft/restbed/detail/resource_impl.hpp" //External Includes //System Namespaces using std::set; using std::string; using std::multimap; using std::function; using std::exception; using std::shared_ptr; using std::invalid_argument; //Project Namespaces using restbed::detail::ResourceImpl; //External Namespaces namespace restbed { Resource::Resource( void ) : m_pimpl( new ResourceImpl ) { return; } Resource::~Resource( void ) { delete m_pimpl; } void Resource::add_rule( const shared_ptr< Rule >& rule ) { if ( rule not_eq nullptr ) { m_pimpl->m_rules.push_back( rule ); stable_sort( m_pimpl->m_rules.begin( ), m_pimpl->m_rules.end( ), [ ]( const shared_ptr< const Rule >& lhs, const shared_ptr< const Rule >& rhs ) { return lhs->get_priority( ) < rhs->get_priority( ); } ); } } void Resource::add_rule( const shared_ptr< Rule >& rule, const int priority ) { if ( rule not_eq nullptr ) { rule->set_priority( priority ); add_rule( rule ); } } void Resource::set_path( const string& value ) { m_pimpl->m_paths = { value }; } void Resource::set_paths( const set< string >& values ) { m_pimpl->m_paths = values; } void Resource::set_default_header( const string& name, const string& value ) { m_pimpl->m_default_headers.insert( make_pair( name, value ) ); } void Resource::set_default_headers( const multimap< string, string >& values ) { m_pimpl->m_default_headers = values; } void Resource::set_failed_filter_validation_handler( const function< void ( const shared_ptr< Session > ) >& value ) { m_pimpl->m_failed_filter_validation_handler = value; } void Resource::set_error_handler( const function< void ( const int, const exception&, const shared_ptr< Session > ) >& value ) { m_pimpl->m_error_handler = value; } void Resource::set_authentication_handler( const function< void ( const shared_ptr< Session >, const function< void ( const shared_ptr< Session > ) >& ) >& value ) { m_pimpl->m_authentication_handler = value; } void Resource::set_method_handler( const string& method, const function< void ( const shared_ptr< Session > ) >& callback ) { static const multimap< string, string > empty { }; set_method_handler( method, empty, callback ); } void Resource::set_method_handler( const string& method, const multimap< string, string >& filters, const function< void ( const shared_ptr< Session > ) >& callback ) { if ( method.empty( ) ) { throw invalid_argument( "Attempt to set resource handler to an empty protocol method." ); } if ( callback not_eq nullptr ) { m_pimpl->m_methods.insert( method ); m_pimpl->m_method_handlers.insert( make_pair( method, make_pair( filters, callback ) ) ); } } } restbed-4.0/source/corvusoft/restbed/resource.hpp000066400000000000000000000063461270400672200223400ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_RESOURCE_H #define _RESTBED_RESOURCE_H 1 //System Includes #include #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Rule; class Session; class Service; namespace detail { class SessionImpl; class ServiceImpl; struct ResourceImpl; } class Resource { public: //Friends //Definitions //Constructors Resource( void ); virtual ~Resource( void ); //Functionality void add_rule( const std::shared_ptr< Rule >& rule ); void add_rule( const std::shared_ptr< Rule >& rule, const int priority ); //Getters //Setters void set_path( const std::string& value ); void set_paths( const std::set< std::string >& values ); void set_default_header( const std::string& name, const std::string& value ); void set_default_headers( const std::multimap< std::string, std::string >& values ); void set_failed_filter_validation_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value ); void set_error_handler( const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) >& value ); void set_authentication_handler( const std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) >& value ); void set_method_handler( const std::string& method, const std::function< void ( const std::shared_ptr< Session > ) >& callback ); void set_method_handler( const std::string& method, const std::multimap< std::string, std::string >& filters, const std::function< void ( const std::shared_ptr< Session > ) >& callback ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends friend Service; friend detail::ServiceImpl; friend detail::SessionImpl; //Definitions //Constructors Resource( const Resource& original ) = delete; //Functionality //Getters //Setters //Operators Resource& operator =( const Resource& value ) = delete; //Properties detail::ResourceImpl* m_pimpl; }; } #endif /* _RESTBED_RESOURCE_H */ restbed-4.0/source/corvusoft/restbed/response.cpp000066400000000000000000000152071270400672200223360ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/detail/response_impl.hpp" //External Includes //System Namespaces using std::map; using std::pair; using std::string; using std::function; using std::multimap; using std::shared_ptr; using std::invalid_argument; //Project Namespaces using restbed::Request; using restbed::detail::ResponseImpl; //External Namespaces namespace restbed { Response::Response( void ) : m_pimpl( new ResponseImpl ) { return; } Response::~Response( void ) { delete m_pimpl; } bool Response::has_header( const string& name ) const { const auto key = String::lowercase( name ); auto iterator = find_if( m_pimpl->m_headers.begin( ), m_pimpl->m_headers.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return iterator not_eq m_pimpl->m_headers.end( ); } Bytes Response::get_body( void ) const { return m_pimpl->m_body; } double Response::get_version( void ) const { return m_pimpl->m_version; } int Response::get_status_code( void ) const { return m_pimpl->m_status_code; } string Response::get_protocol( void ) const { return m_pimpl->m_protocol; } string Response::get_status_message( void ) const { return m_pimpl->m_status_message; } const shared_ptr< const Request > Response::get_request( void ) const { return m_pimpl->m_request.lock( ); } void Response::get_body( string& body, const function< string ( const Bytes& ) >& transform ) const { body = ( transform == nullptr ) ? string( m_pimpl->m_body.begin( ), m_pimpl->m_body.end( ) ) : transform( m_pimpl->m_body ); } void Response::get_header( const string& name, int& value, const int default_value ) const { try { value = stoi( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, long& value, const long default_value ) const { try { value = stol( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, float& value, const float default_value ) const { try { value = stof( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, double& value, const double default_value ) const { try { value = stod( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, long long& value, const long long default_value ) const { try { value = stoll( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, unsigned int& value, const unsigned int default_value ) const { try { value = stoul( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, unsigned long& value, const unsigned long default_value ) const { try { value = stoul( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } void Response::get_header( const string& name, unsigned long long& value, const unsigned long long default_value ) const { try { value = stoull( get_header( name ) ); } catch ( const invalid_argument& ) { value = default_value; } } multimap< string, string > Response::get_headers( const string& name ) const { if ( name.empty( ) ) { return m_pimpl->m_headers; } decltype( m_pimpl->m_headers ) headers; const auto key = String::lowercase( name ); for ( const auto& header : m_pimpl->m_headers ) { if ( key == String::lowercase( header.first ) ) { headers.insert( header ); } } return headers; } string Response::get_header( const string& name, const string& default_value ) const { const auto key = String::lowercase( name ); const auto iterator = find_if( m_pimpl->m_headers.begin( ), m_pimpl->m_headers.end( ), [ &key ]( const pair< string, string >& value ) { return ( key == String::lowercase( value.first ) ); } ); return ( iterator == m_pimpl->m_headers.end( ) ) ? default_value : iterator->second; } string Response::get_header( const string& name, const function< string ( const string& ) >& transform ) const { const string header = get_header( name ); return ( transform == nullptr ) ? header : transform( header ); } void Response::set_body( const Bytes& value ) { m_pimpl->m_body = value; } void Response::set_body( const string& value ) { m_pimpl->m_body = String::to_bytes( value ); } void Response::set_version( const double value ) { m_pimpl->m_version = value; } void Response::set_status_code( const int value ) { m_pimpl->m_status_code = value; } void Response::set_protocol( const string& value ) { m_pimpl->m_protocol = value; } void Response::set_status_message( const string& value ) { m_pimpl->m_status_message = value; } void Response::set_header( const string& name, const string& value ) { m_pimpl->m_headers.insert( make_pair( name, value ) ); } void Response::set_headers( const multimap< string, string >& values ) { m_pimpl->m_headers = values; } } restbed-4.0/source/corvusoft/restbed/response.hpp000066400000000000000000000105061270400672200223400ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_RESPONSE_H #define _RESTBED_RESPONSE_H 1 //System Includes #include #include #include #include #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Http; class Request; namespace detail { struct ResponseImpl; } class Response { public: //Friends //Definitions //Constructors Response( void ); virtual ~Response( void ); //Functionality bool has_header( const std::string& name ) const; //Getters Bytes get_body( void ) const; double get_version( void ) const; int get_status_code( void ) const; std::string get_protocol( void ) const; std::string get_status_message( void ) const; const std::shared_ptr< const Request > get_request( void ) const; void get_body( std::string& body, const std::function< std::string ( const Bytes& ) >& transform = nullptr ) const; void get_header( const std::string& name, int& value, const int default_value = 0 ) const; void get_header( const std::string& name, long& value, const long default_value = 0 ) const; void get_header( const std::string& name, float& value, const float default_value = 0 ) const; void get_header( const std::string& name, double& value, const double default_value = 0 ) const; void get_header( const std::string& name, long long& value, const long long default_value = 0 ) const; void get_header( const std::string& name, unsigned int& value, const unsigned int default_value = 0 ) const; void get_header( const std::string& name, unsigned long& value, const unsigned long default_value = 0 ) const; void get_header( const std::string& name, unsigned long long& value, const unsigned long long default_value = 0 ) const; std::multimap< std::string, std::string > get_headers( const std::string& name = "" ) const; std::string get_header( const std::string& name, const std::string& default_value = "" ) const; std::string get_header( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const; //Setters void set_body( const Bytes& value ); void set_body( const std::string& value ); void set_version( const double value ); void set_status_code( const int value ); void set_protocol( const std::string& protocol ); void set_status_message( const std::string& value ); void set_header( const std::string& name, const std::string& value ); void set_headers( const std::multimap< std::string, std::string >& values ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends friend Http; //Definitions //Constructors Response( const Response& original ) = delete; //Functionality //Getters //Setters //Operators Response& operator =( const Response& value ) = delete; //Properties detail::ResponseImpl* m_pimpl; }; } #endif /* _RESTBED_RESPONSE_H */ restbed-4.0/source/corvusoft/restbed/rule.cpp000066400000000000000000000014271270400672200214460ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes //Project Includes #include "corvusoft/restbed/rule.hpp" #include "corvusoft/restbed/detail/rule_impl.hpp" //External Includes //System Namespaces using std::shared_ptr; //Project Namespaces using restbed::detail::RuleImpl; //External Namespaces namespace restbed { bool Rule::condition( const shared_ptr< Session > ) { return true; } int Rule::get_priority( void ) const { return m_pimpl->m_priority; } void Rule::set_priority( const int value ) { m_pimpl->m_priority = value; } Rule::Rule( void ) : m_pimpl( new RuleImpl ) { return; } Rule::~Rule( void ) { delete m_pimpl; } } restbed-4.0/source/corvusoft/restbed/rule.hpp000066400000000000000000000036751270400672200214620ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_RULE_H #define _RESTBED_RULE_H 1 //System Includes #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Session; namespace detail { struct RuleImpl; } class Rule { public: //Friends //Definitions //Constructors //Functionality virtual bool condition( const std::shared_ptr< Session > session ); virtual void action( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback ) = 0; //Getters int get_priority( void ) const; //Setters void set_priority( const int value ); //Operators //Properties protected: //Friends //Definitions //Constructors Rule( void ); virtual ~Rule( void ); //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors explicit Rule( const Rule& ) = delete; //Functionality //Getters //Setters //Operators Rule& operator =( const Rule& ) = delete; //Properties detail::RuleImpl* m_pimpl; }; } #endif /* _RESTBED_RULE_H */ restbed-4.0/source/corvusoft/restbed/service.cpp000066400000000000000000000300261270400672200221340ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/rule.hpp" #include "corvusoft/restbed/logger.hpp" #include "corvusoft/restbed/service.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/resource.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/session_manager.hpp" #include "corvusoft/restbed/detail/service_impl.hpp" #include "corvusoft/restbed/detail/resource_impl.hpp" //External Includes #include #include #ifdef BUILD_SSL #include #endif //System Namespaces using std::map; using std::bind; using std::thread; using std::string; using std::vector; using std::function; using std::exception; using std::to_string; using std::shared_ptr; using std::error_code; using std::make_shared; using std::stable_sort; using std::runtime_error; using std::invalid_argument; using std::chrono::milliseconds; //Project Namespaces using restbed::detail::ServiceImpl; //External Namespaces using asio::io_service; using asio::steady_timer; namespace restbed { Service::Service( void ) : m_pimpl( new ServiceImpl ) { return; } Service::~Service( void ) { try { stop( ); } catch ( ... ) { m_pimpl->log( Logger::WARNING, "Service failed graceful wind down." ); } delete m_pimpl; } void Service::stop( void ) { m_pimpl->m_is_running = false; if ( m_pimpl->m_io_service not_eq nullptr ) { m_pimpl->m_io_service->stop( ); } if ( m_pimpl->m_session_manager not_eq nullptr ) { m_pimpl->m_session_manager->stop( ); } for ( auto& worker : m_pimpl->m_workers ) { worker->join( ); } m_pimpl->m_workers.clear( ); if ( m_pimpl->m_logger not_eq nullptr ) { m_pimpl->log( Logger::INFO, "Service halted." ); m_pimpl->m_logger->stop( ); } } void Service::start( const shared_ptr< const Settings >& settings ) { m_pimpl->setup_signal_handler( ); m_pimpl->m_settings = settings; if ( m_pimpl->m_settings == nullptr ) { m_pimpl->m_settings = make_shared< Settings >( ); } #ifdef BUILD_SSL m_pimpl->m_ssl_settings = m_pimpl->m_settings->get_ssl_settings( ); #else if ( m_pimpl->m_settings->get_ssl_settings( ) not_eq nullptr ) { throw runtime_error( "Not Implemented! Rebuild Restbed with SSL funcationality enabled." ); } #endif if ( m_pimpl->m_logger not_eq nullptr ) { m_pimpl->m_logger->start( m_pimpl->m_settings ); } if ( m_pimpl->m_session_manager == nullptr ) { m_pimpl->m_session_manager = make_shared< SessionManager >( ); } m_pimpl->m_session_manager->start( m_pimpl->m_settings ); stable_sort( m_pimpl->m_rules.begin( ), m_pimpl->m_rules.end( ), [ ]( const shared_ptr< const Rule >& lhs, const shared_ptr< const Rule >& rhs ) { return lhs->get_priority( ) < rhs->get_priority( ); } ); m_pimpl->http_start( ); #ifdef BUILD_SSL m_pimpl->https_start( ); #endif for ( const auto& route : m_pimpl->m_resource_paths ) { auto path = String::format( "/%s/%s", m_pimpl->m_settings->get_root( ).data( ), route.second.data( ) ); path = String::replace( "//", "/", path ); m_pimpl->log( Logger::INFO, String::format( "Resource published on route '%s'.", path.data( ) ) ); } if ( m_pimpl->m_ready_handler not_eq nullptr ) { m_pimpl->m_io_service->post( m_pimpl->m_ready_handler ); } m_pimpl->m_is_running = true; unsigned int limit = m_pimpl->m_settings->get_worker_limit( ); if ( limit > 0 ) { const auto this_thread = 1; limit = limit - this_thread; for ( unsigned int count = 0; count < limit; count++ ) { auto worker = make_shared< thread >( [ this ]( ) { m_pimpl->m_io_service->run( ); } ); m_pimpl->m_workers.push_back( worker ); } } m_pimpl->m_io_service->run( ); } void Service::restart( const shared_ptr< const Settings >& settings ) { try { stop( ); } catch ( ... ) { m_pimpl->log( Logger::WARNING, "Service failed graceful reboot." ); } start( settings ); } void Service::add_rule( const shared_ptr< Rule >& rule ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( rule not_eq nullptr ) { m_pimpl->m_rules.push_back( rule ); } } void Service::add_rule( const shared_ptr< Rule >& rule, const int priority ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( rule not_eq nullptr ) { rule->set_priority( priority ); m_pimpl->m_rules.push_back( rule ); } } void Service::publish( const shared_ptr< const Resource >& resource ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( resource == nullptr ) { return; } auto paths = resource->m_pimpl->m_paths; if ( not m_pimpl->has_unique_paths( paths ) ) { throw invalid_argument( "Resource would pollute namespace. Please ensure all published resources have unique paths." ); } for ( auto& path : paths ) { const string sanitised_path = m_pimpl->sanitise_path( path ); m_pimpl->m_resource_paths[ sanitised_path ] = path; m_pimpl->m_resource_routes[ sanitised_path ] = resource; } const auto& methods = resource->m_pimpl->m_methods; m_pimpl->m_supported_methods.insert( methods.begin( ), methods.end( ) ); } void Service::suppress( const shared_ptr< const Resource >& resource ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( resource == nullptr ) { return; } for ( const auto& path : resource->m_pimpl->m_paths ) { if ( m_pimpl->m_resource_routes.erase( path ) ) { m_pimpl->log( Logger::INFO, String::format( "Suppressed resource route '%s'.", path.data( ) ) ); } else { m_pimpl->log( Logger::WARNING, String::format( "Failed to suppress resource route '%s'; Not Found!", path.data( ) ) ); } } } void Service::schedule( const function< void ( void ) >& task, const milliseconds& interval ) { if ( task == nullptr ) { return; } if ( interval == milliseconds::zero( ) ) { m_pimpl->m_io_service->post( task ); return; } auto timer = make_shared< steady_timer >( *m_pimpl->m_io_service ); timer->expires_from_now( interval ); timer->async_wait( [ this, task, interval, timer ]( const error_code& ) { task( ); schedule( task, interval ); } ); } const shared_ptr< const Uri > Service::get_http_uri( void ) const { return m_pimpl->get_http_uri( ); } const shared_ptr< const Uri > Service::get_https_uri( void ) const { return m_pimpl->get_https_uri( ); } void Service::set_logger( const shared_ptr< Logger >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_logger = value; } void Service::set_session_manager( const shared_ptr< SessionManager >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_session_manager = value; } void Service::set_ready_handler( const function< void ( Service& ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( value not_eq nullptr ) { m_pimpl->m_ready_handler = bind( value, std::ref( *this ) ); } } void Service::set_signal_handler( const int signal, const function< void ( const int ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( value not_eq nullptr ) { m_pimpl->m_signal_handlers[ signal ] = value; } } void Service::set_not_found_handler( const function< void ( const shared_ptr< Session > ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_not_found_handler = value; } void Service::set_method_not_allowed_handler( const function< void ( const shared_ptr< Session > ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_method_not_allowed_handler = value; } void Service::set_method_not_implemented_handler( const function< void ( const shared_ptr< Session > ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_method_not_implemented_handler = value; } void Service::set_failed_filter_validation_handler( const function< void ( const shared_ptr< Session > ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_failed_filter_validation_handler = value; } void Service::set_error_handler( const function< void ( const int, const exception&, const shared_ptr< Session > ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } if ( value == nullptr ) { m_pimpl->m_error_handler = ServiceImpl::default_error_handler; } m_pimpl->m_error_handler = value; } void Service::set_authentication_handler( const function< void ( const shared_ptr< Session >, const function< void ( const shared_ptr< Session > ) >& ) >& value ) { if ( m_pimpl->m_is_running ) { throw runtime_error( "Runtime modifications of the service are prohibited." ); } m_pimpl->m_authentication_handler = value; } } restbed-4.0/source/corvusoft/restbed/service.hpp000066400000000000000000000077721270400672200221550ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_SERVICE_H #define _RESTBED_SERVICE_H 1 //System Includes #include #include #include #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Uri; class Rule; class Logger; class Session; class Resource; class Settings; class SessionManager; namespace detail { class ServiceImpl; } class Service { public: //Friends //Definitions //Constructors Service( void ); virtual ~Service( void ); //Functionality void stop( void ); void start( const std::shared_ptr< const Settings >& settings = nullptr ); void restart( const std::shared_ptr< const Settings >& settings = nullptr ); void add_rule( const std::shared_ptr< Rule >& rule ); void add_rule( const std::shared_ptr< Rule >& rule, const int priority ); void publish( const std::shared_ptr< const Resource >& resource ); void suppress( const std::shared_ptr< const Resource >& resource ); void schedule( const std::function< void ( void ) >& task, const std::chrono::milliseconds& interval = std::chrono::milliseconds::zero( ) ); //Getters const std::shared_ptr< const Uri > get_http_uri( void ) const; const std::shared_ptr< const Uri > get_https_uri( void ) const; //Setters void set_logger( const std::shared_ptr< Logger >& value ); void set_session_manager( const std::shared_ptr< SessionManager >& value ); void set_ready_handler( const std::function< void ( Service& ) >& value ); void set_signal_handler( const int signal, const std::function< void ( const int ) >& value ); void set_not_found_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value ); void set_method_not_allowed_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value ); void set_method_not_implemented_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value ); void set_failed_filter_validation_handler( const std::function< void ( const std::shared_ptr< Session > ) >& value ); void set_error_handler( const std::function< void ( const int, const std::exception&, const std::shared_ptr< Session > ) >& value ); void set_authentication_handler( const std::function< void ( const std::shared_ptr< Session >, const std::function< void ( const std::shared_ptr< Session > ) >& ) >& value ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors Service( const Service& original ) = delete; //Functionality //Getters //Setters //Operators Service& operator =( const Service& value ) = delete; //Properties detail::ServiceImpl* m_pimpl; }; } #endif /* _RESTBED_SERVICE_H */ restbed-4.0/source/corvusoft/restbed/session.cpp000066400000000000000000000342531270400672200221650ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/context_value.hpp" #include "corvusoft/restbed/session_manager.hpp" #include "corvusoft/restbed/detail/socket_impl.hpp" #include "corvusoft/restbed/detail/request_impl.hpp" #include "corvusoft/restbed/detail/session_impl.hpp" //External Includes #include //System Namespaces using std::set; using std::string; using std::function; using std::multimap; using std::error_code; using std::shared_ptr; using std::make_shared; using std::runtime_error; using std::placeholders::_1; using std::placeholders::_2; using std::invalid_argument; using std::chrono::milliseconds; //Project Namespaces using restbed::detail::SessionImpl; //External Namespaces using asio::buffer; namespace restbed { Session::Session( const string& id ) : m_pimpl( new SessionImpl ) { m_pimpl->m_id = id; } Session::~Session( void ) { delete m_pimpl; } bool Session::has( const string& name ) const { return m_pimpl->m_context.find( name ) not_eq m_pimpl->m_context.end( ); } void Session::erase( const string& name ) { if ( name.empty( ) ) { m_pimpl->m_context.clear( ); } else { m_pimpl->m_context.erase( name ); } } const set< string > Session::keys( void ) const { std::set< string > keys; for ( const auto& value : m_pimpl->m_context ) { keys.insert( value.first ); } return keys; } bool Session::is_open( void ) const { return m_pimpl->m_request not_eq nullptr and m_pimpl->m_request->m_pimpl->m_socket not_eq nullptr and m_pimpl->m_request->m_pimpl->m_socket->is_open( ); } bool Session::is_closed( void ) const { return not is_open( ); } void Session::close( const Bytes& body ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Close failed: session already closed." ), session ); } m_pimpl->m_request->m_pimpl->m_socket->write( body, [ this, session ]( const error_code & error, size_t ) { if ( error ) { const auto message = String::format( "Close failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } m_pimpl->m_manager->save( session, [ this, session ]( const shared_ptr< Session > ) { m_pimpl->m_request->m_pimpl->m_socket->close( ); } ); } ); } void Session::close( const Response& response ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Close failed: session already closed." ), session ); } m_pimpl->transmit( response, [ this, session ]( const error_code & error, size_t ) { if ( error ) { const auto message = String::format( "Close failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } m_pimpl->m_manager->save( session, [ this ]( const shared_ptr< Session > ) { m_pimpl->m_request->m_pimpl->m_socket->close( ); } ); } ); } void Session::close( const string& body ) { close( String::to_bytes( body ) ); } void Session::close( const int status, const Bytes& body ) { static multimap< string, string > empty; close( status, body, empty ); } void Session::close( const int status, const string& body ) { static multimap< string, string > empty; close( status, body, empty ); } void Session::close( const int status, const multimap< string, string >& headers ) { close( status, "", headers ); } void Session::close( const int status, const string& body, const multimap< string, string >& headers ) { close( status, String::to_bytes( body ), headers ); } void Session::close( const int status, const Bytes& body, const multimap< string, string >& headers ) { Response response; response.set_body( body ); response.set_headers( headers ); response.set_status_code( status ); close( response ); } void Session::yield( const Bytes& body, const function< void ( const shared_ptr< Session > ) >& callback ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Yield failed: session already closed." ), session ); } m_pimpl->m_request->m_pimpl->m_socket->write( body, [ this, session, callback ]( const error_code & error, size_t ) { if ( error ) { const auto message = String::format( "Yield failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } callback( session ); } ); } void Session::yield( const string& body, const function< void ( const shared_ptr< Session > ) >& callback ) { yield( String::to_bytes( body ), callback ); } void Session::yield( const Response& response, const function< void ( const shared_ptr< Session > ) >& callback ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Yield failed: session already closed." ), session ); } m_pimpl->transmit( response, [ this, session, callback ]( const error_code & error, size_t ) { if ( error ) { const auto message = String::format( "Yield failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } if ( callback == nullptr ) { m_pimpl->m_request->m_pimpl->m_buffer = make_shared< asio::streambuf >( ); m_pimpl->m_request->m_pimpl->m_socket->read( m_pimpl->m_request->m_pimpl->m_buffer, "\r\n\r\n", [ this, session ]( const error_code & error, const size_t length ) { m_pimpl->m_keep_alive_callback( error, length, session ); } ); return; } callback( session ); } ); } void Session::yield( const int status, const Bytes& body, const function< void ( const shared_ptr< Session > ) >& callback ) { static multimap< string, string > empty; yield( status, body, empty, callback ); } void Session::yield( const int status, const string& body, const function< void ( const shared_ptr< Session > ) >& callback ) { static multimap< string, string > empty; yield( status, body, empty, callback ); } void Session::yield( const int status, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback ) { yield( status, "", headers, callback ); } void Session::yield( const int status, const string& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback ) { yield( status, String::to_bytes( body ), headers, callback ); } void Session::yield( const int status, const Bytes& body, const multimap< string, string >& headers, const function< void ( const shared_ptr< Session > ) >& callback ) { Response response; response.set_body( body ); response.set_headers( headers ); response.set_status_code( status ); yield( response, callback ); } void Session::fetch( const size_t length, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Fetch failed: session already closed." ), session ); } if ( length > m_pimpl->m_request->m_pimpl->m_buffer->size( ) ) { size_t size = length - m_pimpl->m_request->m_pimpl->m_buffer->size( ); m_pimpl->m_request->m_pimpl->m_socket->read( m_pimpl->m_request->m_pimpl->m_buffer, size, [ this, session, length, callback ]( const error_code & error, size_t ) { if ( error ) { const auto message = String::format( "Fetch failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } m_pimpl->fetch_body( length, session, callback ); } ); } else { m_pimpl->fetch_body( length, session, callback ); } } void Session::fetch( const string& delimiter, const function< void ( const shared_ptr< Session >, const Bytes& ) >& callback ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Fetch failed: session already closed." ), session ); } m_pimpl->m_request->m_pimpl->m_socket->read( m_pimpl->m_request->m_pimpl->m_buffer, delimiter, [ this, session, callback ]( const error_code & error, size_t length ) { if ( error ) { const auto message = String::format( "Fetch failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } m_pimpl->fetch_body( length, session, callback ); } ); } void Session::sleep_for( const milliseconds& delay, const function< void ( const shared_ptr< Session > ) >& callback ) { auto session = shared_from_this( ); if ( is_closed( ) ) { const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( "Sleep failed: session already closed." ), session ); } m_pimpl->m_request->m_pimpl->m_socket->sleep_for( delay, [ delay, session, callback, this ]( const error_code & error ) { if ( error ) { const auto message = String::format( "Wait failed: %s", error.message( ).data( ) ); const auto error_handler = m_pimpl->get_error_handler( ); return error_handler( 500, runtime_error( message ), session ); } if ( callback not_eq nullptr ) { callback( session ); } } ); } const string& Session::get_id( void ) const { return m_pimpl->m_id; } const string Session::get_origin( void ) const { if ( m_pimpl->m_request == nullptr or m_pimpl->m_request->m_pimpl->m_socket == nullptr ) { return ""; } return m_pimpl->m_request->m_pimpl->m_socket->get_remote_endpoint( ); } const string Session::get_destination( void ) const { if ( m_pimpl->m_request == nullptr or m_pimpl->m_request->m_pimpl->m_socket == nullptr ) { return ""; } return m_pimpl->m_request->m_pimpl->m_socket->get_local_endpoint( ); } const shared_ptr< const Request > Session::get_request( void ) const { return m_pimpl->m_request; } const shared_ptr< const Resource > Session::get_resource( void ) const { return m_pimpl->m_resource; } const multimap< string, string >& Session::get_headers( void ) const { return m_pimpl->m_headers; } const ContextValue& Session::get( const string& name ) const { return m_pimpl->m_context.at( name ); } const ContextValue& Session::get( const string& name, const ContextValue& default_value ) const { if ( has( name ) ) { return m_pimpl->m_context.at( name ); } return default_value; } void Session::set_id( const string& value ) { m_pimpl->m_id = value; } void Session::set( const string& name, const ContextValue& value ) { if ( has( name ) ) { m_pimpl->m_context.erase( name ); } m_pimpl->m_context.insert( make_pair( name, value ) ); } void Session::set_header( const string& name, const string& value ) { m_pimpl->m_headers.insert( make_pair( name, value ) ); } void Session::set_headers( const multimap< string, string >& values ) { m_pimpl->m_headers = values; } } restbed-4.0/source/corvusoft/restbed/session.hpp000066400000000000000000000137671270400672200222010ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_SESSION_H #define _RESTBED_SESSION_H 1 //System Includes #include #include #include #include #include #include //Project Includes #include #include #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Request; class Session; class Response; class Resource; namespace detail { class SessionImpl; class ServiceImpl; } class Session : public std::enable_shared_from_this< Session > { public: //Friends //Definitions //Constructors explicit Session( const std::string& id ); virtual ~Session( void ); //Functionality bool has( const std::string& name ) const; void erase( const std::string& name = "" ); const std::set< std::string > keys( void ) const; bool is_open( void ) const; bool is_closed( void ) const; void close( const Bytes& body ); void close( const Response& response ); void close( const std::string& body = "" ); void close( const int status, const Bytes& body ); void close( const int status, const std::string& body = "" ); void close( const int status, const std::multimap< std::string, std::string >& headers ); void close( const int status, const std::string& body, const std::multimap< std::string, std::string >& headers ); void close( const int status, const Bytes& body, const std::multimap< std::string, std::string >& headers ); void yield( const Bytes& data, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const std::string& data, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const Response& response, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const int status, const std::string& body, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const int status, const Bytes& body = { }, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const int status, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const int status, const Bytes& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void yield( const int status, const std::string& body, const std::multimap< std::string, std::string >& headers, const std::function< void ( const std::shared_ptr< Session > ) >& callback = nullptr ); void fetch( const std::size_t length, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback ); void fetch( const std::string& delimiter, const std::function< void ( const std::shared_ptr< Session >, const Bytes& ) >& callback ); void sleep_for( const std::chrono::milliseconds& delay, const std::function< void ( const std::shared_ptr< Session > ) >& callback ); //Getters const std::string& get_id( void ) const; const std::string get_origin( void ) const; const std::string get_destination( void ) const; const std::shared_ptr< const Request > get_request( void ) const; const std::shared_ptr< const Resource > get_resource( void ) const; const std::multimap< std::string, std::string >& get_headers( void ) const; const ContextValue& get( const std::string& name ) const; const ContextValue& get( const std::string& name, const ContextValue& default_value ) const; //Setters void set_id( const std::string& value ); void set( const std::string& name, const ContextValue& value ); void set_header( const std::string& name, const std::string& value ); void set_headers( const std::multimap< std::string, std::string >& values ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends friend detail::ServiceImpl; friend detail::SessionImpl; //Definitions //Constructors Session( void ) = delete; Session( const Session& original ) = delete; //Functionality //Getters //Setters //Operators Session& operator =( const Session& value ) = delete; //Properties detail::SessionImpl* m_pimpl; }; } #endif /* _RESTBED_SESSION_H */ restbed-4.0/source/corvusoft/restbed/session_manager.cpp000066400000000000000000000025361270400672200236560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/session_manager.hpp" //External Includes //System Namespaces using std::string; using std::function; using std::to_string; using std::shared_ptr; using std::make_shared; //Project Namespaces //External Namespaces namespace restbed { SessionManager::SessionManager( void ) { return; } SessionManager::~SessionManager( void ) { return; } void SessionManager::stop( void ) { return; } void SessionManager::start( const shared_ptr< const Settings >& ) { return; } void SessionManager::create( const function< void ( const shared_ptr< Session > ) >& callback ) { callback( make_shared< Session >( String::empty ) ); } void SessionManager::load( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { callback( session ); } void SessionManager::save( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { callback( session ); } } restbed-4.0/source/corvusoft/restbed/session_manager.hpp000066400000000000000000000044121270400672200236560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_SESSION_MANAGER_H #define _RESTBED_SESSION_MANAGER_H 1 //System Includes #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Session; class Settings; namespace detail { struct SessionManagerImpl; } class SessionManager { public: //Friends //Definitions //Constructors SessionManager( void ); virtual ~SessionManager( void ); //Functionality virtual void stop( void ); virtual void start( const std::shared_ptr< const Settings >& settings ); virtual void create( const std::function< void ( const std::shared_ptr< Session > ) >& callback ); virtual void load( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback ); virtual void save( const std::shared_ptr< Session > session, const std::function< void ( const std::shared_ptr< Session > ) >& callback ); //Getters //Setters //Operators //Properties protected: //Friends //Definitions //Constructors SessionManager( const SessionManager& original ) = delete; //Functionality //Getters //Setters //Operators SessionManager& operator =( const SessionManager& value ) = delete; //Properties private: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties }; } #endif /* _RESTBED_SESSION_MANAGER_H */ restbed-4.0/source/corvusoft/restbed/settings.cpp000066400000000000000000000105101270400672200223300ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes //Project Includes #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/detail/settings_impl.hpp" //External Includes //System Namespaces using std::map; using std::string; using std::multimap; using std::make_pair; using std::shared_ptr; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::duration_cast; //Project Namespaces using restbed::detail::SettingsImpl; //External Namespaces namespace restbed { Settings::Settings( void ) : m_pimpl( new SettingsImpl ) { return; } Settings::~Settings( void ) { delete m_pimpl; } uint16_t Settings::get_port( void ) const { return m_pimpl->m_port; } string Settings::get_root( void ) const { return m_pimpl->m_root; } unsigned int Settings::get_worker_limit( void ) const { return m_pimpl->m_worker_limit; } unsigned int Settings::get_connection_limit( void ) const { return m_pimpl->m_connection_limit; } string Settings::get_bind_address( void ) const { return m_pimpl->m_bind_address; } bool Settings::get_case_insensitive_uris( void ) const { return m_pimpl->m_case_insensitive_uris; } milliseconds Settings::get_connection_timeout( void ) const { return m_pimpl->m_connection_timeout; } string Settings::get_status_message( const int code ) const { return ( m_pimpl->m_status_messages.count( code ) ) ? m_pimpl->m_status_messages.at( code ) : "No Appropriate Status Message Found"; } map< int, string > Settings::get_status_messages( void ) const { return m_pimpl->m_status_messages; } string Settings::get_property( const string& name ) const { return ( m_pimpl->m_properties.count( name ) ) ? m_pimpl->m_properties.at( name ) : ""; } map< string, string > Settings::get_properties( void ) const { return m_pimpl->m_properties; } shared_ptr< const SSLSettings > Settings::get_ssl_settings( void ) const { return m_pimpl->m_ssl_settings; } multimap< string, string > Settings::get_default_headers( void ) const { return m_pimpl->m_default_headers; } void Settings::set_port( const uint16_t value ) { m_pimpl->m_port = value; } void Settings::set_root( const string& value ) { m_pimpl->m_root = value; } void Settings::set_worker_limit( const unsigned int value ) { m_pimpl->m_worker_limit = value; } void Settings::set_connection_limit( const unsigned int value ) { m_pimpl->m_connection_limit = value; } void Settings::set_bind_address( const string& value ) { m_pimpl->m_bind_address = value; } void Settings::set_case_insensitive_uris( const bool value ) { m_pimpl->m_case_insensitive_uris = value; } void Settings::set_connection_timeout( const seconds& value ) { m_pimpl->m_connection_timeout = duration_cast< milliseconds >( value ); } void Settings::set_connection_timeout( const milliseconds& value ) { m_pimpl->m_connection_timeout = value; } void Settings::set_status_message( const int code, const string& message ) { m_pimpl->m_status_messages[ code ] = message; } void Settings::set_status_messages( const map< int, string >& values ) { m_pimpl->m_status_messages = values; } void Settings::set_property( const string& name, const string& value ) { m_pimpl->m_properties[ name ] = value; } void Settings::set_properties( const map< string, string >& values ) { m_pimpl->m_properties = values; } void Settings::set_ssl_settings( const shared_ptr< const SSLSettings >& values ) { m_pimpl->m_ssl_settings = values; } void Settings::set_default_header( const string& name, const string& value ) { m_pimpl->m_default_headers.insert( make_pair( name, value ) ); } void Settings::set_default_headers( const multimap< string, string >& values ) { m_pimpl->m_default_headers = values; } } restbed-4.0/source/corvusoft/restbed/settings.hpp000066400000000000000000000077631270400672200223550ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_SETTINGS_H #define _RESTBED_SETTINGS_H 1 //System Includes #include #include #include #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class SSLSettings; namespace detail { struct SettingsImpl; } class Settings { public: //Friends //Definitions //Constructors Settings( void ); virtual ~Settings( void ); //Functionality //Getters uint16_t get_port( void ) const; std::string get_root( void ) const; unsigned int get_worker_limit( void ) const; unsigned int get_connection_limit( void ) const; std::string get_bind_address( void ) const; bool get_case_insensitive_uris( void ) const; std::chrono::milliseconds get_connection_timeout( void ) const; std::string get_status_message( const int code ) const; std::map< int, std::string > get_status_messages( void ) const; std::string get_property( const std::string& name ) const; std::map< std::string, std::string > get_properties( void ) const; std::shared_ptr< const SSLSettings > get_ssl_settings( void ) const; std::multimap< std::string, std::string > get_default_headers( void ) const; //Setters void set_port( const uint16_t value ); void set_root( const std::string& value ); void set_worker_limit( const unsigned int value ); void set_connection_limit( const unsigned int value ); void set_bind_address( const std::string& value ); void set_case_insensitive_uris( const bool value ); void set_connection_timeout( const std::chrono::seconds& value ); void set_connection_timeout( const std::chrono::milliseconds& value ); void set_status_message( const int code, const std::string& message ); void set_status_messages( const std::map< int, std::string >& values ); void set_property( const std::string& name, const std::string& value ); void set_properties( const std::map< std::string, std::string >& values ); void set_ssl_settings( const std::shared_ptr< const SSLSettings >& value ); void set_default_header( const std::string& name, const std::string& value ); void set_default_headers( const std::multimap< std::string, std::string >& values ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors Settings( const Settings& original ) = delete; //Functionality //Getters //Setters //Operators Settings& operator =( const Settings& value ) = delete; //Properties detail::SettingsImpl* m_pimpl; }; } #endif /* _RESTBED_SETTINGS_H */ restbed-4.0/source/corvusoft/restbed/ssl_settings.cpp000066400000000000000000000125111270400672200232140ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/detail/ssl_settings_impl.hpp" //External Includes //System Namespaces using std::string; //Project Namespaces using restbed::detail::SSLSettingsImpl; //External Namespaces namespace restbed { SSLSettings::SSLSettings( void ) : m_pimpl( new SSLSettingsImpl ) { return; } SSLSettings::~SSLSettings( void ) { delete m_pimpl; } bool SSLSettings::has_disabled_http( void ) const { return m_pimpl->m_http_disabled; } bool SSLSettings::has_enabled_sslv2( void ) const { return m_pimpl->m_sslv2_enabled; } bool SSLSettings::has_enabled_sslv3( void ) const { return m_pimpl->m_sslv3_enabled; } bool SSLSettings::has_enabled_tlsv1( void ) const { return m_pimpl->m_tlsv1_enabled; } bool SSLSettings::has_enabled_tlsv11( void ) const { return m_pimpl->m_tlsv11_enabled; } bool SSLSettings::has_enabled_tlsv12( void ) const { return m_pimpl->m_tlsv12_enabled; } bool SSLSettings::has_enabled_compression( void ) const { return m_pimpl->m_compression_enabled; } bool SSLSettings::has_enabled_default_workarounds( void ) const { return m_pimpl->m_default_workarounds_enabled; } bool SSLSettings::has_enabled_single_diffie_hellman_use( void ) const { return m_pimpl->m_single_diffie_hellman_use_enabled; } uint16_t SSLSettings::get_port( void ) const { return m_pimpl->m_port; } string SSLSettings::get_bind_address( void ) const { return m_pimpl->m_bind_address; } string SSLSettings::get_certificate( void ) const { return m_pimpl->m_certificate; } string SSLSettings::get_passphrase( void ) const { return m_pimpl->m_passphrase; } string SSLSettings::get_private_key( void ) const { return m_pimpl->m_private_key; } string SSLSettings::get_private_rsa_key( void ) const { return m_pimpl->m_private_rsa_key; } string SSLSettings::get_certificate_chain( void ) const { return m_pimpl->m_certificate_chain; } string SSLSettings::get_temporary_diffie_hellman( void ) const { return m_pimpl->m_temporary_diffie_hellman; } string SSLSettings::get_certificate_authority_pool( void ) const { return m_pimpl->m_certificate_authority_pool; } void SSLSettings::set_port( const uint16_t value ) { m_pimpl->m_port = value; } void SSLSettings::set_bind_address( const string& value ) { m_pimpl->m_bind_address = value; } void SSLSettings::set_http_disabled( const bool value ) { m_pimpl->m_http_disabled = value; } void SSLSettings::set_sslv2_enabled( const bool value ) { m_pimpl->m_sslv2_enabled = value; } void SSLSettings::set_sslv3_enabled( const bool value ) { m_pimpl->m_sslv3_enabled = value; } void SSLSettings::set_tlsv1_enabled( const bool value ) { m_pimpl->m_tlsv1_enabled = value; } void SSLSettings::set_tlsv11_enabled( const bool value ) { m_pimpl->m_tlsv11_enabled = value; } void SSLSettings::set_tlsv12_enabled( const bool value ) { m_pimpl->m_tlsv12_enabled = value; } void SSLSettings::set_compression_enabled( const bool value ) { m_pimpl->m_compression_enabled = value; } void SSLSettings::set_default_workarounds_enabled( const bool value ) { m_pimpl->m_default_workarounds_enabled = value; } void SSLSettings::set_single_diffie_hellman_use_enabled( const bool value ) { m_pimpl->m_single_diffie_hellman_use_enabled = value; } void SSLSettings::set_certificate( const Uri& value ) { m_pimpl->m_certificate = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } void SSLSettings::set_certificate_chain( const Uri& value ) { m_pimpl->m_certificate_chain = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } void SSLSettings::set_certificate_authority_pool( const Uri& value ) { m_pimpl->m_certificate_authority_pool = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } void SSLSettings::set_passphrase( const string& value ) { m_pimpl->m_passphrase = value; } void SSLSettings::set_private_key( const Uri& value ) { m_pimpl->m_private_key = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } void SSLSettings::set_private_rsa_key( const Uri& value ) { m_pimpl->m_private_rsa_key = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } void SSLSettings::set_temporary_diffie_hellman( const Uri& value ) { m_pimpl->m_temporary_diffie_hellman = String::remove( "file://", value.to_string( ), String::CASE_INSENSITIVE ); } } restbed-4.0/source/corvusoft/restbed/ssl_settings.hpp000066400000000000000000000102321270400672200232170ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_SSL_SETTINGS_H #define _RESTBED_SSL_SETTINGS_H 1 //System Includes #include #include //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class Uri; namespace detail { struct SSLSettingsImpl; } class SSLSettings { public: //Friends //Definitions //Constructors SSLSettings( void ); virtual ~SSLSettings( void ); //Functionality bool has_disabled_http( void ) const; bool has_enabled_sslv2( void ) const; bool has_enabled_sslv3( void ) const; bool has_enabled_tlsv1( void ) const; bool has_enabled_tlsv11( void ) const; bool has_enabled_tlsv12( void ) const; bool has_enabled_compression( void ) const; bool has_enabled_default_workarounds( void ) const; bool has_enabled_single_diffie_hellman_use( void ) const; //Getters uint16_t get_port( void ) const; std::string get_bind_address( void ) const; std::string get_certificate( void ) const; std::string get_passphrase( void ) const; std::string get_private_key( void ) const; std::string get_private_rsa_key( void ) const; std::string get_certificate_chain( void ) const; std::string get_temporary_diffie_hellman( void ) const; std::string get_certificate_authority_pool( void ) const; //Setters void set_port( const uint16_t value ); void set_bind_address( const std::string& value ); void set_http_disabled( const bool value ); void set_sslv2_enabled( const bool value ); void set_sslv3_enabled( const bool value ); void set_tlsv1_enabled( const bool value ); void set_tlsv11_enabled( const bool value ); void set_tlsv12_enabled( const bool value ); void set_compression_enabled( const bool value ); void set_default_workarounds_enabled( const bool value ); void set_single_diffie_hellman_use_enabled( const bool value ); void set_certificate( const Uri& value ); void set_certificate_chain( const Uri& value ); void set_certificate_authority_pool( const Uri& value ); void set_passphrase( const std::string& value ); void set_private_key( const Uri& value ); void set_private_rsa_key( const Uri& value ); void set_temporary_diffie_hellman( const Uri& value ); //Operators //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors SSLSettings( const SSLSettings& original ) = delete; //Functionality //Getters //Setters //Operators SSLSettings& operator =( const SSLSettings& value ) = delete; //Properties detail::SSLSettingsImpl* m_pimpl; }; } #endif /* _RESTBED_SSL_SETTINGS_H */ restbed-4.0/source/corvusoft/restbed/status_code.hpp000066400000000000000000000042341270400672200230200ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_STATUS_CODE_H #define _RESTBED_STATUS_CODE_H 1 //System Includes //Project Includes //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations enum : int { CONTINUE = 100, SWITCHING_PROTOCOLS = 101, PROCESSING = 102, OK = 200, CREATED = 201, ACCEPTED = 202, NON_AUTHORITATIVE_INFORMATION = 203, NO_CONTENT = 204, RESET_CONTENT = 205, PARTIAL_CONTENT = 206, MULTI_STATUS = 207, ALREADY_REPORTED = 208, IM_USED = 226, MULTIPLE_CHOICES = 300, MOVED_PERMANENTLY = 301, FOUND = 302, SEE_OTHER = 303, NOT_MODIFIED = 304, USE_PROXY = 305, RESERVED = 306, TEMPORARY_REDIRECT = 307, PERMANENT_REDIRECT = 308, 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, REQUESTED_RANGE_NOT_SATISFIABLE = 416, EXPECTATION_FAILED = 417, UNPROCESSABLE_ENTITY = 422, LOCKED = 423, FAILED_DEPENDENCY = 424, 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, VARIANT_ALSO_NEGOTIATES = 506, INSUFFICIENT_STORAGE = 507, LOOP_DETECTED = 508, NOT_EXTENDED = 510, NETWORK_AUTHENTICATION_REQUIRED = 511 }; } #endif /* _RESTBED_STATUS_CODE_H */ restbed-4.0/source/corvusoft/restbed/string.cpp000066400000000000000000000107421270400672200220050ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/string.hpp" //External Includes //System Namespaces using std::regex; using std::string; using std::vector; using std::smatch; using std::multimap; using std::transform; using std::regex_match; using std::regex_replace; using std::regex_constants::icase; //Project Namespaces //External Namespaces namespace restbed { const string String::empty = ""; Bytes String::to_bytes( const string& value ) { return Bytes( value.begin( ), value.end( ) ); } string String::to_string( const Bytes& value ) { return string( value.begin( ), value.end( ) ); } string String::lowercase( const string& value ) { string result = ""; transform( value.begin( ), value.end( ), back_inserter( result ), ( int ( * )( int ) )tolower ); return result; } string String::uppercase( const string& value ) { string result = ""; transform( value.begin( ), value.end( ), back_inserter( result ), ( int ( * )( int ) )toupper ); return result; } string String::format( const char* format, ... ) { va_list arguments; va_start( arguments, format ); string formatted = ""; string::size_type length = 1024; string::size_type required_length = String::format( formatted, length, format, arguments ); if ( required_length > length ) { String::format( formatted, required_length, format, arguments ); } va_end( arguments ); return formatted; } vector< string > String::split( const string& value, const char delimiter ) { vector< string > tokens; string::size_type start = 0; string::size_type end = 0; while ( ( end = value.find( delimiter, start ) ) not_eq string::npos ) { const auto text = value.substr( start, end - start ); if ( not text.empty( ) ) { tokens.push_back( text ); } start = end + 1; } const auto token = value.substr( start ); if ( not token.empty( ) ) { tokens.push_back( token ); } return tokens; } string String::join( const multimap< string, string >& values, const string& pair_delimiter, const string& delimiter ) { string result = ""; for ( auto value : values ) { result += value.first + pair_delimiter + value.second + delimiter; } if ( not result.empty( ) ) { const size_t position = result.find_last_not_of( delimiter ); if ( string::npos not_eq position ) { result = result.substr( 0, position + 1 ); } } return result; } string String::remove( const string& target, const string& value, const Option option ) { return replace( target, "", value, option ); } string String::replace( const string& target, const string& substitute, const string& value, const Option option ) { if ( target.empty( ) ) { return value; } static const regex escape( "([.^$|()\\[\\]{}*+?\\\\])" ); const auto expression = regex_replace( target, escape, "\\$1" ); auto pattern = regex( expression ); if ( option & String::Option::CASE_INSENSITIVE ) { pattern.assign( expression, icase ); } smatch match; string result = value; while ( regex_search( result, match, pattern ) ) { result = regex_replace( result, pattern, substitute ); } return result; } string::size_type String::format( string& output, const string::size_type length, const char* format, va_list arguments ) { char* formatted = new char[ length + 1 ]; int required_length = vsnprintf( formatted, length + 1, format, arguments ); if ( required_length == -1 ) { required_length = 0; } output = formatted; delete[ ] formatted; return required_length; } } restbed-4.0/source/corvusoft/restbed/string.hpp000066400000000000000000000056111270400672200220110ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_STRING_H #define _RESTBED_STRING_H 1 //System Includes #include #include #include #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations class String { public: //Friends //Definitions enum Option : int { CASE_SENSITIVE = 0, CASE_INSENSITIVE = 1 }; //Constructors //Functionality static Bytes to_bytes( const std::string& value ); static std::string to_string( const Bytes& value ); static std::string lowercase( const std::string& value ); static std::string uppercase( const std::string& value ); static std::string format( const char* format, ... ); static std::vector< std::string > split( const std::string& text, const char delimiter ); static std::string join( const std::multimap< std::string, std::string >& values, const std::string& pair_delimiter, const std::string& delimiter ); static std::string remove( const std::string& needle, const std::string& haystack, const Option option = CASE_SENSITIVE ); static std::string replace( const std::string& target, const std::string& substitute, const std::string& value, const Option option = CASE_SENSITIVE ); //Getters //Setters //Operators //Properties static const std::string empty; protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors String( void ) = delete; String( const String& original ) = delete; virtual ~String( void ) = delete; //Functionality static std::string::size_type format( std::string& output, const std::string::size_type length, const char* format, va_list arguments ); //Getters //Setters //Operators String& operator =( const String& value ) = delete; //Properties }; } #endif /* _RESTBED_STRING_H */ restbed-4.0/source/corvusoft/restbed/uri.cpp000066400000000000000000000250161270400672200212760ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #if defined(WIN32) #include #include #pragma comment( lib, "Ws2_32.lib" ) #else #include #include #include #endif //Project Includes #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/detail/uri_impl.hpp" //External Includes //System Namespaces using std::stoi; using std::regex; using std::smatch; using std::strtol; using std::string; using std::multimap; using std::snprintf; using std::to_string; using std::runtime_error; using std::invalid_argument; //Project Namespaces using restbed::detail::UriImpl; //External Namespaces namespace restbed { Uri::Uri( const string& value, bool relative ) : m_pimpl( new UriImpl ) { if ( not is_valid( value ) ) { throw invalid_argument( "Argument is not a valid URI: " + value ); } m_pimpl->m_uri = value; m_pimpl->m_relative = relative; } Uri::Uri( const Uri& original ) : m_pimpl( new UriImpl( *original.m_pimpl ) ) { return; } Uri::~Uri( void ) { delete m_pimpl; } bool Uri::is_relative( void ) const { return m_pimpl->m_relative; } bool Uri::is_absolute( void ) const { return not m_pimpl->m_relative; } string Uri::to_string( void ) const { return m_pimpl->m_uri; } bool Uri::is_valid( const string& value ) { static const regex pattern( "^([a-zA-Z][a-zA-Z0-9+-.]*):((\\/\\/(((([a-zA-Z0-9\\-._~!$&'()*+,;=':]|(%[0-9a-fA-F]{2}))*)@)?((\\[((((([0-9a-fA-F]{1,4}:){6}|(::([0-9a-fA-F]{1,4}:){5})|(([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:){4})|((([0-9a-fA-F]{1,4}:)?[0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:){3})|((([0-9a-fA-F]{1,4}:){0,2}[0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:){2})|((([0-9a-fA-F]{1,4}:){0,3}[0-9a-fA-F]{1,4})?::[0-9a-fA-F]{1,4}:)|((([0-9a-fA-F]{1,4}:){0,4}[0-9a-fA-F]{1,4})?::))((([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4}))|(([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5])))))|((([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})?::[0-9a-fA-F]{1,4})|((([0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4})?::))|(v[0-9a-fA-F]+\\.[a-zA-Z0-9\\-._~!$&'()*+,;=':]+))\\])|(([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.([0-9]|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5])))|(([a-zA-Z0-9\\-._~!$&'()*+,;=']|(%[0-9a-fA-F]{2}))*))(:[0-9]*)?)((\\/([a-zA-Z0-9\\-._~!$&'()*+,;=':@]|(%[0-9a-fA-F]{2}))*)*))|(\\/?(([a-zA-Z0-9\\-._~!$&'()*+,;=':@]|(%[0-9a-fA-F]{2}))+(\\/([a-zA-Z0-9\\-._~!$&'()*+,;=':@]|(%[0-9a-fA-F]{2}))*)*)?))(\\?(([a-zA-Z0-9\\-._~!$&'()*+,;=':@\\/?]|(%[0-9a-fA-F]{2}))*))?((#(([a-zA-Z0-9\\-._~!$&'()*+,;=':@\\/?]|(%[0-9a-fA-F]{2}))*)))?$" ); return regex_match( value, pattern ); } Uri Uri::parse( const string& value ) { return Uri( value ); } string Uri::decode( const Bytes& value ) { return decode( string( value.begin( ), value.end( ) ) ); } string Uri::decode( const string& value ) { string result = String::empty; for ( string::size_type index = 0; index not_eq value.length( ); index++ ) { if ( value[ index ] == '%' ) { char hexidecimal[ 3 ] = { 0 }; hexidecimal[ 0 ] = value[ ++index ]; hexidecimal[ 1 ] = value[ ++index ]; char byte = static_cast< char >( strtol( hexidecimal, nullptr, 16 ) ); result.push_back( byte ); } else { result.push_back( value[ index ] ); } } return result; } string Uri::decode_parameter( const string& value ) { return decode( String::replace( "+", " ", value ) ); } string Uri::encode( const Bytes& value ) { string encoded = String::empty; for ( Byte character : value ) { char hexidecimal[ 4 ] = { 0 }; switch ( character ) { //unsafe carachters case ' ': case '\"': case '<': case '>': case '#': case '%': case '{': case '}': case '|': case '\\': case '^': case '~': case '[': case ']': case '`': //reserved characters case '$': case '&': case '+': case ',': case '/': case ':': case ';': case '=': case '?': case '@': snprintf( hexidecimal, sizeof( hexidecimal ), "%%%02X", character ); encoded.append( hexidecimal ); break; default: hexidecimal[ 0 ] = character; encoded.append( hexidecimal ); break; } } return encoded; } string Uri::encode( const string& value ) { return encode( Bytes( value.begin( ), value.end( ) ) ); } string Uri::encode_parameter( const string& value ) { return encode( value ); } uint16_t Uri::get_port( void ) const { smatch match; string port = String::empty; static const regex pattern( "^[a-zA-Z][a-zA-Z0-9+\\-.]*://(([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@)?([a-zA-Z0-9\\-._~%]+|\\[[a-zA-Z0-9\\-._~%!$&'()*+,;=:]+\\]):([0-9]+)" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { port = match[ 6 ]; } else { const auto scheme = get_scheme( ); if ( not scheme.empty( ) ) { const struct servent* entry = getservbyname( scheme.data( ), nullptr ); if ( entry not_eq nullptr ) { port = ::to_string( ntohs( entry->s_port ) ); } } } if ( port.empty( ) ) { return 0; } return stoi( port ); } string Uri::get_path( void ) const { static const regex pattern( "^([a-zA-Z][a-zA-Z0-9+\\-.]*://([^/?#]+)?)?([a-zA-Z0-9\\-._~%!$&'()*+,;=:@/]*)" ); smatch match; if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return ( is_absolute( ) ) ? match[ 3 ] : string( match[ 2 ] ) + string( match[ 3 ] ); } return String::empty; } string Uri::get_query( void ) const { smatch match; static const regex pattern( "^[^?#]+\\?([^#]+)" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 1 ]; } return String::empty; } string Uri::get_scheme( void ) const { smatch match; static const regex pattern( "^([a-zA-Z][a-zA-Z0-9+\\-.]*):" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 1 ]; } return String::empty; } string Uri::get_fragment( void ) const { smatch match; static const regex pattern( "#(.+)" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 1 ]; } return String::empty; } string Uri::get_username( void ) const { smatch match; static const regex pattern( "^[a-zA-Z0-9+\\-.]+://([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 1 ]; } return String::empty; } string Uri::get_password( void ) const { smatch match; static const regex pattern( "^[a-zA-Z0-9+\\-.]+://([a-zA-Z0-9\\-._~%!$&'()*+,;=]+):([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)@" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 2 ]; } return String::empty; } string Uri::get_authority( void ) const { if ( is_relative( ) ) { return String::empty; } smatch match; static const regex pattern( "^[a-zA-Z][a-zA-Z0-9+\\-.]*://(([a-zA-Z0-9\\-._~%!$&'()*+,;=]+)(:([a-zA-Z0-9\\-._~%!$&'()*+,;=]+))?@)?([a-zA-Z0-9\\-._~%]+|\\[[a-zA-Z0-9\\-._~%!$&'()*+,;=:]+\\])" ); if ( regex_search( m_pimpl->m_uri, match, pattern ) ) { return match[ 5 ]; } return String::empty; } multimap< string, string > Uri::get_query_parameters( void ) const { multimap< string, string > parameters; auto query = String::split( get_query( ), '&' ); for ( auto parameter : query ) { auto index = parameter.find_first_of( '=' ); auto name = decode_parameter( parameter.substr( 0, index ) ); auto value = decode_parameter( parameter.substr( index + 1, parameter.length( ) ) ); parameters.insert( make_pair( name, value ) ); } return parameters; } Uri& Uri::operator =( const Uri& rhs ) { m_pimpl->m_uri = rhs.m_pimpl->m_uri; return *this; } bool Uri::operator <( const Uri& rhs ) const { return m_pimpl->m_uri < rhs.m_pimpl->m_uri; } bool Uri::operator >( const Uri& rhs ) const { return m_pimpl->m_uri > rhs.m_pimpl->m_uri; } bool Uri::operator ==( const Uri& rhs ) const { return m_pimpl->m_uri == rhs.m_pimpl->m_uri; } bool Uri::operator !=( const Uri& rhs ) const { return m_pimpl->m_uri not_eq rhs.m_pimpl->m_uri; } Uri::Uri( void ) : m_pimpl( new UriImpl ) { return; } } restbed-4.0/source/corvusoft/restbed/uri.hpp000066400000000000000000000066301270400672200213040ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #ifndef _RESTBED_URI_H #define _RESTBED_URI_H 1 //System Includes #include #include #include //Project Includes #include //External Includes //System Namespaces //Project Namespaces //External Namespaces namespace restbed { //Forward Declarations namespace detail { struct UriImpl; } class Uri { public: //Friends //Definitions static const bool Relative = true; static const bool Absolute = false; //Constructors explicit Uri( const std::string& value, bool relative = false ); Uri( const Uri& original ); virtual ~Uri( void ); //Functionality bool is_relative( void ) const; bool is_absolute( void ) const; std::string to_string( void ) const; static bool is_valid( const std::string& value ); static Uri parse( const std::string& value ); static std::string decode( const Bytes& value ); static std::string decode( const std::string& value ); static std::string decode_parameter( const std::string& value ); static std::string encode( const Bytes& value ); static std::string encode( const std::string& value ); static std::string encode_parameter( const std::string& value ); //Getters uint16_t get_port( void ) const; std::string get_path( void ) const; std::string get_query( void ) const; std::string get_scheme( void ) const; std::string get_fragment( void ) const; std::string get_username( void ) const; std::string get_password( void ) const; std::string get_authority( void ) const; std::multimap< std::string, std::string > get_query_parameters( void ) const; //Setters //Operators Uri& operator =( const Uri& rhs ); bool operator <( const Uri& rhs ) const; bool operator >( const Uri& rhs ) const; bool operator ==( const Uri& rhs ) const; bool operator !=( const Uri& rhs ) const; //Properties protected: //Friends //Definitions //Constructors //Functionality //Getters //Setters //Operators //Properties private: //Friends //Definitions //Constructors Uri( void ); //Functionality //Getters //Setters //Operators //Properties detail::UriImpl* m_pimpl; }; } #endif /* _RESTBED_URI_H */ restbed-4.0/source/restbed000066400000000000000000000015161270400672200156630ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ #include "corvusoft/restbed/uri.hpp" #include "corvusoft/restbed/http.hpp" #include "corvusoft/restbed/rule.hpp" #include "corvusoft/restbed/byte.hpp" #include "corvusoft/restbed/string.hpp" #include "corvusoft/restbed/logger.hpp" #include "corvusoft/restbed/request.hpp" #include "corvusoft/restbed/service.hpp" #include "corvusoft/restbed/session.hpp" #include "corvusoft/restbed/response.hpp" #include "corvusoft/restbed/resource.hpp" #include "corvusoft/restbed/settings.hpp" #include "corvusoft/restbed/status_code.hpp" #include "corvusoft/restbed/ssl_settings.hpp" #include "corvusoft/restbed/context_value.hpp" #include "corvusoft/restbed/session_manager.hpp" #include "corvusoft/restbed/context_placeholder.hpp" #include "corvusoft/restbed/context_placeholder_base.hpp" restbed-4.0/test/000077500000000000000000000000001270400672200137645ustar00rootroot00000000000000restbed-4.0/test/acceptance/000077500000000000000000000000001270400672200160525ustar00rootroot00000000000000restbed-4.0/test/acceptance/CMakeLists.txt000066400000000000000000000602531270400672200206200ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "acceptance test suite" ) cmake_minimum_required( VERSION 2.8.10 ) # # Configuration # set( SOURCE_DIR "source" ) set( HELPERS_DIR "${CMAKE_SOURCE_DIR}/test/helpers/source" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCATCH_CONFIG_MAIN" ) include_directories( SYSTEM ${catch_INCLUDE} ${asio_INCLUDE} ) # # Build # add_executable( publishing_single_path_resources_http_get_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_get.cpp ) target_link_libraries( publishing_single_path_resources_http_get_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_get_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_get_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_put_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_put.cpp ) target_link_libraries( publishing_single_path_resources_http_put_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_put_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_put_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_head_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_head.cpp ) target_link_libraries( publishing_single_path_resources_http_head_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_head_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_head_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_post_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_post.cpp ) target_link_libraries( publishing_single_path_resources_http_post_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_post_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_post_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_trace_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_trace.cpp ) target_link_libraries( publishing_single_path_resources_http_trace_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_trace_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_trace_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_patch_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_patch.cpp ) target_link_libraries( publishing_single_path_resources_http_patch_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_patch_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_patch_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_delete_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_delete.cpp ) target_link_libraries( publishing_single_path_resources_http_delete_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_delete_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_delete_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_connect_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_connect.cpp ) target_link_libraries( publishing_single_path_resources_http_connect_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_connect_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_connect_acceptance_test_suite ) add_executable( publishing_single_path_resources_http_options_acceptance_test_suite ${SOURCE_DIR}/publishing_single_path_resources/http_options.cpp ) target_link_libraries( publishing_single_path_resources_http_options_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_single_path_resources_http_options_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_single_path_resources_http_options_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_get_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_get.cpp ) target_link_libraries( publishing_multi_path_resources_http_get_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_get_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_get_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_put_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_put.cpp ) target_link_libraries( publishing_multi_path_resources_http_put_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_put_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_put_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_head_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_head.cpp ) target_link_libraries( publishing_multi_path_resources_http_head_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_head_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_head_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_post_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_post.cpp ) target_link_libraries( publishing_multi_path_resources_http_post_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_post_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_post_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_trace_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_trace.cpp ) target_link_libraries( publishing_multi_path_resources_http_trace_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_trace_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_trace_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_patch_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_patch.cpp ) target_link_libraries( publishing_multi_path_resources_http_patch_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_patch_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_patch_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_delete_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_delete.cpp ) target_link_libraries( publishing_multi_path_resources_http_delete_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_delete_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_delete_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_connect_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_connect.cpp ) target_link_libraries( publishing_multi_path_resources_http_connect_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_connect_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_connect_acceptance_test_suite ) add_executable( publishing_multi_path_resources_http_options_acceptance_test_suite ${SOURCE_DIR}/publishing_multi_path_resources/http_options.cpp ) target_link_libraries( publishing_multi_path_resources_http_options_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publishing_multi_path_resources_http_options_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publishing_multi_path_resources_http_options_acceptance_test_suite ) add_executable( custom_http_methods_http_get_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_get.cpp ) target_link_libraries( custom_http_methods_http_get_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_get_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_get_acceptance_test_suite ) add_executable( custom_http_methods_http_put_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_put.cpp ) target_link_libraries( custom_http_methods_http_put_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_put_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_put_acceptance_test_suite ) add_executable( custom_http_methods_http_head_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_head.cpp ) target_link_libraries( custom_http_methods_http_head_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_head_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_head_acceptance_test_suite ) add_executable( custom_http_methods_http_post_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_post.cpp ) target_link_libraries( custom_http_methods_http_post_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_post_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_post_acceptance_test_suite ) add_executable( custom_http_methods_http_trace_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_trace.cpp ) target_link_libraries( custom_http_methods_http_trace_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_trace_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_trace_acceptance_test_suite ) add_executable( custom_http_methods_http_patch_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_patch.cpp ) target_link_libraries( custom_http_methods_http_patch_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_patch_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_patch_acceptance_test_suite ) add_executable( custom_http_methods_http_delete_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_delete.cpp ) target_link_libraries( custom_http_methods_http_delete_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_delete_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_delete_acceptance_test_suite ) add_executable( custom_http_methods_http_connect_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_connect.cpp ) target_link_libraries( custom_http_methods_http_connect_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_connect_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_connect_acceptance_test_suite ) add_executable( custom_http_methods_http_options_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_options.cpp ) target_link_libraries( custom_http_methods_http_options_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_options_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_options_acceptance_test_suite ) add_executable( custom_http_methods_http_invoke_acceptance_test_suite ${SOURCE_DIR}/custom_http_methods/http_invoke.cpp ) target_link_libraries( custom_http_methods_http_invoke_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_http_methods_http_invoke_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_http_methods_http_invoke_acceptance_test_suite ) add_executable( http_method_handlers_http_get_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_get.cpp ) target_link_libraries( http_method_handlers_http_get_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_get_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_get_acceptance_test_suite ) add_executable( http_method_handlers_http_put_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_put.cpp ) target_link_libraries( http_method_handlers_http_put_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_put_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_put_acceptance_test_suite ) add_executable( http_method_handlers_http_head_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_head.cpp ) target_link_libraries( http_method_handlers_http_head_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_head_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_head_acceptance_test_suite ) add_executable( http_method_handlers_http_post_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_post.cpp ) target_link_libraries( http_method_handlers_http_post_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_post_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_post_acceptance_test_suite ) add_executable( http_method_handlers_http_trace_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_trace.cpp ) target_link_libraries( http_method_handlers_http_trace_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_trace_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_trace_acceptance_test_suite ) add_executable( http_method_handlers_http_patch_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_patch.cpp ) target_link_libraries( http_method_handlers_http_patch_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_patch_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_patch_acceptance_test_suite ) add_executable( http_method_handlers_http_delete_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_delete.cpp ) target_link_libraries( http_method_handlers_http_delete_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_delete_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_delete_acceptance_test_suite ) add_executable( http_method_handlers_http_connect_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_connect.cpp ) target_link_libraries( http_method_handlers_http_connect_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_connect_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_connect_acceptance_test_suite ) add_executable( http_method_handlers_http_options_acceptance_test_suite ${SOURCE_DIR}/http_method_handlers/http_options.cpp ) target_link_libraries( http_method_handlers_http_options_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_method_handlers_http_options_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_method_handlers_http_options_acceptance_test_suite ) add_executable( custom_service_authentication_acceptance_test_suite ${SOURCE_DIR}/custom_authentication/service.cpp ) target_link_libraries( custom_service_authentication_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_service_authentication_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_service_authentication_acceptance_test_suite ) add_executable( custom_resource_authentication_acceptance_test_suite ${SOURCE_DIR}/custom_authentication/resource.cpp ) target_link_libraries( custom_resource_authentication_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_resource_authentication_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_resource_authentication_acceptance_test_suite ) add_executable( custom_failed_service_filter_validation_handler_acceptance_test_suite ${SOURCE_DIR}/custom_failed_resource_filter_validation_handler/service.cpp ) target_link_libraries( custom_failed_service_filter_validation_handler_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_failed_service_filter_validation_handler_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_failed_service_filter_validation_handler_acceptance_test_suite ) add_executable( custom_failed_resource_filter_validation_handler_acceptance_test_suite ${SOURCE_DIR}/custom_failed_resource_filter_validation_handler/resource.cpp ) target_link_libraries( custom_failed_resource_filter_validation_handler_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_failed_resource_filter_validation_handler_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_failed_resource_filter_validation_handler_acceptance_test_suite ) add_executable( custom_method_not_allowed_handler_acceptance_test_suite ${SOURCE_DIR}/custom_method_not_allowed_handler/feature.cpp ) target_link_libraries( custom_method_not_allowed_handler_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_method_not_allowed_handler_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_method_not_allowed_handler_acceptance_test_suite ) add_executable( custom_method_not_implemented_handler_acceptance_test_suite ${SOURCE_DIR}/custom_method_not_implemented_handler/feature.cpp ) target_link_libraries( custom_method_not_implemented_handler_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_method_not_implemented_handler_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_method_not_implemented_handler_acceptance_test_suite ) add_executable( custom_resource_not_found_handler_acceptance_test_suite ${SOURCE_DIR}/custom_resource_not_found_handler/feature.cpp ) target_link_libraries( custom_resource_not_found_handler_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_resource_not_found_handler_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_resource_not_found_handler_acceptance_test_suite ) add_executable( path_parameters_acceptance_test_suite ${SOURCE_DIR}/path_parameters/feature.cpp ) target_link_libraries( path_parameters_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( path_parameters_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/path_parameters_acceptance_test_suite ) add_executable( query_parameters_acceptance_test_suite ${SOURCE_DIR}/query_parameters/feature.cpp ) target_link_libraries( query_parameters_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( query_parameters_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/query_parameters_acceptance_test_suite ) add_executable( resource_method_filters_acceptance_test_suite ${SOURCE_DIR}/resource_method_filters/feature.cpp ) target_link_libraries( resource_method_filters_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_method_filters_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_method_filters_acceptance_test_suite ) add_executable( runtime_service_modifications_acceptance_test_suite ${SOURCE_DIR}/runtime_service_modifications/feature.cpp ) target_link_libraries( runtime_service_modifications_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( runtime_service_modifications_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/runtime_service_modifications_acceptance_test_suite ) add_executable( publish_duplicate_resources_acceptance_test_suite ${SOURCE_DIR}/publish_duplicate_resources/feature.cpp ) target_link_libraries( publish_duplicate_resources_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( publish_duplicate_resources_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/publish_duplicate_resources_acceptance_test_suite ) add_executable( custom_service_error_handling_acceptance_test_suite ${SOURCE_DIR}/custom_error_handling/service.cpp ) target_link_libraries( custom_service_error_handling_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_service_error_handling_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_service_error_handling_acceptance_test_suite ) add_executable( custom_resource_error_handling_acceptance_test_suite ${SOURCE_DIR}/custom_error_handling/resource.cpp ) target_link_libraries( custom_resource_error_handling_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_resource_error_handling_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_resource_error_handling_acceptance_test_suite ) add_executable( case_sensitive_service_acceptance_test_suite ${SOURCE_DIR}/service_case_sensitivity/case_sensitive.cpp ) target_link_libraries( case_sensitive_service_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( case_sensitive_service_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/case_sensitive_service_acceptance_test_suite ) add_executable( case_insensitive_service_acceptance_test_suite ${SOURCE_DIR}/service_case_sensitivity/case_insensitive.cpp ) target_link_libraries( case_insensitive_service_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( case_insensitive_service_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/case_insensitive_service_acceptance_test_suite ) add_executable( service_rules_engine_acceptance_test_suite ${SOURCE_DIR}/rules_engine/service.cpp ) target_link_libraries( service_rules_engine_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( service_rules_engine_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/service_rules_engine_acceptance_test_suite ) add_executable( resource_rules_engine_acceptance_test_suite ${SOURCE_DIR}/rules_engine/resource.cpp ) target_link_libraries( resource_rules_engine_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_rules_engine_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_rules_engine_acceptance_test_suite ) add_executable( mixed_rules_engine_acceptance_test_suite ${SOURCE_DIR}/rules_engine/mixed.cpp ) target_link_libraries( mixed_rules_engine_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( mixed_rules_engine_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/mixed_rules_engine_acceptance_test_suite ) add_executable( http_client_connect_acceptance_test_suite ${SOURCE_DIR}/http_client/connect.cpp ) target_link_libraries( http_client_connect_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_client_connect_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_client_connect_acceptance_test_suite ) add_executable( http_client_fetch_acceptance_test_suite ${SOURCE_DIR}/http_client/fetch.cpp ) target_link_libraries( http_client_fetch_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_client_fetch_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_client_fetch_acceptance_test_suite ) add_executable( http_client_close_acceptance_test_suite ${SOURCE_DIR}/http_client/close.cpp ) target_link_libraries( http_client_close_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_client_close_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_client_close_acceptance_test_suite ) add_executable( http_client_async_acceptance_test_suite ${SOURCE_DIR}/http_client/async.cpp ) target_link_libraries( http_client_async_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_client_async_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_client_async_acceptance_test_suite ) add_executable( http_client_keep_alive_acceptance_test_suite ${SOURCE_DIR}/http_client/keep_alive.cpp ) target_link_libraries( http_client_keep_alive_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_client_keep_alive_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_client_keep_alive_acceptance_test_suite ) add_executable( custom_status_message_acceptance_test_suite ${SOURCE_DIR}/custom_status_message/feature.cpp ) target_link_libraries( custom_status_message_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( custom_status_message_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/custom_status_message_acceptance_test_suite ) add_executable( signal_handling_acceptance_test_suite ${SOURCE_DIR}/signal_handling/feature.cpp ) target_link_libraries( signal_handling_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( signal_handling_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/signal_handling_acceptance_test_suite ) add_executable( service_connection_timeout_acceptance_test_suite ${SOURCE_DIR}/connection_timeout/server.cpp ) target_link_libraries( service_connection_timeout_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( service_connection_timeout_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/service_connection_timeout_acceptance_test_suite ) add_executable( client_connection_timeout_acceptance_test_suite ${SOURCE_DIR}/connection_timeout/client.cpp ) target_link_libraries( client_connection_timeout_acceptance_test_suite ${CMAKE_PROJECT_NAME} ) add_test( client_connection_timeout_acceptance_test_suite ${EXECUTABLE_OUTPUT_PATH}/client_connection_timeout_acceptance_test_suite ) restbed-4.0/test/acceptance/source/000077500000000000000000000000001270400672200173525ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/connection_timeout/000077500000000000000000000000001270400672200232575ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/connection_timeout/client.cpp000066400000000000000000000047361270400672200252530ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include #include //System Namespaces using std::thread; using std::string; using std::vector; using std::shared_ptr; using std::make_shared; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces using asio::ip::tcp; using asio::connect; using asio::io_service; using asio::socket_base; void get_handler( const shared_ptr< Session > session ) { session->sleep_for( seconds( 4 ), [ ]( const shared_ptr< Session > session ) { if ( session->is_open( ) ) { session->close( 200 ); } } ); } SCENARIO( "validate connection timeout", "[socket]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_connection_timeout( seconds( 30 ) ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I create a service with a delayed 'GET' handler" ) { WHEN( "I perform a request with a connection timeout of '2' seconds" ) { auto configuration = make_shared< Settings >( ); configuration->set_connection_timeout( seconds( 2 ) ); auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource" ); auto response = Http::sync( request, configuration ); THEN( "I should see the service has closed the socket" ) { REQUIRE( response->get_status_code( ) == 0 ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/connection_timeout/server.cpp000066400000000000000000000112241270400672200252710ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include #include //System Namespaces using std::thread; using std::string; using std::vector; using std::shared_ptr; using std::make_shared; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces using asio::ip::tcp; using asio::connect; using asio::io_service; using asio::socket_base; void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } SCENARIO( "validate connection timeout after connect", "[socket]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_connection_timeout( seconds( 2 ) ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I create a service with a socket connection timeout of '2' seconds" ) { WHEN( "I establish a network connection and wait '4' seconds" ) { io_service io_service; asio::ip::tcp::socket socket( io_service ); asio::ip::tcp::endpoint endpoint( asio::ip::address::from_string( "127.0.0.1" ), 1984 ); socket.connect( endpoint ); REQUIRE( true == socket.is_open( ) ); std::this_thread::sleep_for( seconds( 4 ) ); THEN( "I should see the peer close the socket" ) { uint8_t data; asio::error_code error; socket.receive( asio::buffer( &data, 1 ), socket_base::message_peek, error ); REQUIRE( error == asio::error::eof ); } socket.close( ); } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "validate connection timeout after partial request", "[socket]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_connection_timeout( seconds( 2 ) ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I create a service with a socket connection timeout of '2' seconds" ) { AND_THEN( "I perform a partial HTTP request" ) { io_service io_service; asio::ip::tcp::socket socket( io_service ); asio::ip::tcp::endpoint endpoint( asio::ip::address::from_string( "127.0.0.1" ), 1984 ); socket.connect( endpoint ); REQUIRE( true == socket.is_open( ) ); string request = "GET /resource HTTP/1.1\r\n"; socket.send( asio::buffer( request, request.length( ) ) ); WHEN( "I wait '4' seconds" ) { std::this_thread::sleep_for( seconds( 4 ) ); THEN( "I should see the peer close the socket" ) { uint8_t data; asio::error_code error; socket.receive( asio::buffer( &data, 1 ), socket_base::message_peek, error ); REQUIRE( error == asio::error::eof ); } socket.close( ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_authentication/000077500000000000000000000000001270400672200237635ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_authentication/resource.cpp000066400000000000000000000221021270400672200263130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void resource_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" ) { session->close( FORBIDDEN, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } ); } else { callback( session ); } } void service_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation.empty( ) ) { session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } ); } else { callback( session ); } } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } ); } SCENARIO( "custom resource authentication", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_method_handler ); resource->set_authentication_handler( resource_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_authentication_handler( service_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a secure resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform an authorised HTTP 'GET' request to '/resources/1' with header 'Authorization: Basic Q29ydnVzb2Z0OkdsYXNnb3c='" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Authorization", "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Password Protected Hello, World!'" ) { auto actual = Http::fetch( 32, response ); Bytes expectation { 'P', 'a', 's', 's', 'w', 'o', 'r', 'd', ' ', 'P', 'r', 'o', 't', 'e', 'c', 't', 'e', 'd', ' ', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '32'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "32" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an unauthorised HTTP 'GET' request to '/resources/1' with header 'Authorization: Basic Q29y28fsoOkdsYXNnb3c'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Authorization", "Basic Q29y28fsoOkdsYXNnb3c" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '403' (Forbidden) status code" ) { REQUIRE( 403 == response->get_status_code( ) ); REQUIRE( "Forbidden" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } AND_THEN( "I should see a 'WWW-Authenticate' header value of 'Basic realm=\"restbed\"'" ) { auto header = headers.find( "WWW-Authenticate" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "Basic realm=\"restbed\"" == headers.find( "WWW-Authenticate" )->second ); } } WHEN( "I perform an unauthorised HTTP 'GET' request to '/resources/1' without an 'Authorization' header" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '401' (Unauthorized) status code" ) { REQUIRE( 401 == response->get_status_code( ) ); REQUIRE( "Unauthorized" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } AND_THEN( "I should see a 'WWW-Authenticate' header value of 'Basic realm=\"restbed\"'" ) { auto header = headers.find( "WWW-Authenticate" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "Basic realm=\"restbed\"" == headers.find( "WWW-Authenticate" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_authentication/service.cpp000066400000000000000000000151021270400672200261260ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void authentication_handler( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) { auto authorisation = session->get_request( )->get_header( "Authorization" ); if ( authorisation not_eq "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" ) { session->close( UNAUTHORIZED, { { "WWW-Authenticate", "Basic realm=\"restbed\"" } } ); } else { callback( session ); } } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Password Protected Hello, World!", { { "Content-Length", "32" } } ); } SCENARIO( "custom service authentication", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_authentication_handler( authentication_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a secure resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform an authorised HTTP 'GET' request to '/resources/1' with header 'Authorization: Basic Q29ydnVzb2Z0OkdsYXNnb3c='" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Authorization", "Basic Q29ydnVzb2Z0OkdsYXNnb3c=" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Password Protected Hello, World!'" ) { auto actual = Http::fetch( 32, response ); Bytes expectation { 'P', 'a', 's', 's', 'w', 'o', 'r', 'd', ' ', 'P', 'r', 'o', 't', 'e', 'c', 't', 'e', 'd', ' ', 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '32'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "32" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an unauthorised HTTP 'GET' request to '/resources/1' with header 'Authorization: Basic Q29ydndsYXNnb3c='" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Authorization", "Basic Q29ydndsYXNnb3c=" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '401' (Unauthorized) status code" ) { REQUIRE( 401 == response->get_status_code( ) ); REQUIRE( "Unauthorized" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } AND_THEN( "I should see a 'WWW-Authenticate' header value of 'Basic realm=\"restbed\"'" ) { auto header = headers.find( "WWW-Authenticate" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "Basic realm=\"restbed\"" == headers.find( "WWW-Authenticate" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_error_handling/000077500000000000000000000000001270400672200237415ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_error_handling/resource.cpp000066400000000000000000000077261270400672200263100ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::exception; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void resource_error_handler( const int, const exception&, const shared_ptr< Session > session ) { session->close( 22, "overridden service handler!", { { "Content-Length", "27" } } ); } void service_error_handler( const int, const exception&, const shared_ptr< Session > session ) { session->close( 0, "I see nothing!", { { "Content-Length", "14" } } ); } void faulty_method_handler( const shared_ptr< Session > ) { throw SERVICE_UNAVAILABLE; } SCENARIO( "custom service error handler", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", faulty_method_handler ); resource->set_error_handler( resource_error_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_error_handler( service_error_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a faulty resource and set a resource error handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '0' (No Appropriate Status Message Found) status code" ) { REQUIRE( 22 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'I see nothing!'" ) { auto actual = Http::fetch( 27, response ); Bytes expectation { 'o', 'v', 'e', 'r', 'r', 'i', 'd', 'd', 'e', 'n', ' ', 's', 'e', 'r', 'v', 'i', 'c', 'e', ' ', 'h', 'a', 'n', 'd', 'l', 'e', 'r', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '27'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "27" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_error_handling/service.cpp000066400000000000000000000072131270400672200261100ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::exception; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void error_handler( const int, const exception&, const shared_ptr< Session > session ) { session->close( 0, "I see nothing!", { { "Content-Length", "14" } } ); } void faulty_method_handler( const shared_ptr< Session > ) { throw SERVICE_UNAVAILABLE; } SCENARIO( "custom service error handler", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", faulty_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_error_handler( error_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a faulty resources and set a service error handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '0' (No Appropriate Status Message Found) status code" ) { REQUIRE( 0 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'I see nothing!'" ) { auto actual = Http::fetch( 14, response ); Bytes expectation { 'I', ' ', 's', 'e', 'e', ' ', 'n', 'o', 't', 'h', 'i', 'n', 'g', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '14'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "14" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_failed_resource_filter_validation_handler/000077500000000000000000000000001270400672200313535ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_failed_resource_filter_validation_handler/resource.cpp000066400000000000000000000147041270400672200337140ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void resource_failed_filter_validation_handler( const shared_ptr< Session > session ) { session->close( -949, "Yikes! Filters Mismatched.", { { "Content-Length", "26" } } ); } void service_failed_filter_validation_handler( const shared_ptr< Session > session ) { session->close( 500 ); } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "32" } } ); } SCENARIO( "custom resource failed filter validation handler", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", { { "Content-Type", "application/csv" } }, get_method_handler ); resource->set_failed_filter_validation_handler( resource_failed_filter_validation_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_failed_filter_validation_handler( service_failed_filter_validation_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform an valid HTTP 'GET' request to '/resources/1' with header 'Content-Type: application/csv'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '32'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "32" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an invalid HTTP 'GET' request to '/resources/1' with header 'Content-Type: application/yaml'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/yaml" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '-949' (No Appropriate Status Message Found) status code" ) { REQUIRE( -949 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Yikes! Filters Mismatched.'" ) { auto actual = Http::fetch( 26, response ); Bytes expectation { 'Y', 'i', 'k', 'e', 's', '!', ' ', 'F', 'i', 'l', 't', 'e', 'r', 's', ' ', 'M', 'i', 's', 'm', 'a', 't', 'c', 'h', 'e', 'd', '.' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '26'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "26" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_failed_resource_filter_validation_handler/service.cpp000066400000000000000000000143351270400672200335250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void failed_filter_validation_handler( const shared_ptr< Session > session ) { session->close( -949, "Yikes! Filters Mismatched.", { { "Content-Length", "26" } } ); } void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "32" } } ); } SCENARIO( "custom resource failed filter validation handler", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", { { "Content-Type", "application/csv" } }, get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_failed_filter_validation_handler( failed_filter_validation_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform an valid HTTP 'GET' request to '/resources/1' with header 'Content-Type: application/csv'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '32'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "32" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an invalid HTTP 'GET' request to '/resources/1' with header 'Content-Type: application/yaml'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/yaml" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '-949' (No Appropriate Status Message Found) status code" ) { REQUIRE( -949 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Yikes! Filters Mismatched.'" ) { auto actual = Http::fetch( 26, response ); Bytes expectation { 'Y', 'i', 'k', 'e', 's', '!', ' ', 'F', 'i', 'l', 't', 'e', 'r', 's', ' ', 'M', 'i', 's', 'm', 'a', 't', 'c', 'h', 'e', 'd', '.' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '26'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "26" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/000077500000000000000000000000001270400672200234465ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_http_methods/http_connect.cpp000066400000000000000000000061531270400672200266470ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'CONNECT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "CONNECT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_delete.cpp000066400000000000000000000061511270400672200264560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'DELETE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "DELETE" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_get.cpp000066400000000000000000000061431270400672200257740ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_head.cpp000066400000000000000000000061451270400672200261200ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'HEAD' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "HEAD" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_invoke.cpp000066400000000000000000000066171270400672200265160ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'INVOKE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "INVOKE" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_options.cpp000066400000000000000000000061531270400672200267110ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'OPTIONS' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "OPTIONS" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_patch.cpp000066400000000000000000000061471270400672200263200ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'PATCH' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PATCH" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_post.cpp000066400000000000000000000061451270400672200262040ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'POST' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "POST" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_put.cpp000066400000000000000000000061431270400672200260250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PUT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_http_methods/http_trace.cpp000066400000000000000000000061471270400672200263170ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void invoke_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing custom HTTP methods", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "INVOKE", invoke_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'INVOKE' method handler" ) { WHEN( "I perform a HTTP 'TRACE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "TRACE" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_method_not_allowed_handler/000077500000000000000000000000001270400672200263105ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_method_not_allowed_handler/feature.cpp000066400000000000000000000077021270400672200304550ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void method_not_allowed_handler( const shared_ptr< Session > session ) { session->close( -400, "Banned Method", { { "Content-Length", "13" } } ); } void jack_of_all_trades_method_handler( const shared_ptr< Session > session ) { session->close( OK ); } SCENARIO( "custom resource method not allowed handler", "[resource]" ) { auto resource_one = make_shared< Resource >( ); resource_one->set_path( "/resources/1" ); resource_one->set_method_handler( "GET", jack_of_all_trades_method_handler ); auto resource_two = make_shared< Resource >( ); resource_two->set_path( "/resources/2" ); resource_two->set_method_handler( "PUT", jack_of_all_trades_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource_one ); service.publish( resource_two ); service.set_method_not_allowed_handler( method_not_allowed_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish two resources with differing HTTP methods" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PUT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '-400' (No Appropriate Status Message Found) status code" ) { REQUIRE( -400 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Banned Method'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'B', 'a', 'n', 'n', 'e', 'd', ' ', 'M', 'e', 't', 'h', 'o', 'd' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_method_not_implemented_handler/000077500000000000000000000000001270400672200271645ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_method_not_implemented_handler/feature.cpp000066400000000000000000000071001270400672200313210ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void method_not_implemented_handler( const shared_ptr< Session > session ) { session->close( -232, "Banned Method", { { "Content-Length", "13" } } ); } SCENARIO( "custom resource method not implemented handler", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_method_not_implemented_handler( method_not_implemented_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' without a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PUT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '-232' (No Appropriate Status Message Found) status code" ) { REQUIRE( -232 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Banned Method'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'B', 'a', 'n', 'n', 'e', 'd', ' ', 'M', 'e', 't', 'h', 'o', 'd' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_resource_not_found_handler/000077500000000000000000000000001270400672200263435ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_resource_not_found_handler/feature.cpp000066400000000000000000000065261270400672200305130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void not_found_handler( const shared_ptr< Session > session ) { session->close( 0, "I see nothing!", { { "Content-Length", "14" } } ); } SCENARIO( "custom resource not found handler", "[resource]" ) { auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.set_not_found_handler( not_found_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish no resources" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PUT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '0' (No Appropriate Status Message Found) status code" ) { REQUIRE( 0 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'I see nothing!'" ) { auto actual = Http::fetch( 14, response ); Bytes expectation { 'I', ' ', 's', 'e', 'e', ' ', 'n', 'o', 't', 'h', 'i', 'n', 'g', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '14'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "14" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/custom_status_message/000077500000000000000000000000001270400672200237735ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/custom_status_message/feature.cpp000066400000000000000000000103651270400672200261370ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::exception; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void negative_handler( const shared_ptr< Session > session ) { session->close( -342 ); } void positive_handler( const shared_ptr< Session > session ) { session->close( 9834 ); } SCENARIO( "custom positive response status", "[settings]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", positive_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); auto status_messages = settings->get_status_messages( ); status_messages[ 9834 ] = "Positive Status Code Message"; settings->set_status_messages( status_messages ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resources with a custom status of '9834' (Positive Status Code Message)" ) { WHEN( "I perform a HTTP 'GET' request to '/resource'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '9834' (Positive Status Code Message) status code" ) { REQUIRE( 9834 == response->get_status_code( ) ); REQUIRE( "Positive Status Code Message" == response->get_status_message( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "custom negative response status", "[settings]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", negative_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); auto status_messages = settings->get_status_messages( ); status_messages[ -342 ] = "Negative Status Code Message"; settings->set_status_messages( status_messages ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resources with a custom status of '-342' (Negative Status Code Message)" ) { WHEN( "I perform a HTTP 'GET' request to '/resource'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '-342' (Negative Status Code Message) status code" ) { REQUIRE( -342 == response->get_status_code( ) ); REQUIRE( "Negative Status Code Message" == response->get_status_message( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_client/000077500000000000000000000000001270400672200216675ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/http_client/async.cpp000066400000000000000000000116711270400672200235160ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::stoi; using std::thread; using std::string; using std::multimap; using std::to_string; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "establish connection to reachable server", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I have started a service" ) { WHEN( "I perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto future = Http::async( request, [ &service ]( const shared_ptr< Request >, const shared_ptr< Response > response ) { THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } ); auto response = future.get( ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } service.stop( ); } } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_client/close.cpp000066400000000000000000000142471270400672200235100ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::stoi; using std::thread; using std::string; using std::multimap; using std::to_string; using std::shared_ptr; using std::make_shared; using std::chrono::milliseconds; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->sleep_for( milliseconds( 5 ), [ ]( const shared_ptr< Session > session ) { session->close( 200, { { "Connection", "close" } } ); } ); } SCENARIO( "close active request", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I have started a service and perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto response = Http::sync( request ); WHEN( "I close the connection before seeing a response" ) { Http::close( request ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( request ) == false ); REQUIRE( Http::is_open( response ) == false ); REQUIRE( Http::is_closed( request ) == true ); REQUIRE( Http::is_closed( response ) == true ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "close active response", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I have started a service and perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto response = Http::sync( request ); WHEN( "I close the connection before seeing a response" ) { Http::close( response ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( request ) == false ); REQUIRE( Http::is_open( response ) == false ); REQUIRE( Http::is_closed( request ) == true ); REQUIRE( Http::is_closed( response ) == true ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "close inactive request", "[client]" ) { GIVEN( "I have not started a service and not perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); WHEN( "I close the request socket" ) { Http::close( request ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( request ) == false ); REQUIRE( Http::is_closed( request ) == true ); } } } } SCENARIO( "close inactive response", "[client]" ) { GIVEN( "I have a blank response" ) { auto response = make_shared< Response >( ); WHEN( "I close the response socket" ) { Http::close( response ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( response ) == false ); REQUIRE( Http::is_closed( response ) == true ); } } } } SCENARIO( "close null request", "[client]" ) { GIVEN( "I have a null request" ) { WHEN( "I close the request socket" ) { shared_ptr< Request > request = nullptr; Http::close( request ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( request ) == false ); REQUIRE( Http::is_closed( request ) == true ); } } } } SCENARIO( "close null response", "[client]" ) { GIVEN( "I have a null response" ) { WHEN( "I close the response socket" ) { shared_ptr< Response > response = nullptr; Http::close( response ); THEN( "I should see the connection closed" ) { REQUIRE( Http::is_open( response ) == false ); REQUIRE( Http::is_closed( response ) == true ); } } } } restbed-4.0/test/acceptance/source/http_client/connect.cpp000066400000000000000000000124401270400672200240250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::stoi; using std::thread; using std::string; using std::multimap; using std::to_string; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "establish connection to reachable server", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I have started a service" ) { WHEN( "I perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "establish connection to unreachable server", "[client]" ) { GIVEN( "I have not started a service" ) { WHEN( "I perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_protocol( "HTTP" ); request->set_version( 1.1 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '0' (Error) status code" ) { REQUIRE( 0 == response->get_status_code( ) ); REQUIRE( "Error" == response->get_status_message( ) ); } AND_THEN( "I should see a 'HTTP' response protocol" ) { REQUIRE( "HTTP" == response->get_protocol( ) ); } AND_THEN( "I should see a '1.1' response version" ) { REQUIRE( 1.1 == response->get_version( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Content-Type' header value of 'text/plain; utf-8'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain; utf-8" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value greater than zero" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( stoi( headers.find( "Content-Length" )->second ) > 0 ); } AND_THEN( "I should see a response body detailing the error" ) { REQUIRE( response->get_body( ).size( ) > 0 ); } } } } restbed-4.0/test/acceptance/source/http_client/fetch.cpp000066400000000000000000000100521270400672200234620ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; using std::runtime_error; using std::invalid_argument; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "fetch response body", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resource' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request and fetch the response body" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } SCENARIO( "invoke fetch on default response", "[client]" ) { GIVEN( "I create a default response instance" ) { auto response = make_shared< Response >( ); WHEN( "I invoke fetch" ) { THEN( "I should see an invalid_argument" ) { REQUIRE_THROWS_AS( Http::fetch( 13, response ), invalid_argument ); } } } } SCENARIO( "invoke fetch on null response", "[client]" ) { GIVEN( "I create a null response instance" ) { WHEN( "I invoke fetch" ) { THEN( "I should see an invalid_argument" ) { REQUIRE_THROWS_AS( Http::fetch( 13, nullptr ), invalid_argument ); } } } } restbed-4.0/test/acceptance/source/http_client/keep_alive.cpp000066400000000000000000000123651270400672200245060ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::stoi; using std::thread; using std::string; using std::multimap; using std::to_string; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { if ( session->get_request( )->get_header( "Connection" ) == "keep-alive" ) { session->yield( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "keep-alive" } } ); } else { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } } SCENARIO( "http connection keep-alive", "[client]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I have started a service" ) { WHEN( "I perform a HTTP 'GET' request" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_header( "Connection", "keep-alive" ); request->set_path( "/resource" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'keep-alive'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "keep-alive" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } AND_THEN( "I should see an open connection" ) { REQUIRE( Http::is_open( request ) ); } response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'keep-alive'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "keep-alive" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } Http::close( request ); } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/000077500000000000000000000000001270400672200235515ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/http_method_handlers/http_connect.cpp000066400000000000000000000065661270400672200267620ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void connect_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "CONNECT", connect_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'CONNECT' method handler" ) { WHEN( "I perform a HTTP 'CONNECT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "CONNECT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); }restbed-4.0/test/acceptance/source/http_method_handlers/http_delete.cpp000066400000000000000000000065601270400672200265650ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void delete_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "DELETE", delete_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'DELETE' method handler" ) { WHEN( "I perform a HTTP 'DELETE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "DELETE" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); }restbed-4.0/test/acceptance/source/http_method_handlers/http_get.cpp000066400000000000000000000065371270400672200261060ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "GET" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_head.cpp000066400000000000000000000065451270400672200262270ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void head_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "HEAD", head_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'HEAD' method handler" ) { WHEN( "I perform a HTTP 'HEAD' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "HEAD" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_options.cpp000066400000000000000000000065671270400672200270250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void options_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "OPTIONS", options_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'OPTIONS' method handler" ) { WHEN( "I perform a HTTP 'OPTIONS' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "OPTIONS" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_patch.cpp000066400000000000000000000065531270400672200264240ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void patch_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "PATCH", patch_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'PATCH' method handler" ) { WHEN( "I perform a HTTP 'PATCH' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PATCH" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_post.cpp000066400000000000000000000065451270400672200263130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void post_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "POST", post_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'POST' method handler" ) { WHEN( "I perform a HTTP 'POST' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "POST" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_put.cpp000066400000000000000000000065371270400672200261370ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void put_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "PUT", put_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'PUT' method handler" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "PUT" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/http_method_handlers/http_trace.cpp000066400000000000000000000065531270400672200264230ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void trace_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" }, { "Connection", "close" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "TRACE", trace_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'TRACE' method handler" ) { WHEN( "I perform a HTTP 'TRACE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_method( "TRACE" ); request->set_path( "/resources/1" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/path_parameters/000077500000000000000000000000001270400672200225315ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/path_parameters/feature.cpp000066400000000000000000000060611270400672200246730ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); int id = 0; request->get_path_parameter( "id", id ); REQUIRE( 100 == id ); REQUIRE( "events" == request->get_path_parameter( "name" ) ); session->close( 204 ); } SCENARIO( "request path parameters", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/queues/{name: [a-z]*}/messages/{id: [0-9]*}" ); resource->set_method_handler( "GET", get_handler ); resource->set_default_header( "Connection", "close" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/queues/{name: [a-z]*}/messages/{id: [0-9]*}' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/queues/events/messages/100'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/queues/events/messages/100" ); auto response = Http::sync( request ); THEN( "I should see a '204' (No Content) status code" ) { REQUIRE( 204 == response->get_status_code( ) ); REQUIRE( "No Content" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto headers = response->get_headers( ); auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publish_duplicate_resources/000077500000000000000000000000001270400672200251445ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/publish_duplicate_resources/feature.cpp000066400000000000000000000022731270400672200273070ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::string; using std::shared_ptr; using std::make_shared; using std::invalid_argument; //Project Namespaces using namespace restbed; //External Namespaces SCENARIO( "publishing duplicate resources", "[service]" ) { GIVEN( "I publish a resource at '/resources/1'" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); Service service; service.publish( resource ); WHEN( "I attempt to publish another resource at '/resources/1'" ) { THEN( "I should see an invalid argument error of 'Resource would pollute namespace. Please ensure all published resources have unique paths.'" ) { REQUIRE_THROWS_AS( service.publish( resource ), invalid_argument ); } } service.stop( ); } } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/000077500000000000000000000000001270400672200260365ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_connect.cpp000066400000000000000000000114661270400672200312420ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'CONNECT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "CONNECT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'CONNECT' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "CONNECT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_delete.cpp000066400000000000000000000114621270400672200310470ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'DELETE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "DELETE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'DELETE' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "DELETE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_get.cpp000066400000000000000000000125621270400672200303660ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "GET" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform a HTTP 'GET' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "GET" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_head.cpp000066400000000000000000000114521270400672200305050ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'HEAD' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "HEAD" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'HEAD' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "HEAD" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_options.cpp000066400000000000000000000114661270400672200313040ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'OPTIONS' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "OPTIONS" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'OPTIONS' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "OPTIONS" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_patch.cpp000066400000000000000000000114561270400672200307070ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'PATCH' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "PATCH" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'PATCH' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "PATCH" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_post.cpp000066400000000000000000000114521270400672200305710ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'POST' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "POST" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'POST' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "POST" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_put.cpp000066400000000000000000000114461270400672200304170ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "PUT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'PUT' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "PUT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_multi_path_resources/http_trace.cpp000066400000000000000000000114561270400672200307060ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing multi path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_paths( { "/resources/1", "/resources/one" } ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' and '/resources/one' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'TRACE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "TRACE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } WHEN( "I perform a HTTP 'TRACE' request to '/resources/one'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/one" ); request->set_method( "TRACE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/000077500000000000000000000000001270400672200261655ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_connect.cpp000066400000000000000000000061411270400672200313630ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'CONNECT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "CONNECT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_delete.cpp000066400000000000000000000061371270400672200312010ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'DELETE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "DELETE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_get.cpp000066400000000000000000000065771270400672200305260ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "GET" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_head.cpp000066400000000000000000000061331270400672200306340ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'HEAD' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "HEAD" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_options.cpp000066400000000000000000000061411270400672200314250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'OPTIONS' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "OPTIONS" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_patch.cpp000066400000000000000000000061351270400672200310340ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'PATCH' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "PATCH" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_post.cpp000066400000000000000000000061331270400672200307200ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'POST' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "POST" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_put.cpp000066400000000000000000000061311270400672200305410ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'PUT' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "PUT" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/publishing_single_path_resources/http_trace.cpp000066400000000000000000000061351270400672200310330ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "publishing single path resources", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'TRACE' request to '/resources/1'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1" ); request->set_method( "TRACE" ); auto response = Http::sync( request ); THEN( "I should see a '501' (Not Implemented) status code" ) { REQUIRE( 501 == response->get_status_code( ) ); REQUIRE( "Not Implemented" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } multimap< string, string > headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header value" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/query_parameters/000077500000000000000000000000001270400672200227425ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/query_parameters/feature.cpp000066400000000000000000000057241270400672200251110ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); int id = 0; request->get_query_parameter( "id", id ); REQUIRE( 100 == id ); REQUIRE( "events" == request->get_query_parameter( "name" ) ); session->close( 204 ); } SCENARIO( "request query parameters", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/1" ); resource->set_method_handler( "GET", get_handler ); resource->set_default_header( "Connection", "close" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1?id=100&name=events'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1?id=100&name=events" ); auto response = Http::sync( request ); THEN( "I should see a '204' (No Content) status code" ) { REQUIRE( 204 == response->get_status_code( ) ); REQUIRE( "No Content" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto headers = response->get_headers( ); auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/resource_method_filters/000077500000000000000000000000001270400672200242715ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/resource_method_filters/feature.cpp000066400000000000000000000073121270400672200264330ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::function; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void xml_method_handler( const shared_ptr< Session > session ) { session->close( 1 ); } void json_method_handler( const shared_ptr< Session > session ) { session->close( 2 ); } SCENARIO( "resource method filters", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resource" ); resource->set_method_handler( "GET", { { "Content-Type", "application/xml" } }, xml_method_handler ); resource->set_method_handler( "GET", { { "Content-Type", "application/json" } }, json_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource with 'Content-Type' header filters of 'application/json' and 'application/xml'" ) { WHEN( "I perform a HTTP 'GET' request to '/resource' with header 'Content-Type: application/xml'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/xml" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '1' (No Appropriate Status Message Found) status code" ) { REQUIRE( 1 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } } WHEN( "I perform a HTTP 'GET' request to '/resource' with header 'Content-Type: application/json'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/json" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '2' (No Appropriate Status Message Found) status code" ) { REQUIRE( 2 == response->get_status_code( ) ); REQUIRE( "No Appropriate Status Message Found" == response->get_status_message( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/rules_engine/000077500000000000000000000000001270400672200220315ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/rules_engine/content_length_rule.hpp000066400000000000000000000036341270400672200266120ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include //External Includes //System Namespaces using std::string; using std::function; using std::shared_ptr; //Project Namespaces using namespace restbed; //External Namespaces class ContentLengthRule : public Rule { public: ContentLengthRule( void ) : Rule( ) { return; } virtual ~ContentLengthRule( void ) { return; } bool condition( const shared_ptr< Session > session ) final override { return session->get_request( )->get_method( String::uppercase ) == "POST"; } void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override { const auto request = session->get_request( ); if ( not request->has_header( "Content-Length" ) ) { session->close( LENGTH_REQUIRED, "Length Required.", { { "Content-Length", "16" }, { "Content-Type", "text/plain" } } ); } else { size_t length = 0; request->get_header( "Content-Length", length ); session->fetch( length, [ length, callback ]( const shared_ptr< Session > session, const Bytes & body ) { if ( length not_eq body.size( ) ) { session->close( LENGTH_REQUIRED, "Length Required.", { { "Content-Length", "16" }, { "Content-Type", "text/plain" } } ); } else { callback( session ); } } ); } } }; restbed-4.0/test/acceptance/source/rules_engine/content_type_rule.hpp000066400000000000000000000026041270400672200263060ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include //External Includes //System Namespaces using std::string; using std::function; using std::shared_ptr; //Project Namespaces using namespace restbed; //External Namespaces class ContentTypeRule : public Rule { public: ContentTypeRule( void ) : Rule( ) { return; } virtual ~ContentTypeRule( void ) { return; } bool condition( const shared_ptr< Session > session ) final override { return session->get_request( )->has_header( "Content-Type" ); } void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override { const auto request = session->get_request( ); if ( request->get_header( "Content-Type", String::lowercase ) not_eq "application/csv" ) { session->close( UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type, must be 'application/csv'.", { { "Content-Length", "50" }, { "Content-Type", "text/plain" } } ); } else { callback( session ); } } }; restbed-4.0/test/acceptance/source/rules_engine/mixed.cpp000066400000000000000000000274211270400672200236510ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include #include "content_type_rule.hpp" #include "content_length_rule.hpp" //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::function; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "resource rules engine", "[resource]" ) { const auto content_type = make_shared< ContentTypeRule >( ); auto resource = make_shared< Resource >( ); resource->set_path( "/resources" ); resource->set_method_handler( "POST", get_method_handler ); resource->add_rule( content_type ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; const auto content_length = make_shared< ContentLengthRule >( ); Service service; service.publish( resource ); service.add_rule( content_length ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources' with a HTTP 'POST' method handler" ) { WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/json, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/json" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '415' (Unsupported Media Type) status code" ) { REQUIRE( 415 == response->get_status_code( ) ); REQUIRE( "Unsupported Media Type" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Unsupported Media Type, must be 'application/csv'.'" ) { auto actual = Http::fetch( 50, response ); string body( actual.begin( ), actual.end( ) ); REQUIRE( body == "Unsupported Media Type, must be 'application/csv'." ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '50'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "50" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 4' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "4" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '411' (Length Required) status code" ) { REQUIRE( 411 == response->get_status_code( ) ); REQUIRE( "Length Required" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Length Required.'" ) { auto actual = Http::fetch( 16, response ); Bytes expectation { 'L', 'e', 'n', 'g', 't', 'h', ' ', 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd', '.' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '16'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "16" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/rules_engine/resource.cpp000066400000000000000000000274161270400672200243760ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include #include "content_type_rule.hpp" #include "content_length_rule.hpp" //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "resource rules engine", "[resource]" ) { shared_ptr< thread > worker = nullptr; const auto content_type = make_shared< ContentTypeRule >( ); const auto content_length = make_shared< ContentLengthRule >( ); auto resource = make_shared< Resource >( ); resource->set_path( "/resources" ); resource->set_method_handler( "POST", get_method_handler ); resource->add_rule( content_type ); resource->add_rule( content_length ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources' with a HTTP 'POST' method handler" ) { WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/json, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/json" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '415' (Unsupported Media Type) status code" ) { REQUIRE( 415 == response->get_status_code( ) ); REQUIRE( "Unsupported Media Type" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Unsupported Media Type, must be 'application/csv'.'" ) { auto actual = Http::fetch( 50, response ); string body( actual.begin( ), actual.end( ) ); REQUIRE( body == "Unsupported Media Type, must be 'application/csv'." ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '50'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "50" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 4' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "4" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '411' (Length Required) status code" ) { REQUIRE( 411 == response->get_status_code( ) ); REQUIRE( "Length Required" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Length Required.'" ) { auto actual = Http::fetch( 16, response ); Bytes expectation { 'L', 'e', 'n', 'g', 't', 'h', ' ', 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd', '.' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '16'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "16" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/rules_engine/service.cpp000066400000000000000000000274101270400672200242010ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include #include "content_type_rule.hpp" #include "content_length_rule.hpp" //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_method_handler( const shared_ptr< Session > session ) { session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "service rules engine", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources" ); resource->set_method_handler( "POST", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; const auto content_type = make_shared< ContentTypeRule >( ); const auto content_length = make_shared< ContentLengthRule >( ); Service service; service.publish( resource ); service.add_rule( content_type ); service.add_rule( content_length ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources' with a HTTP 'POST' method handler" ) { WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/json, Content-Length: 0'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "0" ) ); headers.insert( make_pair( "Content-Type", "application/json" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '415' (Unsupported Media Type) status code" ) { REQUIRE( 415 == response->get_status_code( ) ); REQUIRE( "Unsupported Media Type" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Unsupported Media Type, must be 'application/csv'.'" ) { auto actual = Http::fetch( 50, response ); string body( actual.begin( ), actual.end( ) ); REQUIRE( body == "Unsupported Media Type, must be 'application/csv'." ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '50'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "50" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv, Content-Length: 4' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", "4" ) ); headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform an HTTP 'POST' request to '/resources' with headers 'Content-Type: application/csv' and body 'data'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources" ); request->set_body( Bytes( { 'd', 'a', 't', 'a' } ) ); request->set_method( "POST" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/csv" ) ); request->set_headers( headers ); auto response = Http::sync( request ); THEN( "I should see a '411' (Length Required) status code" ) { REQUIRE( 411 == response->get_status_code( ) ); REQUIRE( "Length Required" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Length Required.'" ) { auto actual = Http::fetch( 16, response ); Bytes expectation { 'L', 'e', 'n', 'g', 't', 'h', ' ', 'R', 'e', 'q', 'u', 'i', 'r', 'e', 'd', '.' }; REQUIRE( actual == expectation ); } headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Type' header value of 'text/plain'" ) { auto header = headers.find( "Content-Type" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "text/plain" == headers.find( "Content-Type" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '16'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "16" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/runtime_service_modifications/000077500000000000000000000000001270400672200254655ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/runtime_service_modifications/feature.cpp000066400000000000000000000061351270400672200276310ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::exception; using std::shared_ptr; using std::make_shared; using std::runtime_error; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces void ready_handler( Service& ) { return; } void signal_handler( const int ) { return; } void handler( const shared_ptr< Session > ) { return; } void error_handler( const int, const exception&, const shared_ptr< Session > ) { return; } void authentication_handler( const shared_ptr< Session >, const function< void ( const shared_ptr< Session > ) >& ) { return; } SCENARIO( "runtime service modifications", "[service]" ) { auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/1' with a HTTP 'GET' method handler" ) { WHEN( "I attempt to modify service settings" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/acceptance-tests/runtime service modifications" ); THEN( "I should see an runtime error of 'Runtime modifications of the service are prohibited.'" ) { REQUIRE_THROWS_AS( service.publish( resource ), runtime_error ); REQUIRE_THROWS_AS( service.suppress( resource ), runtime_error ); REQUIRE_THROWS_AS( service.set_logger( nullptr ), runtime_error ); REQUIRE_THROWS_AS( service.set_not_found_handler( handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_error_handler( error_handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_ready_handler( ready_handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_signal_handler( SIGINT, signal_handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_method_not_allowed_handler( handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_method_not_implemented_handler( handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_failed_filter_validation_handler( handler ), runtime_error ); REQUIRE_THROWS_AS( service.set_authentication_handler( authentication_handler ), runtime_error ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/service_case_sensitivity/000077500000000000000000000000001270400672200244575ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/service_case_sensitivity/case_insensitive.cpp000066400000000000000000000130571270400672200305240ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); REQUIRE( "1" == request->get_path_parameter( "id" ) ); REQUIRE( "1" == request->get_path_parameter( "ID" ) ); REQUIRE( "abc" == request->get_query_parameter( "q" ) ); REQUIRE( "abc" == request->get_query_parameter( "Q" ) ); session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "case sensitive service", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/{id: [0-9]}" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_case_insensitive_uris( true ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/{id: [0-9]}' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1?q=abc'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1?q=abc" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } auto headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform a HTTP 'GET' request to '/RESOURCES/1?q=abc'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/RESOURCES/1?q=abc" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } auto headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/service_case_sensitivity/case_sensitive.cpp000066400000000000000000000125241270400672200301730ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::function; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_method_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); REQUIRE( "1" == request->get_path_parameter( "id", String::CASE_SENSITIVE ) ); REQUIRE( "" == request->get_path_parameter( "ID", String::CASE_SENSITIVE ) ); REQUIRE( "abc" == request->get_query_parameter( "q", String::CASE_SENSITIVE ) ); REQUIRE( "" == request->get_query_parameter( "Q", String::CASE_SENSITIVE ) ); session->close( OK, "Hello, World!", { { "Content-Length", "13" } } ); } SCENARIO( "case sensitive service", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/{id: [0-9]}" ); resource->set_method_handler( "GET", get_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_case_insensitive_uris( false ); settings->set_default_header( "Connection", "close" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I publish a resource at '/resources/{id: [0-9]}' with a HTTP 'GET' method handler" ) { WHEN( "I perform a HTTP 'GET' request to '/resources/1?q=abc'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/1?q=abc" ); auto response = Http::sync( request ); THEN( "I should see a '200' (OK) status code" ) { REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( "OK" == response->get_status_message( ) ); } AND_THEN( "I should see a response body of 'Hello, World!'" ) { auto actual = Http::fetch( 13, response ); Bytes expectation { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; REQUIRE( actual == expectation ); } auto headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should see a 'Content-Length' header value of '13'" ) { auto header = headers.find( "Content-Length" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "13" == headers.find( "Content-Length" )->second ); } } WHEN( "I perform a HTTP 'GET' request to '/RESOURCES/1?q=abc'" ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/RESOURCES/1?q=abc" ); auto response = Http::sync( request ); THEN( "I should see a '404' (Not Found) status code" ) { REQUIRE( 404 == response->get_status_code( ) ); REQUIRE( "Not Found" == response->get_status_message( ) ); } AND_THEN( "I should see an empty response body" ) { REQUIRE( response->get_body( ).empty( ) ); } auto headers = response->get_headers( ); AND_THEN( "I should see a 'Connection' header value of 'close'" ) { auto header = headers.find( "Connection" ); REQUIRE( header not_eq headers.end( ) ); REQUIRE( "close" == headers.find( "Connection" )->second ); } AND_THEN( "I should not see a 'Content-Length' header" ) { REQUIRE( headers.find( "Content-Length" ) == headers.end( ) ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/acceptance/source/signal_handling/000077500000000000000000000000001270400672200224735ustar00rootroot00000000000000restbed-4.0/test/acceptance/source/signal_handling/feature.cpp000066400000000000000000000035561270400672200246430ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::raise; using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces int signal_number_actual = 0; void signal_handler( const int signal_number ) { signal_number_actual = signal_number; } SCENARIO( "Handle specific signal", "[service]" ) { auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.set_signal_handler( SIGHUP, signal_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { GIVEN( "I start a service with a 'SIGHUP' signal handler" ) { WHEN( "I generate a 'SIGHUP' event" ) { THEN( "I should see a 'SIGHUP' signal number" ) { signal_number_actual = 0; REQUIRE( signal_number_actual == 0 ); raise( SIGHUP ); std::this_thread::sleep_for( seconds( 1 ) ); REQUIRE( signal_number_actual == SIGHUP ); } } service.stop( ); } } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/integration/000077500000000000000000000000001270400672200163075ustar00rootroot00000000000000restbed-4.0/test/integration/CMakeLists.txt000066400000000000000000000051561270400672200210560ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "integration test suite" ) cmake_minimum_required( VERSION 2.8.10 ) # # Configuration # set( SOURCE_DIR "source" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCATCH_CONFIG_MAIN" ) include_directories( SYSTEM ${catch_INCLUDE} ) # # Build # add_executable( request_integration_test_suite ${SOURCE_DIR}/request_suite.cpp ) target_link_libraries( request_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( request_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/request_integration_test_suite ) add_executable( string_integration_test_suite ${SOURCE_DIR}/string_suite.cpp ) target_link_libraries( string_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( string_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/string_integration_test_suite ) add_executable( session_integration_test_suite ${SOURCE_DIR}/session_suite.cpp ) target_link_libraries( session_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( session_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/session_integration_test_suite ) add_executable( resource_integration_test_suite ${SOURCE_DIR}/resource_suite.cpp ) target_link_libraries( resource_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_integration_test_suite ) add_executable( response_integration_test_suite ${SOURCE_DIR}/response_suite.cpp ) target_link_libraries( response_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( response_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/response_integration_test_suite ) add_executable( ssl_settings_integration_test_suite ${SOURCE_DIR}/ssl_settings_suite.cpp ) target_link_libraries( ssl_settings_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( ssl_settings_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/ssl_settings_integration_test_suite ) add_executable( settings_integration_test_suite ${SOURCE_DIR}/settings_suite.cpp ) target_link_libraries( settings_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( settings_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/settings_integration_test_suite ) add_executable( service_integration_test_suite ${SOURCE_DIR}/service_suite.cpp ) target_link_libraries( service_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( service_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/service_integration_test_suite ) add_executable( http_integration_test_suite ${SOURCE_DIR}/http_suite.cpp ) target_link_libraries( http_integration_test_suite ${CMAKE_PROJECT_NAME} ) add_test( http_integration_test_suite ${EXECUTABLE_OUTPUT_PATH}/http_integration_test_suite ) restbed-4.0/test/integration/source/000077500000000000000000000000001270400672200176075ustar00rootroot00000000000000restbed-4.0/test/integration/source/http_suite.cpp000066400000000000000000000074071270400672200225130ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include #include #include #include #include #include //External Includes #include //System Namespaces using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; using std::runtime_error; //Project Namespaces using restbed::Http; using restbed::Bytes; using restbed::Request; using restbed::Response; using restbed::Settings; using restbed::SSLSettings; //External Namespaces #ifdef BUILD_SSL TEST_CASE( "validate runtime_error is not thrown when SSLSettings passed into sync", "[client]" ) { const auto settings = make_shared< Settings >( ); settings->set_ssl_settings( make_shared< SSLSettings >( ) ); auto request = make_shared< Request >( ); REQUIRE_NOTHROW( Http::sync( request, settings ) ); } #else TEST_CASE( "validate runtime_error is thrown when SSLSettings passed into sync", "[client]" ) { const auto settings = make_shared< Settings >( ); settings->set_ssl_settings( make_shared< SSLSettings >( ) ); auto request = make_shared< Request >( ); REQUIRE_THROWS_AS( Http::sync( request, settings ), runtime_error ); } #endif TEST_CASE( "convert request to bytes", "[http]" ) { auto request = make_shared< Request >( ); SECTION( "default request" ) { const auto bytes = Http::to_bytes( request ); const string data( bytes.data( ), bytes.data( ) + bytes.size( ) ); REQUIRE( data == "GET / HTTP/1.1\r\n\r\n" ); } SECTION( "modified request" ) { request->set_version( 1.0 ); request->set_method( "POST" ); request->set_protocol( "SPDY" ); request->set_path( "/index.html" ); request->set_body( Bytes( { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' } ) ); multimap< string, string > parameters { { "query", "search term" }, { "page", "1" } }; request->set_query_parameters( parameters ); multimap< string, string > headers { { "Connection", "keep-alive" } }; request->set_headers( headers ); const auto bytes = Http::to_bytes( request ); const string data( bytes.data( ), bytes.data( ) + bytes.size( ) ); REQUIRE( data == "POST /index.html?page=1&query=search%20term SPDY/1.0\r\nConnection: keep-alive\r\n\r\nHello, World!" ); } } TEST_CASE( "convert response to bytes", "[http]" ) { auto response = make_shared< Response >( ); SECTION( "default response" ) { const auto bytes = Http::to_bytes( response ); const string data( bytes.data( ), bytes.data( ) + bytes.size( ) ); REQUIRE( data == "HTTP/1.1 0 \r\n\r\n" ); } SECTION( "modified response" ) { response->set_body( Bytes( { 'a', 'b' } ) ); response->set_version( 1.0 ); response->set_status_code( 400 ); response->set_protocol( "SPDY" ); response->set_status_message( "corvusoft ltd" ); multimap< string, string > headers { { "Connection", "keep-alive" } }; response->set_headers( headers ); const auto bytes = Http::to_bytes( response ); const string data( bytes.data( ), bytes.data( ) + bytes.size( ) ); REQUIRE( data == "SPDY/1.0 400 corvusoft ltd\r\nConnection: keep-alive\r\n\r\nab" ); } } restbed-4.0/test/integration/source/request_suite.cpp000066400000000000000000000056401270400672200232210ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include #include #include #include //External Includes #include //System Namespaces using std::string; using std::multimap; //Project Namespaces using restbed::Uri; using restbed::Bytes; using restbed::String; using restbed::Request; //External Namespaces TEST_CASE( "validate setters modify default values", "[request]" ) { Bytes expectation = { 'a', 'b', 'c' }; Request request; request.set_body( expectation ); REQUIRE( request.get_body( ) == expectation ); REQUIRE( request.has_query_parameter( "q", String::CASE_SENSITIVE ) == false ); REQUIRE( request.has_path_parameter( "id", String::CASE_SENSITIVE ) == false ); } TEST_CASE( "validate URI constructor", "[request]" ) { Request request( Uri( "https://localhost:1985/messages?a=1&b=2&c=9#1234" ) ); REQUIRE( request.get_port( ) == 1985 ); REQUIRE( request.get_protocol( ) == "HTTPS" ); REQUIRE( request.get_path( ) == "/messages" ); REQUIRE( request.get_host( ) == "localhost" ); multimap< string, string > parameters { { "a", "1" }, { "b", "2" }, { "c", "9" } }; REQUIRE( request.get_query_parameters( ) == parameters ); } TEST_CASE( "validate HTTPS URI constructor", "[request]" ) { Request request( Uri( "https://localhost/?abc=456&efg=123" ) ); REQUIRE( request.get_port( ) == 443 ); REQUIRE( request.get_protocol( ) == "HTTPS" ); REQUIRE( request.get_path( ) == "/" ); REQUIRE( request.get_host( ) == "localhost" ); multimap< string, string > parameters { { "abc", "456" }, { "efg", "123" } }; REQUIRE( request.get_query_parameters( ) == parameters ); } TEST_CASE( "validate HTTP URI constructor", "[request]" ) { Request request( Uri( "http://localhost/?abc=456&efg=123" ) ); REQUIRE( request.get_port( ) == 80 ); REQUIRE( request.get_protocol( ) == "HTTP" ); REQUIRE( request.get_path( ) == "/" ); REQUIRE( request.get_host( ) == "localhost" ); multimap< string, string > parameters { { "abc", "456" }, { "efg", "123" } }; REQUIRE( request.get_query_parameters( ) == parameters ); } TEST_CASE( "validate URI constructor with no path", "[request]" ) { Request request( Uri( "http://localhost?abc=456&efg=123" ) ); REQUIRE( request.get_port( ) == 80 ); REQUIRE( request.get_protocol( ) == "HTTP" ); REQUIRE( request.get_path( ) == "/" ); REQUIRE( request.get_host( ) == "localhost" ); multimap< string, string > parameters { { "abc", "456" }, { "efg", "123" } }; REQUIRE( request.get_query_parameters( ) == parameters ); } restbed-4.0/test/integration/source/resource_suite.cpp000066400000000000000000000013631270400672200233560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include #include //External Includes #include //System Namespaces using std::string; using std::function; using std::shared_ptr; using std::invalid_argument; //Project Namespaces using restbed::Session; using restbed::Resource; //External Namespaces TEST_CASE( "validate invalid (empty) method argument", "[resource]" ) { Resource resource; const function< void ( const shared_ptr< Session > ) > callback; REQUIRE_THROWS_AS( resource.set_method_handler( "", callback ), invalid_argument ); } restbed-4.0/test/integration/source/response_suite.cpp000066400000000000000000000013511270400672200233620ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes //Project Includes #include #include //External Includes #include //System Namespaces //Project Namespaces using restbed::Bytes; using restbed::Response; //External Namespaces TEST_CASE( "validate default instance values", "[response]" ) { const Response response; REQUIRE( response.get_body( ).empty( ) ); } TEST_CASE( "validate setters modify default values", "[response]" ) { Bytes expectation = { 'a', 'b' }; Response response; response.set_body( expectation ); const auto body = response.get_body( ); REQUIRE( body == expectation ); } restbed-4.0/test/integration/source/service_suite.cpp000066400000000000000000000026121270400672200231650ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include #include #include //External Includes #include //System Namespaces using std::shared_ptr; using std::make_shared; using std::runtime_error; //Project Namespaces using restbed::Service; using restbed::Settings; using restbed::SSLSettings; //External Namespaces #ifdef BUILD_SSL TEST_CASE( "validate runtime_error is not thrown when SSLSettings passed into start", "[service]" ) { const auto ssl_settings = make_shared< SSLSettings >( ); ssl_settings->set_port( 1989 ); const auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_ssl_settings( ssl_settings ); Service service; service.set_ready_handler( [ ]( Service & service ) { service.stop( ); } ); REQUIRE_NOTHROW( service.start( settings ) ); } #else TEST_CASE( "validate runtime_error is thrown when SSLSettings passed into start", "[service]" ) { const auto settings = make_shared< Settings >( ); settings->set_ssl_settings( make_shared< SSLSettings >( ) ); Service service; REQUIRE_THROWS_AS( service.start( settings ), runtime_error ); service.stop( ); } #endif restbed-4.0/test/integration/source/session_suite.cpp000066400000000000000000000053401270400672200232110ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include //Project Includes #include #include #include #include //External Includes #include //System Namespaces using std::set; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; //Project Namespaces using restbed::Bytes; using restbed::Session; using restbed::Request; using restbed::Resource; //External Namespaces TEST_CASE( "validate default instance values", "[session]" ) { const Session session( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); REQUIRE( session.get_request( ) == nullptr ); REQUIRE( session.get_resource( ) == nullptr ); REQUIRE( session.has( "test-key" ) == false ); REQUIRE( session.keys( ) == set< string >( ) ); REQUIRE( session.get_id( ) == "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); } TEST_CASE( "validate setters modify default values", "[session]" ) { const set< string > filters = { "value-1", "value-2" }; Session session( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); session.set( "name", string( "corvusoft" ) ); session.set( "filters", filters ); const string session_name = session.get( "name" ); const set< string > session_filters = session.get( "filters" ); REQUIRE( session_filters == filters ); REQUIRE( session_name == string( "corvusoft" ) ); REQUIRE( session.has( "name" ) == true ); REQUIRE( session.has( "filters" ) == true ); REQUIRE( session.keys( ) == set< string >( { "name", "filters" } ) ); } TEST_CASE( "invoke fetch on uninitialised instance", "[session]" ) { auto session = make_shared< Session >( "" ); REQUIRE( session->is_closed( ) == true ); REQUIRE_NOTHROW( session->fetch( 100, [ ]( const shared_ptr< Session >, const Bytes& ) { return; } ) ); REQUIRE_NOTHROW( session->fetch( "\r\n", [ ]( const shared_ptr< Session >, const Bytes& ) { return; } ) ); } TEST_CASE( "invoke yield on uninitialised instance", "[session]" ) { auto session = make_shared< Session >( "" ); REQUIRE( session->is_closed( ) == true ); REQUIRE_NOTHROW( session->yield( Bytes( { 'a' } ), [ ]( const shared_ptr< Session > ) { return; } ) ); REQUIRE_NOTHROW( session->yield( 200, Bytes( { 'a' } ), [ ]( const shared_ptr< Session > ) { return; } ) ); REQUIRE_NOTHROW( session->yield( 200, Bytes( { 'a' } ), { { "Content-Type", "text" } }, [ ]( const shared_ptr< Session > ) { return; } ) ); } restbed-4.0/test/integration/source/settings_suite.cpp000066400000000000000000000015671270400672200233750ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include #include //External Includes #include //System Namespaces using std::shared_ptr; using std::make_shared; //Project Namespaces using restbed::Settings; using restbed::SSLSettings; //External Namespaces TEST_CASE( "validate default instance values", "[settings]" ) { const Settings settings; REQUIRE( settings.get_ssl_settings( ) == nullptr ); } TEST_CASE( "validate setters modify default values", "[settings]" ) { auto ssl_settings = make_shared< SSLSettings >( ); ssl_settings->set_port( 3434 ); Settings settings; settings.set_ssl_settings( ssl_settings ); REQUIRE( settings.get_ssl_settings( )->get_port( ) == 3434 ); } restbed-4.0/test/integration/source/ssl_settings_suite.cpp000066400000000000000000000024361270400672200242520ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include #include //External Includes #include //System Namespaces using std::string; //Project Namespaces using restbed::Uri; using restbed::SSLSettings; //External Namespaces TEST_CASE( "validate setters modify default values", "[settings]" ) { SSLSettings settings; settings.set_certificate( Uri( "file:///tmp/server.crt" ) ); settings.set_private_key( Uri( "file:///tmp/server.key" ) ); settings.set_private_rsa_key( Uri( "file:///tmp/rsa.key" ) ); settings.set_certificate_chain( Uri( "file:///tmp/chain.crt" ) ); settings.set_certificate_authority_pool( Uri( "file:///tmp" ) ); settings.set_temporary_diffie_hellman( Uri( "file:///tmp/dh512.pem" ) ); REQUIRE( settings.get_certificate( ) == "/tmp/server.crt" ); REQUIRE( settings.get_private_key( ) == "/tmp/server.key" ); REQUIRE( settings.get_private_rsa_key( ) == "/tmp/rsa.key" ); REQUIRE( settings.get_certificate_chain( ) == "/tmp/chain.crt" ); REQUIRE( settings.get_temporary_diffie_hellman( ) == "/tmp/dh512.pem" ); REQUIRE( settings.get_certificate_authority_pool( ) == "/tmp" ); } restbed-4.0/test/integration/source/string_suite.cpp000066400000000000000000000063761270400672200230460ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include "corvusoft/restbed/byte.hpp" #include "corvusoft/restbed/string.hpp" //External Includes #include //System Namespaces using std::string; using std::vector; //Project Namespaces using restbed::Bytes; using restbed::String; //External Namespaces TEST_CASE( "case insensitive remove", "[string]" ) { REQUIRE( String::remove( "Solutions", "Corvusoft SOLUTIONS", String::CASE_INSENSITIVE ) == "Corvusoft " ); } TEST_CASE( "case insensitive remove with missing target", "[string]" ) { REQUIRE( String::remove( "ltd", "Corvusoft SOLUTIONS", String::CASE_INSENSITIVE ) == "Corvusoft SOLUTIONS" ); } TEST_CASE( "case insensitive remove with empty target", "[string]" ) { REQUIRE( String::remove( "", "Corvusoft SOLUTIONS", String::CASE_INSENSITIVE ) == "Corvusoft SOLUTIONS" ); } TEST_CASE( "case insensitive remove with empty value", "[string]" ) { REQUIRE( String::remove( "Solutions", "", String::CASE_INSENSITIVE ) == "" ); } TEST_CASE( "case insensitive remove with empty arguments", "[string]" ) { REQUIRE( String::remove( "", "", String::CASE_INSENSITIVE ) == "" ); } TEST_CASE( "case insensitive replace", "[string]" ) { REQUIRE( String::replace( "ltd", "Solutions", "Corvusoft Ltd", String::CASE_INSENSITIVE ) == "Corvusoft Solutions" ); } TEST_CASE( "case insensitive replace with missing target", "[string]" ) { REQUIRE( String::replace( "", "Solutions", "Corvusoft Ltd", String::CASE_INSENSITIVE ) == "Corvusoft Ltd" ); } TEST_CASE( "case insensitive replace with empty target", "[string]" ) { REQUIRE( String::replace( "", "Solutions", "Corvusoft Ltd", String::CASE_INSENSITIVE ) == "Corvusoft Ltd" ); } TEST_CASE( "case insensitive replace with empty substitute", "[string]" ) { REQUIRE( String::replace( "Ltd", "", "Corvusoft Ltd", String::CASE_INSENSITIVE ) == "Corvusoft " ); } TEST_CASE( "case insensitive replace with empty value", "[string]" ) { REQUIRE( String::replace( "Ltd", "Solutions", "", String::CASE_INSENSITIVE ) == "" ); } TEST_CASE( "case insensitive replace with empty target and substitute", "[string]" ) { REQUIRE( String::replace( "", "", "Corvusoft SOLUTIONS", String::CASE_INSENSITIVE ) == "Corvusoft SOLUTIONS" ); } TEST_CASE( "case insensitive replace with empty target and value", "[string]" ) { REQUIRE( String::replace( "", "SOLUTIONS", "", String::CASE_INSENSITIVE ) == "" ); } TEST_CASE( "case insensitive replace with empty substitute and value", "[string]" ) { REQUIRE( String::replace( "Ltd", "", "Corvusoft SOLUTIONS", String::CASE_INSENSITIVE ) == "Corvusoft SOLUTIONS" ); } TEST_CASE( "case insensitive replace with empty arguments", "[string]" ) { REQUIRE( String::replace( "", "", "", String::CASE_INSENSITIVE ) == "" ); } TEST_CASE( "to_string", "[string]" ) { REQUIRE( String::to_string( Bytes( { 'a', 'b' } ) ) == "ab" ); } TEST_CASE( "empty to_string", "[string]" ) { REQUIRE( String::to_string( Bytes( { } ) ) == "" ); } TEST_CASE( "to_bytes", "[string]" ) { REQUIRE( String::to_bytes( "ab" ) == Bytes( { 'a', 'b' } ) ); } TEST_CASE( "empty to_bytes", "[string]" ) { REQUIRE( String::to_bytes( "" ) == Bytes( { } ) ); } restbed-4.0/test/regression/000077500000000000000000000000001270400672200161445ustar00rootroot00000000000000restbed-4.0/test/regression/CMakeLists.txt000066400000000000000000000210021270400672200206770ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "regression test suite" ) cmake_minimum_required( VERSION 2.8.10 ) # # Configuration # set( SOURCE_DIR "source" ) set( HELPERS_DIR "${CMAKE_SOURCE_DIR}/test/helpers/source" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCATCH_CONFIG_MAIN" ) include_directories( SYSTEM ${catch_INCLUDE} ${asio_INCLUDE} ) # # Build # add_executable( resources_are_not_overwritten_regression_test_suite ${SOURCE_DIR}/resources_are_not_overwritten.cpp ) target_link_libraries( resources_are_not_overwritten_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resources_are_not_overwritten_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/resources_are_not_overwritten_regression_test_suite ) add_executable( missing_regex_support_on_gcc_4.8_regression_test_suite ${SOURCE_DIR}/missing_regex_support_on_gcc_4.8.cpp ) target_link_libraries( missing_regex_support_on_gcc_4.8_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( missing_regex_support_on_gcc_4.8_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/missing_regex_support_on_gcc_4.8_regression_test_suite ) add_executable( request_uris_are_not_being_decoded_regression_test_suite ${SOURCE_DIR}/request_uris_are_not_being_decoded.cpp ) target_link_libraries( request_uris_are_not_being_decoded_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( request_uris_are_not_being_decoded_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/request_uris_are_not_being_decoded_regression_test_suite ) add_executable( resource_responding_on_invalid_path_regression_test_suite ${SOURCE_DIR}/resource_responding_on_invalid_path.cpp ) target_link_libraries( resource_responding_on_invalid_path_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_responding_on_invalid_path_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_responding_on_invalid_path_regression_test_suite ) add_executable( segmentation_fault_on_mismatched_path_regression_test_suite ${SOURCE_DIR}/segmentation_fault_on_mismatched_path.cpp ) target_link_libraries( segmentation_fault_on_mismatched_path_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( segmentation_fault_on_mismatched_path_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/segmentation_fault_on_mismatched_path_regression_test_suite ) add_executable( exception_thrown_with_space_in_resource_path_regression_test_suite ${SOURCE_DIR}/exception_thrown_with_space_in_resource_path.cpp ) target_link_libraries( exception_thrown_with_space_in_resource_path_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( exception_thrown_with_space_in_resource_path_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/exception_thrown_with_space_in_resource_path_regression_test_suite ) add_executable( uncaught_exception_when_peer_closes_connection_regression_test_suite ${SOURCE_DIR}/uncaught_exception_when_peer_closes_connection.cpp ) target_link_libraries( uncaught_exception_when_peer_closes_connection_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( uncaught_exception_when_peer_closes_connection_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/uncaught_exception_when_peer_closes_connection_regression_test_suite ) add_executable( error_handler_not_overwritten_regression_test_suite ${SOURCE_DIR}/error_handler_not_overwritten.cpp ) target_link_libraries( error_handler_not_overwritten_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( error_handler_not_overwritten_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/error_handler_not_overwritten_regression_test_suite ) add_executable( large_request_bodies_being_trimmed_regression_test_suite ${SOURCE_DIR}/large_request_bodies_being_trimmed.cpp ) target_link_libraries( large_request_bodies_being_trimmed_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( large_request_bodies_being_trimmed_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/large_request_bodies_being_trimmed_regression_test_suite ) add_executable( fails_to_parse_identical_query_parameters_regression_test_suite ${SOURCE_DIR}/fails_to_parse_identical_query_parameters.cpp ) target_link_libraries( fails_to_parse_identical_query_parameters_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( fails_to_parse_identical_query_parameters_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/fails_to_parse_identical_query_parameters_regression_test_suite ) add_executable( content_length_present_on_empty_response_body_regression_test_suite ${SOURCE_DIR}/content_length_present_on_empty_response_body.cpp ) target_link_libraries( content_length_present_on_empty_response_body_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( content_length_present_on_empty_response_body_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/content_length_present_on_empty_response_body_regression_test_suite ) add_executable( content_type_present_on_empty_response_body_regression_test_suite ${SOURCE_DIR}/content_type_present_on_empty_response_body.cpp ) target_link_libraries( content_type_present_on_empty_response_body_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( content_type_present_on_empty_response_body_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/content_type_present_on_empty_response_body_regression_test_suite ) add_executable( resource_instance_destroyed_with_bound_method_functors_regression_test_suite ${SOURCE_DIR}/resource_instance_destroyed_with_bound_method_functors.cpp ) target_link_libraries( resource_instance_destroyed_with_bound_method_functors_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_instance_destroyed_with_bound_method_functors_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_instance_destroyed_with_bound_method_functors_regression_test_suite ) add_executable( fails_to_parse_header_values_containing_colons_regression_test_suite ${SOURCE_DIR}/fails_to_parse_header_values_containing_colons.cpp ) target_link_libraries( fails_to_parse_header_values_containing_colons_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( fails_to_parse_header_values_containing_colons_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/fails_to_parse_header_values_containing_colons_regression_test_suite ) add_executable( string_replace_fails_to_replace_embedded_targets_regression_test_suite ${SOURCE_DIR}/string_replace_fails_to_replace_embedded_targets.cpp ) target_link_libraries( string_replace_fails_to_replace_embedded_targets_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( string_replace_fails_to_replace_embedded_targets_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/string_replace_fails_to_replace_embedded_targets_regression_test_suite ) add_executable( path_parameters_are_not_visible_within_rules_regression_test_suite ${SOURCE_DIR}/path_parameters_are_not_visible_within_rules.cpp ) target_link_libraries( path_parameters_are_not_visible_within_rules_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( path_parameters_are_not_visible_within_rules_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/path_parameters_are_not_visible_within_rules_regression_test_suite ) add_executable( request_get_parameters_fails_to_return_data_regression_test_suite ${SOURCE_DIR}/request_get_parameters_fails_to_return_data.cpp ) target_link_libraries( request_get_parameters_fails_to_return_data_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( request_get_parameters_fails_to_return_data_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/request_get_parameters_fails_to_return_data_regression_test_suite ) add_executable( uri_fails_to_handle_file_scheme_relative_paths_regression_test_suite ${SOURCE_DIR}/uri_fails_to_handle_file_scheme_relative_paths.cpp ) target_link_libraries( uri_fails_to_handle_file_scheme_relative_paths_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( uri_fails_to_handle_file_scheme_relative_paths_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/uri_fails_to_handle_file_scheme_relative_paths_regression_test_suite ) add_executable( multiple_log_entries_regression_test_suite ${SOURCE_DIR}/multiple_log_entries.cpp ) target_link_libraries( multiple_log_entries_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( multiple_log_entries_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/multiple_log_entries_regression_test_suite ) add_executable( client_fails_to_calculate_correct_response_body_length_regression_test_suite ${SOURCE_DIR}/client_fails_to_calculate_correct_response_body_length.cpp ) target_link_libraries( client_fails_to_calculate_correct_response_body_length_regression_test_suite ${CMAKE_PROJECT_NAME} ) add_test( client_fails_to_calculate_correct_response_body_length_regression_test_suite ${EXECUTABLE_OUTPUT_PATH}/client_fails_to_calculate_correct_response_body_length_regression_test_suite ) restbed-4.0/test/regression/source/000077500000000000000000000000001270400672200174445ustar00rootroot00000000000000restbed-4.0/test/regression/source/client_fails_to_calculate_correct_response_body_length.cpp000066400000000000000000000037511270400672200333460ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::shared_ptr; using std::make_shared; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { string body( 65535, 'x' ); session->close( OK, body, { { "Connection", "close" }, { "Content-Length", "65535" } } ); } TEST_CASE( "client fails to calculate correct response body length", "[http]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_method( "GET" ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); Http::fetch( 65535, response ); string actual; response->get_body( actual ); string expectation( 65535, 'x' ); REQUIRE( actual.compare( expectation ) == 0 ); REQUIRE( actual.length( ) == expectation.length( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/content_length_present_on_empty_response_body.cpp000066400000000000000000000031261270400672200315720ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "content length present on empty response body", "[response]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_method( "GET" ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); auto headers = response->get_headers( ); REQUIRE( headers.end( ) == headers.find( "Content-Length" ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/content_type_present_on_empty_response_body.cpp000066400000000000000000000031221270400672200312660ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "content type present on empty response body", "[response]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_method( "GET" ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); auto headers = response->get_headers( ); REQUIRE( headers.end( ) == headers.find( "Content-Type" ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/error_handler_not_overwritten.cpp000066400000000000000000000032441270400672200263310ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::exception; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void faulty_method_handler( const shared_ptr< Session > ) { throw 503; } void error_handler( const int, const exception&, const std::shared_ptr< Session > session ) { session->close( 444 ); } TEST_CASE( "overwrite existing resource", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", faulty_method_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_root( "queues" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_error_handler( error_handler ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/queues/test" ); auto response = Http::sync( request ); REQUIRE( 444 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/exception_thrown_with_space_in_resource_path.cpp000066400000000000000000000046711270400672200313760ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "with space in path", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test queue" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_root( "queues" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/queues/test queue" ); auto response = Http::sync( request ); REQUIRE( 400 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } TEST_CASE( "without space in path", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "testQueue" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); settings->set_root( "queues" ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/queues/testQueue" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/fails_to_parse_header_values_containing_colons.cpp000066400000000000000000000035101270400672200316160ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); CHECK( "Mozilla: 4.0" == request->get_header( "User-Agent" ) ); session->close( 200 ); } TEST_CASE( "fails to parse header values containing colons", "[session]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); multimap< string, string > headers; headers.insert( make_pair( "User-Agent", "Mozilla: 4.0" ) ); request->set_headers( headers ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/fails_to_parse_identical_query_parameters.cpp000066400000000000000000000037171270400672200306360ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::string; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); multimap< string, string > expectation; expectation.insert( make_pair( "echo", "false" ) ); expectation.insert( make_pair( "echo", "true" ) ); const auto actual = request->get_query_parameters( "echo" ); if ( actual not_eq expectation ) { session->close( 500 ); } else { session->close( 200 ); } } TEST_CASE( "fails to parse identical query parameters", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test?echo=false&echo=true" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/large_request_bodies_being_trimmed.cpp000066400000000000000000000053121270400672200272250ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::vector; using std::string; using std::multimap; using std::make_pair; using std::to_string; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces const char* body = R"( { "queues": [ { "name": "usb 1", "ttl": 60, "behaviour": "basic" , "consumption": "indirect" }, { "name": "usb 2", "ttl": 60, "behaviour": "acknowledged", "consumption": "direct" }, { "name": "usb 3", "ttl": 120, "behaviour": "basic" , "consumption": "indirect" }, { "name": "usb 4", "ttl": 60, "behaviour": "acknowledged", "consumption": "indirect" }, { "name": "usb 5", "ttl": 30, "behaviour": "acknowledged", "consumption": "indirect" } ] })"; void post_handler( const shared_ptr< Session > session ) { session->fetch( 492, [ ]( const shared_ptr< Session > session, const Bytes& ) { auto expectation = Bytes( body, body + 492 ); const auto request = session->get_request( ); const auto status = ( request->get_body( ) == expectation ) ? 201 : 400; session->close( status, expectation, { { "Content-Length", "492" } } ); } ); } TEST_CASE( "large request bodies being trimmed", "[request]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "POST", post_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_method( "POST" ); request->set_host( "localhost" ); request->set_path( "/test" ); Bytes data( body, body + 492 ); request->set_body( data ); multimap< string, string > headers; headers.insert( make_pair( "Content-Length", ::to_string( data.size( ) ) ) ); request->set_headers( headers ); auto response = Http::sync( request ); REQUIRE( 201 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/missing_regex_support_on_gcc_4.8.cpp000066400000000000000000000005361270400672200265140ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes //External Includes #include //System Namespaces //Project Namespaces using std::regex; //External Namespaces TEST_CASE( "missing regex support", "[stdlib]" ) { REQUIRE_NOTHROW( regex( "(abc[1234])" ) ); } restbed-4.0/test/regression/source/multiple_log_entries.cpp000066400000000000000000000077331270400672200244070ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::regex; using std::thread; using std::string; using std::vector; using std::vsnprintf; using std::shared_ptr; using std::make_shared; using std::runtime_error; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces class CustomLogger : public Logger { public: CustomLogger( void ) : actual( ) { return; } void stop( void ) { return; } void start( const shared_ptr< const Settings >& ) { return; } void log( const Level, const char* format, ... ) { va_list arguments; va_start( arguments, format ); va_list temporary; va_copy( temporary, arguments ); const auto length = vsnprintf( nullptr, 0, format, temporary ) + 1; va_end( temporary ); string buffer; buffer.resize( length ); if ( vsnprintf( &buffer[ 0 ], length, format, arguments ) < 0 ) { throw runtime_error( "" ); } buffer.pop_back( ); actual.push_back( buffer ); va_end( arguments ); } void log_if( bool expression, const Level level, const char* format, ... ) { if ( expression ) { va_list arguments; va_start( arguments, format ); log( level, format, arguments ); va_end( arguments ); } } vector< string > actual; }; void get_handler( const shared_ptr< Session > session ) { session->close( OK, "OK", { { "Connection", "close" } } ); } TEST_CASE( "multiple log entries", "[service]" ) { auto resource_one = make_shared< Resource >( ); resource_one->set_path( "resource_one" ); resource_one->set_method_handler( "GET", get_handler ); auto resource_two = make_shared< Resource >( ); resource_two->set_path( "resource_two" ); resource_two->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; auto logger = make_shared< CustomLogger >( ); Service service; service.publish( resource_one ); service.publish( resource_two ); service.set_logger( logger ); service.set_ready_handler( [ &worker, &logger ]( Service & service ) { worker = make_shared< thread >( [ &service, &logger ] ( ) { auto request = make_shared< Request >( ); request->set_method( "GET" ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resource_two" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); REQUIRE( regex_match( logger->actual[ 0 ], regex( "^Service accepting HTTP connections at '.*'\\.$" ) ) ); REQUIRE( regex_match( logger->actual[ 1 ], regex( "^Resource published on route '/resource_one'\\.$" ) ) ); REQUIRE( regex_match( logger->actual[ 2 ], regex( "^Resource published on route '/resource_two'\\.$" ) ) ); REQUIRE( regex_match( logger->actual[ 3 ], regex( "^Incoming 'GET' request from '.*' for route '/resource_two'\\.$" ) ) ); REQUIRE( 4 == logger->actual.size( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/path_parameters_are_not_visible_within_rules.cpp000066400000000000000000000047271270400672200313610ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::function; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces class TestRule : public Rule { public: TestRule( void ) : Rule( ) { return; } virtual ~TestRule( void ) { return; } bool condition( const shared_ptr< Session > session ) final override { const auto request = session->get_request( ); REQUIRE( "1a230096-928d-4958-90d1-a681bfff22b4" == request->get_path_parameter( "key" ) ); return true; } void action( const shared_ptr< Session > session, const function< void ( const shared_ptr< Session > ) >& callback ) final override { const auto request = session->get_request( ); REQUIRE( "1a230096-928d-4958-90d1-a681bfff22b4" == request->get_path_parameter( "key" ) ); callback( session ); } }; void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "path parameters are not visible within rules", "[request]" ) { auto resource = make_shared< Resource >( ); resource->add_rule( make_shared< TestRule >( ) ); resource->set_path( "/queues/{key: ^[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}$}" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/queues/1a230096-928d-4958-90d1-a681bfff22b4" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/request_get_parameters_fails_to_return_data.cpp000066400000000000000000000040201270400672200311660ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); const auto path_parameters = request->get_path_parameters( ); REQUIRE( path_parameters.at( "id" ) == "123" ); REQUIRE( path_parameters.at( "key" ) == "abc" ); const auto query_paramters = request->get_query_parameters( ); REQUIRE( query_paramters.count( "name" ) == 1 ); REQUIRE( query_paramters.find( "name" )->second == "test item" ); REQUIRE( query_paramters.count( "style" ) == 1 ); REQUIRE( query_paramters.find( "style" )->second == "true" ); session->close( 200 ); } TEST_CASE( "request get parameters fails to return data", "[request]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/resources/{id: .*}/messages/{key: .*}" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/resources/123/messages/abc?style=true&name=test%20item" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/request_uris_are_not_being_decoded.cpp000066400000000000000000000031441270400672200272260ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { const auto request = session->get_request( ); REQUIRE( "/uri test" == request->get_path( ) ); REQUIRE( "@30" == request->get_query_parameter( "ben crowhurst" ) ); session->close( 200 ); } TEST_CASE( "encoded uri test", "[request]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "uri test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 8989 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 8989 ); request->set_host( "localhost" ); request->set_path( "/uri%20test?ben+crowhurst=%4030" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/resource_instance_destroyed_with_bound_method_functors.cpp000066400000000000000000000024501270400672200334530ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces TEST_CASE( "resource instance destroyed with bound method functors", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "/" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); resource.reset( ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/" ); auto response = Http::sync( request ); REQUIRE( 501 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/resource_responding_on_invalid_path.cpp000066400000000000000000000064511270400672200274530ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "mismatched resource path of equal path segments", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/tests" ); auto response = Http::sync( request ); REQUIRE( 404 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } TEST_CASE( "mismatched resource path of unequal path segments", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", &get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 );; shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/event/test" ); auto response = Http::sync( request ); REQUIRE( 404 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } TEST_CASE( "matched resource path", "[resource]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", &get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/resources_are_not_overwritten.cpp000066400000000000000000000067731270400672200263560ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::string; using std::thread; using std::multimap; using std::make_pair; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void json_get_handler( const shared_ptr< Session > session ) { session->close( 200, "{ name: \"value\" }", { { "Content-Length", "17" }, { "Content-Type", "application/json" } } ); } void xml_get_handler( const shared_ptr< Session > session ) { session->close( 401, "value", { { "Content-Length", "18" }, { "Content-Type", "application/xml" } } ); } TEST_CASE( "overwrite existing resource", "[resource]" ) { auto initial_resource = make_shared< Resource >( ); initial_resource->set_path( "TestResource" ); initial_resource->set_method_handler( "GET", json_get_handler ); auto secondary_resource = make_shared< Resource >( ); secondary_resource->set_path( "TestResource" ); secondary_resource->set_method_handler( "GET", xml_get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( initial_resource ); service.publish( secondary_resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/TestResource" ); auto response = Http::sync( request ); REQUIRE( 401 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } TEST_CASE( "add alternative resource", "[resource]" ) { auto initial_resource = make_shared< Resource >( ); initial_resource->set_path( "TestResource" ); initial_resource->set_method_handler( "GET", { { "Content-Type", "application/json" } }, json_get_handler ); auto secondary_resource = make_shared< Resource >( ); secondary_resource->set_path( "TestResource" ); secondary_resource->set_method_handler( "GET", { { "Content-Type", "application/xml" } }, xml_get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( initial_resource ); service.publish( secondary_resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/TestResource" ); multimap< string, string > headers; headers.insert( make_pair( "Content-Type", "application/xml" ) ); request->set_headers( headers ); auto response = Http::sync( request ); REQUIRE( 401 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/segmentation_fault_on_mismatched_path.cpp000066400000000000000000000044651270400672200277570ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; //Project Namespaces using namespace restbed; //External Namespaces void get_handler( const shared_ptr< Session > session ) { session->close( 200 ); } TEST_CASE( "mismatched resource path", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/" ); auto response = Http::sync( request ); REQUIRE( 404 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } TEST_CASE( "matched resource path", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); resource->set_method_handler( "GET", get_handler ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); shared_ptr< thread > worker = nullptr; Service service; service.publish( resource ); service.set_ready_handler( [ &worker ]( Service & service ) { worker = make_shared< thread >( [ &service ] ( ) { auto request = make_shared< Request >( ); request->set_port( 1984 ); request->set_host( "localhost" ); request->set_path( "/test" ); auto response = Http::sync( request ); REQUIRE( 200 == response->get_status_code( ) ); service.stop( ); } ); } ); service.start( settings ); worker->join( ); } restbed-4.0/test/regression/source/string_replace_fails_to_replace_embedded_targets.cpp000066400000000000000000000013061270400672200320660ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include "corvusoft/restbed/string.hpp" //External Includes #include //System Namespaces using std::string; //Project Namespaces using restbed::String; //External Namespaces TEST_CASE( "deduplicate string value", "[string]" ) { const string value = "//path/to////resources/"; REQUIRE( String::replace( "//", "/", value ) == "/path/to/resources/" ); } TEST_CASE( "deduplicate string value with missing target", "[string]" ) { const string value = "/path/to/resources"; REQUIRE( String::replace( "//", "/", value ) == "/path/to/resources" ); } restbed-4.0/test/regression/source/uncaught_exception_when_peer_closes_connection.cpp000066400000000000000000000032671270400672200316770ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include //Project Includes #include //External Includes #include #include //System Namespaces using std::thread; using std::shared_ptr; using std::make_shared; using std::chrono::seconds; //Project Namespaces using namespace restbed; //External Namespaces using asio::ip::tcp; using asio::connect; using asio::io_service; using asio::system_error; bool exception_was_thrown = false; void worker( shared_ptr< Service > service, shared_ptr< Settings > settings ) { try { service->start( settings ); } catch ( const system_error& se ) { if ( se.code( ) == asio::error::eof ) { exception_was_thrown = true; } } } void wait_for_service_initialisation( void ) { std::this_thread::sleep_for( seconds( 1 ) ); } TEST_CASE( "peer closes connection without sending data", "[service]" ) { auto resource = make_shared< Resource >( ); resource->set_path( "test" ); auto settings = make_shared< Settings >( ); settings->set_port( 1984 ); auto service = make_shared< Service >( ); service->publish( resource ); thread restbed_thread( worker, service, settings ); wait_for_service_initialisation( ); io_service io_service; tcp::socket socket( io_service ); tcp::resolver resolver( io_service ); connect( socket, resolver.resolve( { "localhost", "1984" } ) ); socket.close( ); service->stop( ); restbed_thread.join( ); REQUIRE_FALSE( exception_was_thrown ); } restbed-4.0/test/regression/source/uri_fails_to_handle_file_scheme_relative_paths.cpp000066400000000000000000000033721270400672200315640ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include //External Includes #include //System Namespaces using std::string; using std::multimap; //Project Namespaces using namespace restbed; //External Namespaces TEST_CASE( "uri fails to handle file scheme relative paths", "[uri]" ) { multimap< string, string > expectation; Uri relative( "file://certs/server.key", true ); REQUIRE( relative.get_port( ) == 0 ); REQUIRE( relative.get_path( ) == "certs/server.key" ); REQUIRE( relative.get_query( ) == "" ); REQUIRE( relative.get_scheme( ) == "file" ); REQUIRE( relative.get_fragment( ) == "" ); REQUIRE( relative.get_username( ) == "" ); REQUIRE( relative.get_password( ) == "" ); REQUIRE( relative.get_authority( ) == "" ); REQUIRE( relative.is_relative( ) == true ); REQUIRE( relative.is_absolute( ) == false ); REQUIRE( relative.to_string( ) == "file://certs/server.key" ); REQUIRE( relative.get_query_parameters( ) == expectation ); Uri absolute( "file:///certs/server.key" ); REQUIRE( absolute.get_port( ) == 0 ); REQUIRE( absolute.get_path( ) == "/certs/server.key" ); REQUIRE( absolute.get_query( ) == "" ); REQUIRE( absolute.get_scheme( ) == "file" ); REQUIRE( absolute.get_fragment( ) == "" ); REQUIRE( absolute.get_username( ) == "" ); REQUIRE( absolute.get_password( ) == "" ); REQUIRE( absolute.get_authority( ) == "" ); REQUIRE( absolute.is_relative( ) == false ); REQUIRE( absolute.is_absolute( ) == true ); REQUIRE( absolute.to_string( ) == "file:///certs/server.key" ); REQUIRE( absolute.get_query_parameters( ) == expectation ); } restbed-4.0/test/unit/000077500000000000000000000000001270400672200147435ustar00rootroot00000000000000restbed-4.0/test/unit/CMakeLists.txt000066400000000000000000000045461270400672200175140ustar00rootroot00000000000000# Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. project( "unit test suite" ) cmake_minimum_required( VERSION 2.8.10 ) # # Configuration # set( SOURCE_DIR "source" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCATCH_CONFIG_MAIN" ) include_directories( SYSTEM ${catch_INCLUDE} ) # # Build # add_executable( uri_unit_test_suite ${SOURCE_DIR}/uri_suite.cpp ) target_link_libraries( uri_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( uri_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/uri_unit_test_suite ) add_executable( string_unit_test_suite ${SOURCE_DIR}/string_suite.cpp ) target_link_libraries( string_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( string_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/string_unit_test_suite ) add_executable( session_unit_test_suite ${SOURCE_DIR}/session_suite.cpp ) target_link_libraries( session_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( session_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/session_unit_test_suite ) add_executable( service_unit_test_suite ${SOURCE_DIR}/service_suite.cpp ) target_link_libraries( service_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( service_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/service_unit_test_suite ) add_executable( request_unit_test_suite ${SOURCE_DIR}/request_suite.cpp ) target_link_libraries( request_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( request_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/request_unit_test_suite ) add_executable( settings_unit_test_suite ${SOURCE_DIR}/settings_suite.cpp ) target_link_libraries( settings_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( settings_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/settings_unit_test_suite ) add_executable( response_unit_test_suite ${SOURCE_DIR}/response_suite.cpp ) target_link_libraries( response_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( response_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/response_unit_test_suite ) add_executable( resource_unit_test_suite ${SOURCE_DIR}/resource_suite.cpp ) target_link_libraries( resource_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( resource_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/resource_unit_test_suite ) add_executable( ssl_settings_unit_test_suite ${SOURCE_DIR}/ssl_settings_suite.cpp ) target_link_libraries( ssl_settings_unit_test_suite ${CMAKE_PROJECT_NAME} ) add_test( ssl_settings_unit_test_suite ${EXECUTABLE_OUTPUT_PATH}/ssl_settings_unit_test_suite ) restbed-4.0/test/unit/source/000077500000000000000000000000001270400672200162435ustar00rootroot00000000000000restbed-4.0/test/unit/source/request_suite.cpp000066400000000000000000000100431270400672200216460ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include //External Includes #include //System Namespaces using std::map; using std::string; using std::multimap; //Project Namespaces using restbed::Request; //External Namespaces TEST_CASE( "validate default instance values", "[request]" ) { const Request request; REQUIRE( request.get_port( ) == 80 ); REQUIRE( request.get_host( ) == "" ); REQUIRE( request.get_version( ) == 1.1 ); REQUIRE( request.get_path( ) == "/" ); REQUIRE( request.get_body( ).empty( ) ); REQUIRE( request.get_method( ) == "GET" ); REQUIRE( request.get_protocol( ) == "HTTP" ); REQUIRE( request.get_headers( ).empty( ) ); REQUIRE( request.get_response( ) == nullptr ); REQUIRE( request.get_path_parameters( ).empty( ) ); REQUIRE( request.get_query_parameters( ).empty( ) ); REQUIRE( request.has_query_parameter( "q" ) == false ); REQUIRE( request.has_path_parameter( "id" ) == false ); REQUIRE( request.has_header( "Content-Type" ) == false ); } TEST_CASE( "confirm default destructor throws no exceptions", "[request]" ) { auto request = new Request; REQUIRE_NOTHROW( delete request ); } TEST_CASE( "validate setters modify default values", "[request]" ) { Request request; request.set_body( "abc" ); request.set_port( 1984 ); request.set_version( 1.0 ); request.set_path( "/test case" ); request.set_host( "www.google.co.uk" ); request.set_method( "CONNECT" ); request.set_protocol( "HTTPS" ); multimap< string, string > headers { { "X-CUST", "1223" } }; request.set_headers( headers ); multimap< string, string > parameters { { "q", "search" } }; request.set_query_parameters( parameters ); REQUIRE( request.get_port( ) == 1984 ); REQUIRE( request.get_host( ) == "www.google.co.uk" ); REQUIRE( request.get_version( ) == 1.0 ); REQUIRE( request.get_path( ) == "/test case" ); REQUIRE( request.get_body( ).empty( ) == false ); REQUIRE( request.get_method( ) == "CONNECT" ); REQUIRE( request.get_protocol( ) == "HTTPS" ); REQUIRE( request.get_headers( ) == headers ); REQUIRE( request.get_response( ) == nullptr ); REQUIRE( request.get_path_parameters( ).empty( ) ); REQUIRE( request.get_query_parameters( ) == parameters ); REQUIRE( request.has_query_parameter( "q" ) == true ); REQUIRE( request.has_header( "X-CUST" ) == true ); } TEST_CASE( "validate getter default value", "[request]" ) { const Request request; SECTION( "integer" ) { int value; request.get_header( "Var", value, 12 ); REQUIRE( value == 12 ); } SECTION( "unsigned integer" ) { unsigned int value; request.get_header( "Var", value, 6 ); REQUIRE( value == 6 ); } SECTION( "long" ) { long value; request.get_header( "Var", value, 54 ); REQUIRE( value == 54 ); } SECTION( "long long" ) { long long value; request.get_header( "Var", value, 66 ); REQUIRE( value == 66 ); } SECTION( "unsigned long" ) { unsigned long value; unsigned long default_value = static_cast< unsigned long >( -33 ); request.get_header( "Var", value, default_value ); REQUIRE( value == default_value ); } SECTION( "unsigned long long" ) { long long value; request.get_header( "Var", value, -6 ); REQUIRE( value == -6 ); } SECTION( "float" ) { float value; request.get_header( "Var", value, float( 3.6 ) ); REQUIRE( value == 3.6f ); } SECTION( "double" ) { double value; request.get_header( "Var", value, 34443 ); REQUIRE( value == 34443 ); } SECTION( "string" ) { string header = request.get_header( "Var", string( "open" ) ); REQUIRE( header == "open" ); } } restbed-4.0/test/unit/source/resource_suite.cpp000066400000000000000000000011041270400672200220030ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include //Project Includes #include //External Includes #include //System Namespaces //Project Namespaces using restbed::Resource; //External Namespaces TEST_CASE( "confirm default constructor throws no exceptions", "[resource]" ) { REQUIRE_NOTHROW( new Resource ); } TEST_CASE( "confirm default destructor throws no exceptions", "[resource]" ) { auto resource = new Resource; REQUIRE_NOTHROW( delete resource ); } restbed-4.0/test/unit/source/response_suite.cpp000066400000000000000000000062201270400672200220160ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include //External Includes #include //System Namespaces using std::string; using std::multimap; //Project Namespaces using restbed::Response; //External Namespaces TEST_CASE( "validate default instance values", "[response]" ) { const Response response; REQUIRE( response.get_version( ) == 1.1 ); REQUIRE( response.get_status_code( ) == 0 ); REQUIRE( response.get_protocol( ) == "HTTP" ); REQUIRE( response.get_headers( ).empty( ) ); REQUIRE( response.get_status_message( ).empty( ) ); REQUIRE( response.get_request( ) == nullptr ); } TEST_CASE( "confirm default destructor throws no exceptions", "[response]" ) { auto response = new Response; REQUIRE_NOTHROW( delete response ); } TEST_CASE( "validate setters modify default values", "[response]" ) { Response response; response.set_body( "ab" ); response.set_version( 1.0 ); response.set_status_code( 400 ); response.set_protocol( "SPDY" ); response.set_status_message( "corvusoft ltd" ); multimap< string, string > headers { { "Connection", "keep-alive" } }; response.set_headers( headers ); REQUIRE( response.get_version( ) == 1.0 ); REQUIRE( response.get_status_code( ) == 400 ); REQUIRE( response.get_protocol( ) == "SPDY" ); REQUIRE( response.get_headers( ) == headers ); REQUIRE( response.get_status_message( ) == "corvusoft ltd" ); REQUIRE( response.get_request( ) == nullptr ); } TEST_CASE( "validate getter default value", "[response]" ) { const Response response; SECTION( "integer" ) { int value; response.get_header( "Var", value, 12 ); REQUIRE( value == 12 ); } SECTION( "unsigned integer" ) { unsigned int value; response.get_header( "Var", value, 6 ); REQUIRE( value == 6 ); } SECTION( "long" ) { long value; response.get_header( "Var", value, 54 ); REQUIRE( value == 54 ); } SECTION( "long long" ) { long long value; response.get_header( "Var", value, 66 ); REQUIRE( value == 66 ); } SECTION( "unsigned long" ) { unsigned long value; unsigned long default_value = static_cast< unsigned long >( -33 ); response.get_header( "Var", value, default_value ); REQUIRE( value == default_value ); } SECTION( "unsigned long long" ) { long long value; response.get_header( "Var", value, -6 ); REQUIRE( value == -6 ); } SECTION( "float" ) { float value; response.get_header( "Var", value, float( 3.6 ) ); REQUIRE( value == 3.6f ); } SECTION( "double" ) { double value; response.get_header( "Var", value, 34443 ); REQUIRE( value == 34443 ); } SECTION( "string" ) { string header = response.get_header( "Var", string( "open" ) ); REQUIRE( header == "open" ); } } restbed-4.0/test/unit/source/service_suite.cpp000066400000000000000000000013041270400672200216160ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes //Project Includes #include //External Includes #include //System Namespaces //Project Namespaces using restbed::Service; //External Namespaces TEST_CASE( "confirm default constructor throws no exceptions", "[service]" ) { REQUIRE_NOTHROW( new Service ); } TEST_CASE( "confirm default destructor throws no exceptions", "[service]" ) { auto service = new Service; REQUIRE_NOTHROW( delete service ); } TEST_CASE( "confirm calling stop before start throws no exceptions", "[service]" ) { Service service; REQUIRE_NOTHROW( service.stop( ) ); } restbed-4.0/test/unit/source/session_suite.cpp000066400000000000000000000101251270400672200216420ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::set; using std::string; using std::bad_cast; using std::multimap; using std::shared_ptr; using std::make_shared; using std::out_of_range; using std::invalid_argument; //Project Namespaces using restbed::Session; //External Namespaces TEST_CASE( "validate default instance values", "[session]" ) { const Session session( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); REQUIRE( session.is_open( ) == false ); REQUIRE( session.is_closed( ) == true ); REQUIRE( session.get_origin( ) == "" ); REQUIRE( session.get_destination( ) == "" ); REQUIRE( session.get_headers( ).empty( ) ); REQUIRE( session.get_id( ) == "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); } TEST_CASE( "confirm empty session id throws no exceptions", "[session]" ) { REQUIRE_NOTHROW( Session( "" ) ); } TEST_CASE( "confirm default destructor throws no exceptions", "[session]" ) { auto session = new Session( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); REQUIRE_NOTHROW( delete session ); } TEST_CASE( "validate setters modify default values", "[session]" ) { Session session( "" ); session.set_id( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); REQUIRE( session.get_id( ) == "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); session.set_header( "Connection", "close" ); multimap< string, string > expectation = { { "Connection", "close" } }; REQUIRE( session.get_headers( ) == expectation ); expectation = { { "Content-Type", "application/yaml" }, { "Content-Encoding", "" } }; session.set_headers( expectation ); REQUIRE( session.get_headers( ) == expectation ); } TEST_CASE( "validate session context functionality", "[session]" ) { Session session( "f47ac10b-58cc-4372-a567-0e02b2c3d479" ); session.set( "Connection", string( "close" ) ); const string connection = session.get( "Connection" ); REQUIRE( connection == "close" ); REQUIRE( session.has( "Connection" ) ); REQUIRE( not session.has( "connection" ) ); REQUIRE( session.keys( ) == set< string >( { "Connection" } ) ); session.erase( "Connection" ); REQUIRE( not session.has( "Connection" ) ); const string keep_alive = session.get( "Connection", string( "keep-alive" ) ); REQUIRE( keep_alive == "keep-alive" ); REQUIRE( session.keys( ) == set< string >( ) ); session.set( "Connection", string( "close" ) ); session.set( "Connection", string( "keep-alive" ) ); const string header = session.get( "Connection" ); REQUIRE( header == "keep-alive" ); try { int type = session.get( "Connection" ); REQUIRE( type == 0 ); } catch ( const bad_cast& ) { REQUIRE( true ); } session.erase( ); REQUIRE( session.keys( ) == set< string >( ) ); REQUIRE_THROWS_AS( session.get( "Connection" ), out_of_range ); } TEST_CASE( "invoke close on uninitialised instance", "[session]" ) { auto session = make_shared< Session >( "" ); REQUIRE( session->is_closed( ) == true ); REQUIRE_NOTHROW( session->close( ) ); REQUIRE( session->is_closed( ) == true ); } TEST_CASE( "invoke yield on uninitialised instance", "[session]" ) { auto session = make_shared< Session >( "" ); REQUIRE( session->is_closed( ) == true ); REQUIRE_NOTHROW( session->yield( "test data", [ ]( const shared_ptr< Session > ) { return; } ) ); REQUIRE_NOTHROW( session->yield( 200, "test data", [ ]( const shared_ptr< Session > ) { return; } ) ); REQUIRE_NOTHROW( session->yield( 200, "test data", { { "Content-Type", "text" } }, [ ]( const shared_ptr< Session > ) { return; } ) ); REQUIRE_NOTHROW( session->yield( 200, { { "Content-Type", "text" } }, [ ]( const shared_ptr< Session > ) { return; } ) ); } restbed-4.0/test/unit/source/settings_suite.cpp000066400000000000000000000215421270400672200220240ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include //External Includes #include //System Namespaces using std::map; using std::string; using std::multimap; using std::chrono::milliseconds; //Project Namespaces using restbed::Settings; //External Namespaces TEST_CASE( "validate default instance values", "[settings]" ) { const Settings settings; REQUIRE( settings.get_port( ) == 80 ); REQUIRE( settings.get_root( ) == "/" ); REQUIRE( settings.get_worker_limit( ) == 0 ); REQUIRE( settings.get_properties( ).empty( ) ); REQUIRE( settings.get_bind_address( ).empty( ) ); REQUIRE( settings.get_connection_limit( ) == 128 ); REQUIRE( settings.get_default_headers( ).empty( ) ); REQUIRE( settings.get_case_insensitive_uris( ) == true ); REQUIRE( settings.get_connection_timeout( ) == milliseconds( 5000 ) ); map< int, string > expectation = { { 100, "Continue" }, { 101, "Switching Protocols" }, { 102, "Processing" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, { 203, "Non-Authoritative Information" }, { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 207, "Multi-Status" }, { 208, "Already Reported" }, { 226, "IM Used" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Found" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 306, "Reserved" }, { 307, "Temporary Redirect" }, { 308, "Permanent Redirect" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Timeout" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request URI Too Long" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 422, "Unprocessable Entity" }, { 423, "Locked" }, { 424, "Failed Dependency" }, { 426, "Upgrade Required" }, { 428, "Precondition Required" }, { 429, "Too Many Requests" }, { 431, "Request Header Fields Too Large" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Timeout" }, { 505, "HTTP Version Not Supported" }, { 506, "Variant Also Negotiates" }, { 507, "Insufficient Storage" }, { 508, "Loop Detected" }, { 510, "Not Extended" }, { 511, "Network Authentication Required" } }; REQUIRE( settings.get_status_messages( ) == expectation ); } TEST_CASE( "confirm default destructor throws no exceptions", "[settings]" ) { auto settings = new Settings; REQUIRE_NOTHROW( delete settings ); } TEST_CASE( "validate setters modify default values", "[settings]" ) { Settings settings; settings.set_port( 1984 ); settings.set_worker_limit( 4 ); settings.set_root( "/resources" ); settings.set_connection_limit( 1 ); settings.set_bind_address( "::1" ); settings.set_case_insensitive_uris( false ); settings.set_connection_timeout( milliseconds( 30 ) ); settings.set_properties( { { "name", "value" } } ); settings.set_default_headers( { { "Connection", "close" } } ); REQUIRE( settings.get_port( ) == 1984 ); REQUIRE( settings.get_root( ) == "/resources" ); REQUIRE( settings.get_worker_limit( ) == 4 ); REQUIRE( settings.get_bind_address( ) == "::1" ); REQUIRE( settings.get_connection_limit( ) == 1 ); REQUIRE( settings.get_case_insensitive_uris( ) == false ); REQUIRE( settings.get_connection_timeout( ) == milliseconds( 30 ) ); map< string, string > properties_expectation = { { "name", "value" } }; REQUIRE( settings.get_properties( ) == properties_expectation ); multimap< string, string > headers_expectation = { { "Connection", "close" } }; REQUIRE( settings.get_default_headers( ) == headers_expectation ); } TEST_CASE( "manipulating status messages", "[settings]" ) { Settings settings; settings.set_status_message( 418, "I'm a teapot" ); SECTION( "read individual status message from valid status code" ) { REQUIRE( settings.get_status_message( 418 ) == "I'm a teapot" ); } SECTION( "read individual status message from invalid status code" ) { REQUIRE( settings.get_status_message( -2 ) == "No Appropriate Status Message Found" ); } SECTION( "read individual status message from unknown status code" ) { REQUIRE( settings.get_status_message( 888 ) == "No Appropriate Status Message Found" ); } SECTION( "read all status messages" ) { map< int, string > expectation = { { 100, "Continue" }, { 101, "Switching Protocols" }, { 102, "Processing" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, { 203, "Non-Authoritative Information" }, { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 207, "Multi-Status" }, { 208, "Already Reported" }, { 226, "IM Used" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Found" }, { 303, "See Other" }, { 304, "Not Modified" }, { 305, "Use Proxy" }, { 306, "Reserved" }, { 307, "Temporary Redirect" }, { 308, "Permanent Redirect" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Method Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Timeout" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request URI Too Long" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 418, "I'm a teapot" }, { 422, "Unprocessable Entity" }, { 423, "Locked" }, { 424, "Failed Dependency" }, { 426, "Upgrade Required" }, { 428, "Precondition Required" }, { 429, "Too Many Requests" }, { 431, "Request Header Fields Too Large" }, { 500, "Internal Server Error" }, { 501, "Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Unavailable" }, { 504, "Gateway Timeout" }, { 505, "HTTP Version Not Supported" }, { 506, "Variant Also Negotiates" }, { 507, "Insufficient Storage" }, { 508, "Loop Detected" }, { 510, "Not Extended" }, { 511, "Network Authentication Required" } }; REQUIRE( settings.get_status_messages( ) == expectation ); } } TEST_CASE( "manipulation generic properties", "[settings]" ) { Settings settings; settings.set_property( "security-seed", "de305d54-75b4-431b-adb2-eb6b9e546014" ); SECTION( "read individual property from valid name" ) { REQUIRE( settings.get_property( "security-seed" ) == "de305d54-75b4-431b-adb2-eb6b9e546014" ); } SECTION( "read individual property from uppercase name" ) { REQUIRE( settings.get_property( "SECURITY-SEED" ) == "" ); } SECTION( "read individual property from mixedcase name" ) { REQUIRE( settings.get_property( "SEcURiTY-SeeD" ) == "" ); } SECTION( "read individual propety from invalid name" ) { REQUIRE( settings.get_property( "" ) == "" ); } SECTION( "read individual property from unknown name" ) { REQUIRE( settings.get_property( "realm" ) == "" ); } SECTION( "read all properties" ) { map< string, string > expectation = { { "security-seed", "de305d54-75b4-431b-adb2-eb6b9e546014" } }; REQUIRE( settings.get_properties( ) == expectation ); } } restbed-4.0/test/unit/source/ssl_settings_suite.cpp000066400000000000000000000055151270400672200227070ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include //Project Includes #include //External Includes #include //System Namespaces using std::string; //Project Namespaces using restbed::SSLSettings; //External Namespaces TEST_CASE( "validate default instance values", "[ssl-settings]" ) { const SSLSettings settings; REQUIRE( settings.get_port( ) == 443 ); REQUIRE( settings.has_enabled_sslv2( ) ); REQUIRE( settings.has_enabled_sslv3( ) ); REQUIRE( settings.has_enabled_tlsv1( ) ); REQUIRE( settings.has_enabled_tlsv11( ) ); REQUIRE( settings.has_enabled_tlsv12( ) ); REQUIRE( settings.has_enabled_compression( ) ); REQUIRE( settings.has_enabled_default_workarounds( ) ); REQUIRE( settings.has_enabled_single_diffie_hellman_use( ) ); REQUIRE( settings.get_bind_address( ).empty( ) ); REQUIRE( settings.get_passphrase( ).empty( ) ); REQUIRE( settings.get_certificate( ).empty( ) ); REQUIRE( settings.get_private_key( ).empty( ) ); REQUIRE( settings.get_private_rsa_key( ).empty( ) ); REQUIRE( settings.get_certificate_chain( ).empty( ) ); REQUIRE( settings.get_temporary_diffie_hellman( ).empty( ) ); REQUIRE( settings.get_certificate_authority_pool( ).empty( ) ); REQUIRE( settings.has_disabled_http( ) == false ); } TEST_CASE( "confirm default destructor throws no exceptions", "[ssl-settings]" ) { auto settings = new SSLSettings; REQUIRE_NOTHROW( delete settings ); } TEST_CASE( "validate setters modify default values", "[settings]" ) { SSLSettings settings; settings.set_port( 8080 ); settings.set_sslv2_enabled( false ); settings.set_sslv3_enabled( false ); settings.set_tlsv1_enabled( false ); settings.set_tlsv11_enabled( false ); settings.set_tlsv12_enabled( false ); settings.set_http_disabled( true ); settings.set_compression_enabled( false ); settings.set_default_workarounds_enabled( false ); settings.set_single_diffie_hellman_use_enabled( false ); settings.set_bind_address( "127.0.0.1" ); settings.set_passphrase( "my-passphrase" ); REQUIRE( settings.get_port( ) == 8080 ); REQUIRE( not settings.has_enabled_sslv2( ) ); REQUIRE( not settings.has_enabled_sslv3( ) ); REQUIRE( not settings.has_enabled_tlsv1( ) ); REQUIRE( not settings.has_enabled_tlsv11( ) ); REQUIRE( not settings.has_enabled_tlsv12( ) ); REQUIRE( not settings.has_enabled_compression( ) ); REQUIRE( not settings.has_enabled_default_workarounds( ) ); REQUIRE( not settings.has_enabled_single_diffie_hellman_use( ) ); REQUIRE( settings.get_bind_address( ) == "127.0.0.1" ); REQUIRE( settings.get_passphrase( ) == "my-passphrase" ); REQUIRE( settings.has_disabled_http( ) == true ); } restbed-4.0/test/unit/source/string_suite.cpp000066400000000000000000000124241270400672200214710ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/string.hpp" //External Includes #include //System Namespaces using std::string; using std::vector; using std::multimap; //Project Namespaces using restbed::String; //External Namespaces TEST_CASE( "uppercase to lowercase", "[string]" ) { REQUIRE( String::lowercase( "CORVUSOFT" ) == "corvusoft" ); } TEST_CASE( "lowercase to lowercase", "[string]" ) { REQUIRE( String::lowercase( "corvusoft" ) == "corvusoft" ); } TEST_CASE( "mixedcase to lowercase", "[string]" ) { REQUIRE( String::lowercase( "CoRvUSoFt" ) == "corvusoft" ); } TEST_CASE( "empty to lowercase", "[string]" ) { REQUIRE( String::lowercase( "" ) == "" ); } TEST_CASE( "uppercase to uppercase", "[string]" ) { REQUIRE( String::uppercase( "corvusoft" ) == "CORVUSOFT" ); } TEST_CASE( "lowercase to uppercase", "[string]" ) { REQUIRE( String::uppercase( "corvusoft" ) == "CORVUSOFT" ); } TEST_CASE( "mixedcase to uppercase", "[string]" ) { REQUIRE( String::uppercase( "CoRvUSoFt" ) == "CORVUSOFT" ); } TEST_CASE( "empty to uppercase", "[string]" ) { REQUIRE( String::uppercase( "" ) == "" ); } TEST_CASE( "format", "[string]" ) { REQUIRE( String::format( "Corvusoft %s", "Solutions" ) == "Corvusoft Solutions" ); } TEST_CASE( "format with no specifiers", "[string]" ) { REQUIRE( String::format( "Corvusoft" ) == "Corvusoft" ); } TEST_CASE( "format with empty", "[string]" ) { REQUIRE( String::format( "" ) == "" ); } TEST_CASE( "split", "[string]" ) { REQUIRE( String::split( "Corvusoft Solutions", ' ' ) == vector< string >( { "Corvusoft", "Solutions" } ) ); } TEST_CASE( "split with missing delimiter", "[string]" ) { REQUIRE( String::split( "Corvusoft Solutions", '+' ) == vector< string >( { "Corvusoft Solutions" } ) ); } TEST_CASE( "split with empty delimiter", "[string]" ) { char expectation = 0; REQUIRE( String::split( "Corvusoft Solutions", expectation ) == vector< string >( { "Corvusoft Solutions" } ) ); } TEST_CASE( "join map to string", "[string]" ) { multimap< string, string > values = { { "fields", "id,rev" }, { "sort", "rev" } }; REQUIRE( String::join( values, "=", "&" ) == "fields=id,rev&sort=rev" ); } TEST_CASE( "join map to string with missing value & pair delimiters", "[string]" ) { multimap< string, string > values = { { "fields", "id,rev" }, { "sort", "rev" } }; REQUIRE( String::join( values, "", "" ) == "fieldsid,revsortrev" ); } TEST_CASE( "join empty map to string", "[string]" ) { multimap< string, string > values; REQUIRE( String::join( values, "=", "&" ) == "" ); } TEST_CASE( "join map to string with missing value delimiter", "[string]" ) { multimap< string, string > values = { { "fields", "id,rev" }, { "sort", "rev" } }; REQUIRE( String::join( values, "", "&" ) == "fieldsid,rev&sortrev" ); } TEST_CASE( "join map to string with missing pair delimiter", "[string]" ) { multimap< string, string > values = { { "fields", "id,rev" }, { "sort", "rev" } }; REQUIRE( String::join( values, "=", "" ) == "fields=id,revsort=rev" ); } TEST_CASE( "remove", "[string]" ) { REQUIRE( String::remove( "Solutions", "Corvusoft Solutions" ) == "Corvusoft " ); } TEST_CASE( "remove multiple", "[string]" ) { REQUIRE( String::remove( "dot", "dot dash dot dash dash" ) == " dash dash dash" ); } TEST_CASE( "remove with missing target", "[string]" ) { REQUIRE( String::remove( "ltd", "Corvusoft Solutions" ) == "Corvusoft Solutions" ); } TEST_CASE( "remove with empty target", "[string]" ) { REQUIRE( String::remove( "", "Corvusoft Solutions" ) == "Corvusoft Solutions" ); } TEST_CASE( "remove with empty value", "[string]" ) { REQUIRE( String::remove( " Solutions", "" ) == "" ); } TEST_CASE( "remove with empty arguments", "[string]" ) { REQUIRE( String::remove( "", "" ) == "" ); } TEST_CASE( "replace", "[string]" ) { REQUIRE( String::replace( "ltd", "Solutions", "Corvusoft ltd" ) == "Corvusoft Solutions" ); } TEST_CASE( "replace multiple", "[string]" ) { REQUIRE( String::replace( "dot", "ping", "dot dash dot dash dash" ) == "ping dash ping dash dash" ); } TEST_CASE( "replace with missing target", "[string]" ) { REQUIRE( String::replace( "ltd", "Solutions", "Corvusoft Solutions" ) == "Corvusoft Solutions" ); } TEST_CASE( "replace with empty target", "[string]" ) { REQUIRE( String::replace( "", "Solutions", "Corvusoft ltd" ) == "Corvusoft ltd" ); } TEST_CASE( "replace with empty substitute", "[string]" ) { REQUIRE( String::replace( "ltd", "", "Corvusoft ltd" ) == "Corvusoft " ); } TEST_CASE( "replace with empty value", "[string]" ) { REQUIRE( String::replace( "ltd", "Solutions", "" ) == "" ); } TEST_CASE( "replace with empty target and substitute", "[string]" ) { REQUIRE( String::replace( "", "", "Corvusoft ltd" ) == "Corvusoft ltd" ); } TEST_CASE( "replace with empty target and value", "[string]" ) { REQUIRE( String::replace( "", "Solutions", "" ) == "" ); } TEST_CASE( "replace with empty substitue and value", "[string]" ) { REQUIRE( String::replace( "ltd", "", "" ) == "" ); } TEST_CASE( "replace with empty arguments", "[string]" ) { REQUIRE( String::replace( "", "", "" ) == "" ); } restbed-4.0/test/unit/source/uri_suite.cpp000066400000000000000000000107041270400672200207610ustar00rootroot00000000000000/* * Copyright 2013-2016, Corvusoft Ltd, All Rights Reserved. */ //System Includes #include #include #include //Project Includes #include "corvusoft/restbed/uri.hpp" //External Includes #include //System Namespaces using std::string; using std::invalid_argument; //Project Namespaces using restbed::Uri; //External Namespaces TEST_CASE( "default constructor", "[uri]" ) { Uri uri( "http://crowhurst.ben:ASDFFDSA1234@code.google.com:80/resources/index.html?q=bear&b=cubs#frag1" ); REQUIRE( uri.get_port( ) == 80 ); REQUIRE( uri.get_path( ) == "/resources/index.html" ); REQUIRE( uri.get_query( ) == "q=bear&b=cubs" ); REQUIRE( uri.get_scheme( ) == "http" ); REQUIRE( uri.get_fragment( ) == "frag1" ); REQUIRE( uri.get_username( ) == "crowhurst.ben" ); REQUIRE( uri.get_password( ) == "ASDFFDSA1234" ); REQUIRE( uri.get_authority( ) == "code.google.com" ); const string value = uri.to_string( ); REQUIRE( value == "http://crowhurst.ben:ASDFFDSA1234@code.google.com:80/resources/index.html?q=bear&b=cubs#frag1" ); } TEST_CASE( "ipv4 constructor", "[uri]" ) { Uri uri( "http://username:password@127.1.1.1:80/resources/index.html?q=bear&b=cubs#frag1" ); const string value = uri.to_string( ); REQUIRE( value == "http://username:password@127.1.1.1:80/resources/index.html?q=bear&b=cubs#frag1" ); } TEST_CASE( "ipv6 constructor", "[uri]" ) { Uri uri( "http://username:password@[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80/resources/index.html?q=bear&b=cubs#frag1" ); const string value = uri.to_string( ); REQUIRE( value == "http://username:password@[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80/resources/index.html?q=bear&b=cubs#frag1" ); } TEST_CASE( "invalid constructor", "[uri]" ) { REQUIRE_THROWS_AS( Uri( "---_)(*&" ), invalid_argument ); } TEST_CASE( "empty constructor", "[uri]" ) { REQUIRE_THROWS_AS( Uri( "" ), invalid_argument ); } TEST_CASE( "copy constructor", "[uri]" ) { Uri uri( "ftp://code.google.com/p/application-on" ); Uri copy( uri ); REQUIRE( copy.to_string( ) == uri.to_string( ) ); } TEST_CASE( "destructor", "[uri]" ) { Uri* uri = new Uri( "ftp://code.google.com/p/application-on" ); REQUIRE_NOTHROW( delete uri ); } TEST_CASE( "assignment-operator", "[uri]" ) { Uri uri( "http://code.google.com/p/application-on/source/detail?r=73aa44eefc85407545b11ff30abbcd980030aab1" ); Uri copy = uri; REQUIRE( copy.to_string( ) == uri.to_string( ) ); } TEST_CASE( "less-than-operator", "[uri]" ) { Uri lhs( "http://restbed.corvusoft.co.uk" ); Uri rhs( "http://restule.corvusoft.co.uk/index.html" ); REQUIRE( lhs < rhs ); } TEST_CASE( "greater-than-operator", "[uri]" ) { Uri lhs( "http://restbed.corvusoft.co.uk/index.html" ); Uri rhs( "http://restbed.corvusoft.co.uk" ); REQUIRE( lhs > rhs ); } TEST_CASE( "equality-operator", "[uri]" ) { Uri lhs( "http://restq.corvusoft.co.uk" ); Uri rhs( "http://restq.corvusoft.co.uk" ); REQUIRE( lhs == rhs ); } TEST_CASE( "inequality-operator", "[uri]" ) { Uri lhs( "http://restbed.corvusoft.co.uk" ); Uri rhs( "http://restule.corvusoft.co.uk" ); REQUIRE( lhs not_eq rhs ); } TEST_CASE( "to_string", "[uri]" ) { Uri uri( "https://source.corvusoft.co.uk" ); REQUIRE( uri.to_string( ) == "https://source.corvusoft.co.uk" ); } TEST_CASE( "valid is_valid", "[uri]" ) { REQUIRE( Uri::is_valid( "ws://restq.corvusoft.co.uk:443/queues" ) ); } TEST_CASE( "invalid is_valid", "[uri]" ) { REQUIRE( Uri::is_valid( "---_)(*&" ) == false ); } TEST_CASE( "parse", "[uri]" ) { Uri uri = Uri::parse( "http://shard.corvusoft.co.uk" ); const string value = uri.to_string( ); REQUIRE( value == "http://shard.corvusoft.co.uk" ); } TEST_CASE( "invalid parse", "[uri]" ) { REQUIRE_THROWS_AS( Uri::parse( "---_)(*&" ), invalid_argument ); } TEST_CASE( "empty parse", "[uri]" ) { REQUIRE_THROWS_AS( Uri::parse( "" ), invalid_argument ); } TEST_CASE( "decode", "[uri]" ) { REQUIRE( Uri::decode( "file:///tmp/a%2Eb/tmp_20482932.txt" ) == "file:///tmp/a.b/tmp_20482932.txt" ); } TEST_CASE( "decode_parameter", "[uri]" ) { REQUIRE( Uri::decode_parameter( "Corvusoft+Solutions" ) == "Corvusoft Solutions" ); } TEST_CASE( "encode", "[uri]" ) { REQUIRE( Uri::encode( "a=b" ) == "a%3Db" ); } TEST_CASE( "encode parameter", "[uri]" ) { REQUIRE( Uri::encode_parameter( "a=b" ) == "a%3Db" ); } restbed-4.0/tool/000077500000000000000000000000001270400672200137625ustar00rootroot00000000000000restbed-4.0/tool/astyle/000077500000000000000000000000001270400672200152635ustar00rootroot00000000000000restbed-4.0/tool/astyle/options000066400000000000000000000005021270400672200166760ustar00rootroot00000000000000style=allman indent=spaces=4 indent-classes indent-switches indent-namespaces indent-labels indent-col1-comments max-instatement-indent=40 break-blocks pad-oper pad-paren-in pad-header fill-empty-lines break-closing-brackets add-brackets convert-tabs align-pointer=type quiet lineend=linux suffix=none indent-preproc-blockrestbed-4.0/tool/clang-static/000077500000000000000000000000001270400672200163335ustar00rootroot00000000000000restbed-4.0/tool/clang-static/options000066400000000000000000000000121270400672200177420ustar00rootroot00000000000000-k -V makerestbed-4.0/tool/cppcheck/000077500000000000000000000000001270400672200155425ustar00rootroot00000000000000restbed-4.0/tool/cppcheck/options000066400000000000000000000001251270400672200171560ustar00rootroot00000000000000--enable=all --inconclusive --std=posix -I source/corvusoft/restbed/detail/ source/* restbed-4.0/tool/cppclean/000077500000000000000000000000001270400672200155475ustar00rootroot00000000000000restbed-4.0/tool/cppclean/options000066400000000000000000000000571270400672200171670ustar00rootroot00000000000000--include-path=source/ source/corvusoft/restbedrestbed-4.0/tool/git/000077500000000000000000000000001270400672200145455ustar00rootroot00000000000000restbed-4.0/tool/git/pre-commit000077500000000000000000000040131270400672200165450ustar00rootroot00000000000000#!/bin/sh # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # If you want to allow non-ascii filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) # Redirect output to stderr. exec 1>&2 # Cross platform projects tend to avoid non-ascii filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. test $(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 then echo "Error: Attempt to add a non-ascii file name." echo echo "This can cause problems if you want to work" echo "with people on other platforms." echo echo "To be portable it is advisable to rename the file ..." echo echo "If you know what you are doing you can disable this" echo "check using:" echo echo " git config hooks.allownonascii true" echo exit 1 fi # If there are whitespace errors, print the offending file names and fail. #exec git diff-index --check --cached $against -- ASTYLE=astyle ASTYLE_PARAMETERS="--options=tool/astyle/options" echo "Formating source code..." files=`git-diff-index --diff-filter=ACMR --name-only -r --cached $against --` for file in $files; do x=`echo $file |grep -E '(\.cpp|\.hpp)'` if test "x$x" != "x"; then $ASTYLE ${ASTYLE_PARAMETERS} $file git add $file fi done restbed-4.0/tool/valgrind/000077500000000000000000000000001270400672200155705ustar00rootroot00000000000000restbed-4.0/tool/valgrind/options000066400000000000000000000000371270400672200172060ustar00rootroot00000000000000--leak-check=yes --dsymutil=yes