diff --git a/README.md b/README.md index 5ad92f1c..07f24818 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,20 @@ Restbed is a comprehensive and consistent programming model for building applica | Comet Support | 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. | | [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 parsings. | +| 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 as readable units of code. | +| [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. | | 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 . | +| [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. | | Compliance | Flexibility to address HTTP 1.0/1.1+ compliance. | | Mature | Secure, Stable, and extensively tested over 3+ years. | | Community | Active, vibrant and energetic open source community. | @@ -132,8 +133,8 @@ You will now find all required components installed in the distribution folder. | 3.0 | Rules Engine | complete | | 3.5 | Schedule Tasks on Service run-loop | complete | | 3.5 | Multi-Threaded service capability | complete | -| 3.5 | Bind Service to specific Address | in-development | -| 3.5 | Session Management | pending | +| 3.5 | Bind Service to specific Address | complete | +| 3.5 | Session Management | in-development | | 4.0 | Resource Caching | pending | | 4.0 | Web Sockets | pending | | 5.0 | HTTP 2.0 compliance | pending | diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index df12b627..ee846f96 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -78,6 +78,9 @@ 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} ) + if ( PAM_FOUND ) add_executable( pam_authentication pam_authentication/source/example.cpp ) target_link_libraries( pam_authentication ${CMAKE_PROJECT_NAME} pam ) diff --git a/example/bind_service_address/source/example.cpp b/example/bind_service_address/source/example.cpp new file mode 100644 index 00000000..2579fbdf --- /dev/null +++ b/example/bind_service_address/source/example.cpp @@ -0,0 +1,39 @@ +/* + * 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; +} diff --git a/source/corvusoft/restbed/detail/service_impl.cpp b/source/corvusoft/restbed/detail/service_impl.cpp index b19ab189..b7d7bfdc 100644 --- a/source/corvusoft/restbed/detail/service_impl.cpp +++ b/source/corvusoft/restbed/detail/service_impl.cpp @@ -53,6 +53,7 @@ using std::regex_constants::icase; using asio::ip::tcp; using asio::io_service; using asio::error_code; +using asio::ip::address; using asio::socket_base; using asio::system_error; @@ -99,7 +100,17 @@ namespace restbed if ( ssl_settings == nullptr or ssl_settings->has_disabled_http( ) == false ) { #endif - acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( tcp::v6( ), settings->get_port( ) ) ); + + if ( not settings->get_bind_address( ).empty( ) ) + { + const auto address = address::from_string( settings->get_bind_address( ) ); + acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( address, settings->get_port( ) ) ); + } + else + { + acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( tcp::v6( ), settings->get_port( ) ) ); + } + acceptor->set_option( socket_base::reuse_address( true ) ); acceptor->listen( settings->get_connection_limit( ) ); @@ -189,7 +200,16 @@ namespace restbed options = ( ssl_settings->has_enabled_single_diffie_hellman_use( ) ) ? options | asio::ssl::context::single_dh_use : options; ssl_context->set_options( options ); - ssl_acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( tcp::v6( ), ssl_settings->get_port( ) ) ); + if ( not ssl_settings->get_bind_address( ).empty( ) ) + { + const auto address = address::from_string( ssl_settings->get_bind_address( ) ); + ssl_acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( address, ssl_settings->get_port( ) ) ); + } + else + { + ssl_acceptor = make_shared< tcp::acceptor >( *io_service, tcp::endpoint( tcp::v6( ), ssl_settings->get_port( ) ) ); + } + ssl_acceptor->set_option( socket_base::reuse_address( true ) ); ssl_acceptor->listen( settings->get_connection_limit( ) ); diff --git a/source/corvusoft/restbed/detail/settings_impl.hpp b/source/corvusoft/restbed/detail/settings_impl.hpp index 01a997f4..db49a2b8 100644 --- a/source/corvusoft/restbed/detail/settings_impl.hpp +++ b/source/corvusoft/restbed/detail/settings_impl.hpp @@ -41,6 +41,8 @@ namespace restbed uint32_t connection_limit = 128; + std::string bind_address = ""; + bool case_insensitive_uris = true; std::map< std::string, std::string > properties { }; diff --git a/source/corvusoft/restbed/detail/ssl_settings_impl.hpp b/source/corvusoft/restbed/detail/ssl_settings_impl.hpp index 1eef7018..a35e1ca5 100644 --- a/source/corvusoft/restbed/detail/ssl_settings_impl.hpp +++ b/source/corvusoft/restbed/detail/ssl_settings_impl.hpp @@ -50,6 +50,8 @@ namespace restbed bool single_diffie_hellman_use_enabled = true; + std::string bind_address = ""; + std::string passphrase = ""; std::string private_key = ""; diff --git a/source/corvusoft/restbed/settings.cpp b/source/corvusoft/restbed/settings.cpp index 6892a6aa..9e2a9a85 100644 --- a/source/corvusoft/restbed/settings.cpp +++ b/source/corvusoft/restbed/settings.cpp @@ -55,6 +55,11 @@ namespace restbed return m_pimpl->connection_limit; } + string Settings::get_bind_address( void ) const + { + return m_pimpl->bind_address; + } + bool Settings::get_case_insensitive_uris( void ) const { return m_pimpl->case_insensitive_uris; @@ -115,6 +120,11 @@ namespace restbed m_pimpl->connection_limit = value; } + void Settings::set_bind_address( const string& value ) + { + m_pimpl->bind_address = value; + } + void Settings::set_case_insensitive_uris( const bool value ) { m_pimpl->case_insensitive_uris = value; diff --git a/source/corvusoft/restbed/settings.hpp b/source/corvusoft/restbed/settings.hpp index 39ab1e80..05188547 100644 --- a/source/corvusoft/restbed/settings.hpp +++ b/source/corvusoft/restbed/settings.hpp @@ -55,6 +55,8 @@ namespace restbed int32_t 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; @@ -80,6 +82,8 @@ namespace restbed void set_connection_limit( const int32_t value ); + void set_bind_address( const std::string& value ); + void set_case_insensitive_uris( const bool value ); void set_connection_timeout( const std::chrono::milliseconds& value ); diff --git a/source/corvusoft/restbed/ssl_settings.cpp b/source/corvusoft/restbed/ssl_settings.cpp index d821d367..dc37dc3d 100644 --- a/source/corvusoft/restbed/ssl_settings.cpp +++ b/source/corvusoft/restbed/ssl_settings.cpp @@ -82,6 +82,11 @@ namespace restbed return m_pimpl->port; } + string SSLSettings::get_bind_address( void ) const + { + return m_pimpl->bind_address; + } + string SSLSettings::get_certificate( void ) const { return m_pimpl->certificate; @@ -122,6 +127,11 @@ namespace restbed m_pimpl->port = value; } + void SSLSettings::set_bind_address( const string& value ) + { + m_pimpl->bind_address = value; + } + void SSLSettings::set_http_disabled( const bool value ) { m_pimpl->http_disabled = value; diff --git a/source/corvusoft/restbed/ssl_settings.hpp b/source/corvusoft/restbed/ssl_settings.hpp index 9e052165..8f9de490 100644 --- a/source/corvusoft/restbed/ssl_settings.hpp +++ b/source/corvusoft/restbed/ssl_settings.hpp @@ -63,6 +63,8 @@ namespace restbed //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; @@ -80,6 +82,8 @@ namespace restbed //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 ); diff --git a/test/unit/source/settings_suite.cpp b/test/unit/source/settings_suite.cpp index 19472959..4286ee13 100644 --- a/test/unit/source/settings_suite.cpp +++ b/test/unit/source/settings_suite.cpp @@ -32,6 +32,7 @@ TEST_CASE( "validate default instance values", "[settings]" ) 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 ); @@ -116,6 +117,7 @@ TEST_CASE( "validate setters modify default values", "[settings]" ) 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" } } ); @@ -124,6 +126,7 @@ TEST_CASE( "validate setters modify default values", "[settings]" ) 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 ) ); diff --git a/test/unit/source/ssl_settings_suite.cpp b/test/unit/source/ssl_settings_suite.cpp index e196560f..87aa672c 100644 --- a/test/unit/source/ssl_settings_suite.cpp +++ b/test/unit/source/ssl_settings_suite.cpp @@ -32,6 +32,7 @@ TEST_CASE( "validate default instance values", "[ssl-settings]" ) 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( ) ); @@ -62,6 +63,7 @@ TEST_CASE( "validate setters modify default values", "[settings]" ) 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 ); @@ -73,6 +75,7 @@ TEST_CASE( "validate setters modify default values", "[settings]" ) 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 ); }