From c0bff45554647e60a93ee2806099429f628d6084 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Thu, 10 Jan 2019 16:18:05 +0100 Subject: [PATCH 01/26] Add "overwrite" option to the handle() method in mux This allows to add several method (POST,DELETE,PUT) to an existing handler without headache or having to keep a reference to every handle --- src/served/multiplexer.cpp | 12 ++++++++++-- src/served/multiplexer.hpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/served/multiplexer.cpp b/src/served/multiplexer.cpp index e550791..f962acc 100644 --- a/src/served/multiplexer.cpp +++ b/src/served/multiplexer.cpp @@ -121,14 +121,22 @@ multiplexer::get_segments(const std::string & path) // ----- http request handlers ----- served::methods_handler & -multiplexer::handle(const std::string & path, const std::string info /* = "" */) +multiplexer::handle(const std::string & path, const std::string info /* = "" */, bool overwrite) { + // Remove any duplicates. for ( auto it = _handler_candidates.begin(); it != _handler_candidates.end(); ) { if ( std::get<2>(*it) == path ) { - it = _handler_candidates.erase(it); + if(overwrite) + { + it = _handler_candidates.erase(it); + } + else + { + return std::get<1>(*it); + } } else { diff --git a/src/served/multiplexer.hpp b/src/served/multiplexer.hpp index c822d59..9628b86 100644 --- a/src/served/multiplexer.hpp +++ b/src/served/multiplexer.hpp @@ -139,7 +139,7 @@ class multiplexer * * @return returns a methods_handler used to specify handlers per HTTP method */ - served::methods_handler & handle(const std::string & path, const std::string info = ""); + served::methods_handler & handle(const std::string & path, const std::string info = "", bool overwrite = true); // ----- request forwarding ----- From f2fec08376e8153025bcefe0431e0acf71104cf4 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Fri, 22 Feb 2019 12:01:56 +0100 Subject: [PATCH 02/26] Upgrade to last version 1.5.1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ca0357..7abbc6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,8 @@ SET (APPLICATION_NAME "Served HTTP REST Library") SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) -SET (APPLICATION_VERSION_MINOR 4) -SET (APPLICATION_VERSION_PATCH 3) +SET (APPLICATION_VERSION_MINOR 5) +SET (APPLICATION_VERSION_PATCH 1) SET (APPLICATION_VERSION_TYPE DS1) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}") SET (APPLICATION_VENDOR_ID "com.meltwater") From 76e50f90c761c0c0fdb9100f32ba9361f21727e9 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Thu, 7 Mar 2019 15:29:56 +0100 Subject: [PATCH 03/26] Enlarge inlined buffer for connection to allow large header request --- src/served/net/connection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/served/net/connection.hpp b/src/served/net/connection.hpp index ca27a9b..7a3c085 100644 --- a/src/served/net/connection.hpp +++ b/src/served/net/connection.hpp @@ -54,7 +54,7 @@ class connection boost::asio::ip::tcp::socket _socket; connection_manager & _connection_manager; multiplexer & _request_handler; - std::array _buffer; + std::array _buffer; size_t _max_req_size_bytes; int _read_timeout; int _write_timeout; From 1acab59eb0e3d2fba6602f58e8e21c1c17cb0d5f Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Thu, 7 Mar 2019 15:32:31 +0100 Subject: [PATCH 04/26] Add 1.5.2 dev version --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7abbc6f..c687606 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,8 +28,8 @@ SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) SET (APPLICATION_VERSION_MINOR 5) -SET (APPLICATION_VERSION_PATCH 1) -SET (APPLICATION_VERSION_TYPE DS1) +SET (APPLICATION_VERSION_PATCH 2) +SET (APPLICATION_VERSION_TYPE DEVINTERNAL) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}") SET (APPLICATION_VENDOR_ID "com.meltwater") SET (APPLICATION_VENDOR_NAME "Meltwater") From 11ce2d3d63685527b95595549382b404f45277c3 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Thu, 14 Mar 2019 16:02:09 +0100 Subject: [PATCH 05/26] Fix potential memory corruption bug --- src/served/response.cpp | 2 +- src/served/response.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/served/response.cpp b/src/served/response.cpp index 163f893..f636090 100644 --- a/src/served/response.cpp +++ b/src/served/response.cpp @@ -95,7 +95,7 @@ response::body_size() // ----- serialization ----- -const std::string +const std::string & response::to_buffer() { std::stringstream ss; diff --git a/src/served/response.hpp b/src/served/response.hpp index b5e35dd..2c52864 100644 --- a/src/served/response.hpp +++ b/src/served/response.hpp @@ -129,7 +129,7 @@ class response * * @return the HTTP response */ - const std::string to_buffer(); + const std::string & to_buffer(); // ----- stock reply ----- From b05014741c1b4c7a90b8dfb1a90a570d899ccca5 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Thu, 14 Mar 2019 16:03:30 +0100 Subject: [PATCH 06/26] Fix a potential memory corruption on answer --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c687606..1444a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) SET (APPLICATION_VERSION_MINOR 5) -SET (APPLICATION_VERSION_PATCH 2) +SET (APPLICATION_VERSION_PATCH 3) SET (APPLICATION_VERSION_TYPE DEVINTERNAL) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}") SET (APPLICATION_VENDOR_ID "com.meltwater") From 53c959eaebb9122e6b9f35d3b410c9a2e5948100 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Fri, 6 Dec 2019 09:39:01 +0100 Subject: [PATCH 07/26] Add protection to avoid server crash in case of error on socket close () --- src/served/net/connection.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index 440dc68..4a5bdce 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -83,7 +83,11 @@ connection::start() void connection::stop() { - _socket.close(); + try{ + _socket.close(); + }catch(std::exception & e){ + std::cerr << "Error in connection::stop() " << e.what() << std::endl; + } } void From 7e6ddb56eff30ba9dc4882c37abb456b55388296 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Fri, 6 Dec 2019 12:09:28 +0100 Subject: [PATCH 08/26] Update version to new dev version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1444a03..5a66d3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) SET (APPLICATION_VERSION_MINOR 5) -SET (APPLICATION_VERSION_PATCH 3) +SET (APPLICATION_VERSION_PATCH 4) SET (APPLICATION_VERSION_TYPE DEVINTERNAL) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}") SET (APPLICATION_VENDOR_ID "com.meltwater") From ce92cc34fbf0d0f0d802165bd897e16c830b988b Mon Sep 17 00:00:00 2001 From: Quentin BALLAND Date: Wed, 2 Sep 2020 10:29:59 +0200 Subject: [PATCH 09/26] Fix potential segmentation fault at destruction --- src/served/net/server.cpp | 34 ++++++++++++++++++++++------------ src/served/net/server.hpp | 8 +++++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/served/net/server.cpp b/src/served/net/server.cpp index a1ebc62..564a1f2 100644 --- a/src/served/net/server.cpp +++ b/src/served/net/server.cpp @@ -37,8 +37,8 @@ server::server( const std::string & address : _io_service() , _signals(_io_service) , _acceptor(_io_service) - , _connection_manager() , _socket(_io_service) + , _connection_manager() , _request_handler(mux) , _read_timeout(0) , _write_timeout(0) @@ -67,10 +67,16 @@ server::server( const std::string & address _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); _acceptor.bind(endpoint); _acceptor.listen(); - do_accept(); } +server::~server() { + if (_acceptor.is_open()) { + _acceptor.close(); + } + stop(); +} + void server::run(int n_threads /* = 1 */, bool block /* = true */) { @@ -82,26 +88,22 @@ server::run(int n_threads /* = 1 */, bool block /* = true */) */ if ( n_threads > 1 ) { - std::vector v_threads; + _threads.reserve(n_threads); for ( int i = 0; i < n_threads; i++ ) { - v_threads.push_back(std::thread([this](){ + _threads.emplace_back([this] { _io_service.run(); - })); + }); } - for ( auto & thread : v_threads ) + for ( auto & thread : _threads ) { if ( block ) { if ( thread.joinable() ) { - thread.join(); + thread.join(); } } - else - { - thread.detach(); - } } } else @@ -133,8 +135,15 @@ server::stop() { if ( ! _io_service.stopped() ) { - _io_service.stop(); + _io_service.stop(); } + + for (auto& thread : _threads) { + if (thread.joinable()) { + thread.join(); + } + } + _threads.clear(); } void @@ -178,3 +187,4 @@ server::do_await_stop() _connection_manager.stop_all(); }); } + diff --git a/src/served/net/server.hpp b/src/served/net/server.hpp index 9110fff..40d32dd 100644 --- a/src/served/net/server.hpp +++ b/src/served/net/server.hpp @@ -43,15 +43,17 @@ class server boost::asio::io_service _io_service; boost::asio::signal_set _signals; boost::asio::ip::tcp::acceptor _acceptor; - connection_manager _connection_manager; - boost::asio::ip::tcp::socket _socket; - multiplexer & _request_handler; + boost::asio::ip::tcp::socket _socket; + connection_manager _connection_manager; + multiplexer & _request_handler; int _read_timeout; int _write_timeout; size_t _req_max_bytes; + std::vector _threads; public: server(const server&) = delete; + ~server(); server& operator=(const server&) = delete; From c027fb147a3e5c570e39481cbf5ee79931a415aa Mon Sep 17 00:00:00 2001 From: qballand Date: Mon, 4 Jan 2021 11:08:04 +0100 Subject: [PATCH 10/26] Fix lambda temporary shared_ptr not used (avoid side effects) --- src/served/net/connection.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index 4a5bdce..d3206e5 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -129,7 +129,7 @@ connection::do_read() _write_timer.async_wait([this, self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } }); } @@ -178,7 +178,7 @@ connection::do_read() } else if (ec != boost::asio::error::operation_aborted) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } } ); @@ -209,7 +209,7 @@ connection::do_write() if ( ec != boost::asio::error::operation_aborted ) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } } ); From 4ef6efc7d8fe7fcf1fb5558672e5df56cad06e66 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 9 Mar 2019 16:44:46 +0100 Subject: [PATCH 11/26] README: require Boost 1.53 or newer instead of 1.56 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a6f88d..9419e48 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Features: ### Requirements -* [Required] - [Boost 1.56](http://www.boost.org/) +* [Required] - [Boost (1.53 or newer)](http://www.boost.org/) * [Optional] - [Ragel](http://www.complang.org/ragel/) ### Building From 519b570516cda61de9f0585d72935f886373aa06 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 9 Mar 2019 16:23:14 +0100 Subject: [PATCH 12/26] cmake: RPM: resolve boost dependency naming for Redhat --- cmake/CPackConfigRPM.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/CPackConfigRPM.cmake b/cmake/CPackConfigRPM.cmake index f4ca8c6..0e3ea06 100644 --- a/cmake/CPackConfigRPM.cmake +++ b/cmake/CPackConfigRPM.cmake @@ -24,7 +24,7 @@ IF(RPMBUILD_CMD) SET(CPACK_RPM_PACKAGE_RELEASE "${APPLICATION_VERSION_TYPE}") SET(CPACK_RPM_PACKAGE_LICENSE "MIT") SET(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") - SET(CPACK_RPM_PACKAGE_REQUIRES "libboost >= 1.53") + SET(CPACK_RPM_PACKAGE_REQUIRES "boost >= 1.53") SET(CPACK_RPM_PACKAGE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}.${CPACK_RPM_PACKAGE_ARCHITECTURE}") SET(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_SOURCE_DIR}/install/post_install_script.sh") From 1f7453961af4c94e2fa10820a7bbb8d9af546f88 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 9 Mar 2019 16:33:03 +0100 Subject: [PATCH 13/26] served: support building both static and shared libraries --- src/served/CMakeLists.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/served/CMakeLists.txt b/src/served/CMakeLists.txt index 37112f6..c433a29 100644 --- a/src/served/CMakeLists.txt +++ b/src/served/CMakeLists.txt @@ -62,14 +62,20 @@ IF (NOT DEFINED SERVED_BUILD_STATIC) SET(SERVED_BUILD_STATIC 1) ENDIF (NOT DEFINED SERVED_BUILD_STATIC) +IF (SERVED_BUILD_SHARED) + SET(SERVED_TARGET_STATIC_SUFFIX "-static") +ELSE (SERVED_BUILD_SHARED) + SET(SERVED_TARGET_STATIC_SUFFIX "") +ENDIF (SERVED_BUILD_SHARED) + # # Static lib build rules # IF (SERVED_BUILD_STATIC) - ADD_LIBRARY (${PROJECT_NAME} STATIC ${served_SRCS} ${served_RLSOURCES}) - TARGET_LINK_LIBRARIES (${PROJECT_NAME} ${served_LIBS}) - SET_TARGET_PROPERTIES (${PROJECT_NAME} PROPERTIES VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}" OUTPUT_NAME ${served_BIN} CLEAN_DIRECT_OUTPUT 1) - INSTALL (TARGETS ${PROJECT_NAME} DESTINATION lib) + ADD_LIBRARY (${PROJECT_NAME}${SERVED_TARGET_STATIC_SUFFIX} STATIC ${served_SRCS} ${served_RLSOURCES}) + TARGET_LINK_LIBRARIES (${PROJECT_NAME}${SERVED_TARGET_STATIC_SUFFIX} ${served_LIBS}) + SET_TARGET_PROPERTIES (${PROJECT_NAME}${SERVED_TARGET_STATIC_SUFFIX} PROPERTIES VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}" OUTPUT_NAME ${served_BIN} CLEAN_DIRECT_OUTPUT 1) + INSTALL (TARGETS ${PROJECT_NAME}${SERVED_TARGET_STATIC_SUFFIX} DESTINATION lib) ENDIF (SERVED_BUILD_STATIC) # From a2491d5cf9d3e462c2a0f4a1bcf5f0a49aaa078c Mon Sep 17 00:00:00 2001 From: Jeppe Pihl Date: Fri, 28 Jun 2019 11:07:12 +0200 Subject: [PATCH 14/26] Added missing virtual destructor Added missing virtual destructor to `segment_matcher`. --- src/served/mux/segment_matcher.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/served/mux/segment_matcher.hpp b/src/served/mux/segment_matcher.hpp index 56bd94a..1836592 100644 --- a/src/served/mux/segment_matcher.hpp +++ b/src/served/mux/segment_matcher.hpp @@ -54,6 +54,12 @@ class segment_matcher * @param path_segment the segment of path the variable should be extracted from */ virtual void get_param(served::parameters & params, const std::string & path_segment) = 0; + + /* + * Class destructor. + */ + virtual ~segment_matcher() + { } }; typedef std::shared_ptr segment_matcher_ptr; From cfa88ae884a344466d1e16382fe973c6b34d7ea2 Mon Sep 17 00:00:00 2001 From: Jonas Gloning Date: Mon, 22 Jul 2019 03:53:00 +0200 Subject: [PATCH 15/26] `git_repository` is no longer a native rule --- WORKSPACE | 1 + 1 file changed, 1 insertion(+) diff --git a/WORKSPACE b/WORKSPACE index ffe0ed4..3890c6a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,5 +1,6 @@ workspace(name = "com_github_meltwater_served") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "com_github_nelhage_rules_boost", commit = "72ec09168e5c3a296f667b3d956a853ccd65c8ed", From 5f12c39edcfe523a703cdb90ebd6d299ee93b20e Mon Sep 17 00:00:00 2001 From: Jonas Gloning Date: Mon, 22 Jul 2019 04:00:12 +0200 Subject: [PATCH 16/26] Update bazel build rules for boost --- WORKSPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WORKSPACE b/WORKSPACE index 3890c6a..d8fad40 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,7 +3,7 @@ workspace(name = "com_github_meltwater_served") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") git_repository( name = "com_github_nelhage_rules_boost", - commit = "72ec09168e5c3a296f667b3d956a853ccd65c8ed", + commit = "552baa34c17984ca873cd0c4fdd010c177737b6f", remote = "https://github.com/nelhage/rules_boost", ) load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps") From 1eec092714d42cda03c707e2423f7a28d80ee95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20Lul=C3=A9?= Date: Mon, 29 Jul 2019 15:36:16 +0200 Subject: [PATCH 17/26] Fix two name clash with Windows includes - ERROR is a macro in wingdi.h. It can be avoided by defining NOGDI - DELETE is a macro in winnt.h. It has to be undefined --- BUILD | 4 ++++ src/served/methods.hpp | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/BUILD b/BUILD index 8ad1752..76205d4 100644 --- a/BUILD +++ b/BUILD @@ -49,6 +49,10 @@ cc_library( "src/served/net/connection_manager.hpp", "src/served/net/server.hpp", ], + defines = select({ + "@bazel_tools//src/conditions:windows": ["NOGDI"], + "//conditions:default": [], + }), strip_include_prefix = "src/", visibility = ["//visibility:public"], deps = [ diff --git a/src/served/methods.hpp b/src/served/methods.hpp index b9ad62b..bf9c2cd 100644 --- a/src/served/methods.hpp +++ b/src/served/methods.hpp @@ -26,6 +26,12 @@ #include #include +#ifdef _MSC_VER + // DELETE is also defined as a macro in winnt.h, included through boost::asio + #pragma push_macro("DELETE") + #undef DELETE +#endif // _MSC_VER + namespace served { /* @@ -116,4 +122,8 @@ method_from_string(const std::string & str) } // served +#ifdef _MSC_VER + #pragma pop_macro("DELETE") +#endif // _MSC_VER + #endif // SERVED_METHODS_HPP From b4a8133fc3528512963549488d32661cd687f74e Mon Sep 17 00:00:00 2001 From: Jonas Gloning Date: Mon, 5 Aug 2019 12:21:49 +0200 Subject: [PATCH 18/26] + caching --- src/served/response.cpp | 10 ++++++++++ src/served/response.hpp | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/served/response.cpp b/src/served/response.cpp index f636090..fd37843 100644 --- a/src/served/response.cpp +++ b/src/served/response.cpp @@ -71,6 +71,12 @@ response::set_body(const std::string & body) _body.str(body); } +void response::set_response(const std::shared_ptr &res) +{ + respond_with_cache = true; + cache = res; +} + response& response::operator<<(std::string const& rhs) { @@ -98,6 +104,10 @@ response::body_size() const std::string & response::to_buffer() { + + if (respond_with_cache) + return *cache; + std::stringstream ss; ss << "HTTP/1.1 " << _status << " " << status::status_to_reason(_status) << "\r\n"; diff --git a/src/served/response.hpp b/src/served/response.hpp index 2c52864..e9dcb53 100644 --- a/src/served/response.hpp +++ b/src/served/response.hpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -52,6 +54,10 @@ class response std::stringstream _body; std::string _buffer; + bool respond_with_cache{false}; + std::shared_ptr cache; + + public: // ----- constructors ----- @@ -94,6 +100,15 @@ class response */ void set_body(const std::string & body); + /* + * Set the entire response. + * + * Sets the entire response, discarding any previous data stored in both the headers and the body. + * + * @param res the response buffer + */ + void set_response(const std::shared_ptr &res); + /* * Pipe data to the body of the response. * From f9cd24a9186b97086907933cd08c09028b2c94d6 Mon Sep 17 00:00:00 2001 From: Adrian DC Date: Sat, 24 Aug 2019 19:09:18 +0200 Subject: [PATCH 19/26] examples: create json_data example for JSON responses --- src/examples/CMakeLists.txt | 1 + src/examples/json_data/CMakeLists.txt | 36 ++++++++++++ src/examples/json_data/main.cpp | 81 +++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 src/examples/json_data/CMakeLists.txt create mode 100644 src/examples/json_data/main.cpp diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 637b58b..54049ee 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -23,6 +23,7 @@ ADD_SUBDIRECTORY (form_data) ADD_SUBDIRECTORY (handlers) ADD_SUBDIRECTORY (hello_world) ADD_SUBDIRECTORY (hello_world_no_blocking) +ADD_SUBDIRECTORY (json_data) ADD_SUBDIRECTORY (query_params) ADD_SUBDIRECTORY (list_endpoints) ADD_SUBDIRECTORY (request_logger_plugin) diff --git a/src/examples/json_data/CMakeLists.txt b/src/examples/json_data/CMakeLists.txt new file mode 100644 index 0000000..4882b87 --- /dev/null +++ b/src/examples/json_data/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2014 MediaSift Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# +# Locate project sources +# +FILE (GLOB_RECURSE json_data_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) + +# +# Configure common project settings +# +SET (json_data_LIBS ${PROJECT_NAME} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + +# +# Executable build rules +# +ADD_EXECUTABLE (json_data ${json_data_SRCS}) +TARGET_LINK_LIBRARIES (json_data ${json_data_LIBS}) +INSTALL (TARGETS json_data DESTINATION bin) diff --git a/src/examples/json_data/main.cpp b/src/examples/json_data/main.cpp new file mode 100644 index 0000000..708338d --- /dev/null +++ b/src/examples/json_data/main.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 MediaSift Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include + +/* json_data example + * + * This is a JSON data example of served in action. + */ +int main(int, char const**) +{ + served::multiplexer mux; + + mux.handle("/flat") + .get([](served::response & res, const served::request &) { + boost::property_tree::ptree root; + std::string response; + std::stringstream ss; + + // Populate JSON response + root.put("bool", true); + root.put("integer", 123); + root.put("string", "String"); + + // Export JSON response + boost::property_tree::json_parser::write_json(ss, root, true /* human */); + res << ss.str(); + }); + + mux.handle("/nested") + .get([](served::response & res, const served::request &) { + boost::property_tree::ptree root; + boost::property_tree::ptree arrayTree; + boost::property_tree::ptree flatTree; + std::stringstream ss; + + // Populate JSON response + flatTree.put("bool", true); + flatTree.put("integer", 123); + flatTree.put("string", "String"); + arrayTree.push_back(std::make_pair("", flatTree)); + arrayTree.push_back(std::make_pair("", flatTree)); + arrayTree.push_back(std::make_pair("", flatTree)); + root.add_child("node_array", arrayTree); + root.add_child("node_1", flatTree); + + // Export JSON response + boost::property_tree::json_parser::write_json(ss, root, true /* human */); + res << ss.str(); + }); + + std::cout << "Try this example with:" << std::endl; + std::cout << " curl http://localhost:8123/flat" << std::endl; + std::cout << " curl http://localhost:8123/nested" << std::endl; + + served::net::server server("127.0.0.1", "8123", mux); + server.run(10); // Run with a pool of 10 threads. + + return 0; +} From 06cd4f3a978b156ea1b9626a7e65cb276642b182 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Mon, 4 Jan 2021 14:48:31 +0100 Subject: [PATCH 20/26] Update version of served to 1.6.0 DEVINTERNAL to reflect hotfixes --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a66d3b..ed9939d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,8 @@ SET (APPLICATION_NAME "Served HTTP REST Library") SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) -SET (APPLICATION_VERSION_MINOR 5) -SET (APPLICATION_VERSION_PATCH 4) +SET (APPLICATION_VERSION_MINOR 6) +SET (APPLICATION_VERSION_PATCH 0) SET (APPLICATION_VERSION_TYPE DEVINTERNAL) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}") SET (APPLICATION_VENDOR_ID "com.meltwater") From e945cef8729fb3acb4e104521115a9165187dcd3 Mon Sep 17 00:00:00 2001 From: qballand Date: Mon, 4 Jan 2021 11:08:04 +0100 Subject: [PATCH 21/26] Fix lambda temporary shared_ptr not used (avoid side effects) --- src/served/net/connection.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index 4a5bdce..b8247a8 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -74,7 +74,7 @@ connection::start() _read_timer.async_wait([this, self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } }); } @@ -129,7 +129,7 @@ connection::do_read() _write_timer.async_wait([this, self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } }); } @@ -178,7 +178,7 @@ connection::do_read() } else if (ec != boost::asio::error::operation_aborted) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } } ); @@ -209,7 +209,7 @@ connection::do_write() if ( ec != boost::asio::error::operation_aborted ) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); } } ); From 9e34b9abb66b0329fb3cd396e74358160b31405f Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Wed, 6 Jan 2021 14:31:02 +0100 Subject: [PATCH 22/26] Replace auto by explicit typing, make the mess a bit more readable --- src/served/net/connection.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index b8247a8..f7a2cd7 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -69,7 +69,7 @@ connection::start() if ( _read_timeout > 0 ) { - auto self(shared_from_this()); + connection_ptr self(shared_from_this()); _read_timer.async_wait([this, self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) @@ -93,7 +93,7 @@ connection::stop() void connection::do_read() { - auto self(shared_from_this()); + connection_ptr self(shared_from_this()); _socket.async_read_some(boost::asio::buffer(_buffer.data(), _buffer.size()), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { @@ -187,7 +187,7 @@ connection::do_read() void connection::do_write() { - auto self(shared_from_this()); + connection_ptr self(shared_from_this()); boost::asio::async_write(_socket, boost::asio::buffer(_response.to_buffer()), [this, self](boost::system::error_code ec, std::size_t) { From 3f5e07e8e833d68a864aa05dfaa8eb47ce8b901a Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Wed, 6 Jan 2021 18:05:49 +0100 Subject: [PATCH 23/26] Change the scope of a self lifetime object to make lifetime requirement explicit on the entire action --- src/served/net/connection.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index f7a2cd7..6452c19 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -58,6 +58,9 @@ connection::start() { boost::system::error_code ec; boost::asio::ip::tcp::endpoint endpoint = _socket.remote_endpoint(ec); + + connection_ptr self(shared_from_this()); + if (ec) { _connection_manager.stop(shared_from_this()); @@ -69,7 +72,6 @@ connection::start() if ( _read_timeout > 0 ) { - connection_ptr self(shared_from_this()); _read_timer.async_wait([this, self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) From 06a978692d11000d026183c6ebdcb47989b8c602 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Wed, 6 Jan 2021 18:20:38 +0100 Subject: [PATCH 24/26] Remove every this argument in asynchronous callback to make calls to the shared_ptr explicits --- CMakeLists.txt | 11 ++++-- src/served/net/connection.cpp | 64 +++++++++++++++++------------------ 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed9939d..19541bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,13 +77,18 @@ FIND_PACKAGE (RAGEL) FIND_PACKAGE (Threads) -INCLUDE (EnableStdCXX11) -ENABLE_STDCXX11 () +# Enforce C++17 +# This has the advantage to remove an undefined behavior from shared_from_this +# https://en.cppreference.com/w/cpp/memory/enable_shared_from_this/shared_from_this +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # # Enable warnings # -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra -Wno-missing-field-initializers") +set(CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra -Wno-missing-field-initializers") # # Configure Files diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index 6452c19..e33c180 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -63,7 +63,7 @@ connection::start() if (ec) { - _connection_manager.stop(shared_from_this()); + _connection_manager.stop(self); return; } @@ -73,10 +73,10 @@ connection::start() if ( _read_timeout > 0 ) { - _read_timer.async_wait([this, self](const boost::system::error_code& error) { + _read_timer.async_wait([self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) { - _connection_manager.stop(self); + self->_connection_manager.stop(self); } }); } @@ -98,48 +98,48 @@ connection::do_read() connection_ptr self(shared_from_this()); _socket.async_read_some(boost::asio::buffer(_buffer.data(), _buffer.size()), - [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { + [self](boost::system::error_code ec, std::size_t bytes_transferred) { if (!ec) { request_parser_impl::status_type result; - result = _request_parser.parse(_buffer.data(), bytes_transferred); + result = self->_request_parser.parse(self->_buffer.data(), bytes_transferred); if ( request_parser_impl::FINISHED == result ) { // Parsing is finished, stop reading and send response. - _read_timer.cancel(); - _status = status_type::DONE; + self->_read_timer.cancel(); + self->_status = status_type::DONE; try { - _request_handler.forward_to_handler(_response, _request); + self->_request_handler.forward_to_handler(self->_response, self->_request); } catch (const served::request_error & e) { - _response.set_status(e.get_status_code()); - _response.set_header("Content-Type", e.get_content_type()); - _response.set_body(e.what()); + self->_response.set_status(e.get_status_code()); + self->_response.set_header("Content-Type", e.get_content_type()); + self->_response.set_body(e.what()); } catch (...) { - response::stock_reply(status_5XX::INTERNAL_SERVER_ERROR, _response); + response::stock_reply(status_5XX::INTERNAL_SERVER_ERROR, self->_response); } - if ( _write_timeout > 0 ) + if ( self->_write_timeout > 0 ) { - _write_timer.async_wait([this, self](const boost::system::error_code& error) { + self->_write_timer.async_wait([self](const boost::system::error_code& error) { if ( error.value() != boost::system::errc::operation_canceled ) { - _connection_manager.stop(self); + self->_connection_manager.stop(self); } }); } - do_write(); + self->do_write(); try { - _request_handler.on_request_handled(_response, _request); + self->_request_handler.on_request_handled(self->_response, self->_request); } catch (...) { @@ -149,38 +149,38 @@ connection::do_read() { // The client is expecting a 100-continue, so we serve it and continue reading. - response::stock_reply(served::status_1XX::CONTINUE, _response); - do_write(); + response::stock_reply(served::status_1XX::CONTINUE, self->_response); + self->do_write(); } else if ( request_parser_impl::READ_HEADER == result || request_parser_impl::READ_BODY == result ) { // Not finished reading response, continue. - do_read(); + self->do_read(); } else if ( request_parser_impl::REJECTED_REQUEST_SIZE == result ) { // The request is too large and has been rejected - _status = status_type::DONE; + self->_status = status_type::DONE; - response::stock_reply(served::status_4XX::REQ_ENTITY_TOO_LARGE, _response); - do_write(); + response::stock_reply(served::status_4XX::REQ_ENTITY_TOO_LARGE, self->_response); + self->do_write(); } else if ( request_parser_impl::ERROR == result ) { // Error occurred while parsing, respond with BAD_REQUEST - _status = status_type::DONE; + self->_status = status_type::DONE; - response::stock_reply(served::status_4XX::BAD_REQUEST, _response); - do_write(); + response::stock_reply(served::status_4XX::BAD_REQUEST, self->_response); + self->do_write(); } } else if (ec != boost::asio::error::operation_aborted) { - _connection_manager.stop(self); + self->_connection_manager.stop(self); } } ); @@ -192,26 +192,26 @@ connection::do_write() connection_ptr self(shared_from_this()); boost::asio::async_write(_socket, boost::asio::buffer(_response.to_buffer()), - [this, self](boost::system::error_code ec, std::size_t) { + [self](boost::system::error_code ec, std::size_t) { if ( !ec ) { - if ( status_type::READING == _status ) + if ( status_type::READING == self->_status ) { // If we're still reading from the client then continue - do_read(); + self->do_read(); return; } else { // Initiate graceful connection closure. boost::system::error_code ignored_ec; - _socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + self->_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); } } if ( ec != boost::asio::error::operation_aborted ) { - _connection_manager.stop(self); + self->_connection_manager.stop(self); } } ); From 73a79bf4ac7114d73fc689d4e578ff025e8115bc Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Wed, 6 Jan 2021 21:19:55 +0100 Subject: [PATCH 25/26] Add mutex to protect against concurrent modifications on the "connection" object --- src/served/net/connection.cpp | 45 ++++++++++++++++++++++------------- src/served/net/connection.hpp | 8 +++++-- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/served/net/connection.cpp b/src/served/net/connection.cpp index e33c180..102fee2 100644 --- a/src/served/net/connection.cpp +++ b/src/served/net/connection.cpp @@ -31,6 +31,15 @@ using namespace served; using namespace served::net; + +void stop_on_timeout(const boost::system::error_code& error, const connection_ptr & self){ + if ( error.value() != boost::system::errc::operation_canceled ) + { + self->_connection_manager.stop(self); + } +} + + connection::connection( boost::asio::io_service & io_service , boost::asio::ip::tcp::socket socket , connection_manager & manager @@ -56,11 +65,11 @@ connection::connection( boost::asio::io_service & io_service void connection::start() { - boost::system::error_code ec; - boost::asio::ip::tcp::endpoint endpoint = _socket.remote_endpoint(ec); - connection_ptr self(shared_from_this()); + boost::system::error_code ec; + boost::asio::ip::tcp::endpoint endpoint = _socket.remote_endpoint(ec); + if (ec) { _connection_manager.stop(self); @@ -74,10 +83,7 @@ connection::start() { _read_timer.async_wait([self](const boost::system::error_code& error) { - if ( error.value() != boost::system::errc::operation_canceled ) - { - self->_connection_manager.stop(self); - } + stop_on_timeout(error, self); }); } } @@ -85,11 +91,19 @@ connection::start() void connection::stop() { - try{ - _socket.close(); - }catch(std::exception & e){ - std::cerr << "Error in connection::stop() " << e.what() << std::endl; + // use a lock here to avoid any race condition on timeout + std::lock_guard _l(_conn_mutex); + + if(_status != status_type::STOPPED){ + + _status = status_type::STOPPED; + try{ + _socket.close(); + }catch(std::exception & e){ + std::cerr << "Error in connection::stop() " << e.what() << std::endl; + } } + } void @@ -101,6 +115,7 @@ connection::do_read() [self](boost::system::error_code ec, std::size_t bytes_transferred) { if (!ec) { + std::lock_guard _l(self->_conn_mutex); request_parser_impl::status_type result; result = self->_request_parser.parse(self->_buffer.data(), bytes_transferred); @@ -129,10 +144,7 @@ connection::do_read() if ( self->_write_timeout > 0 ) { self->_write_timer.async_wait([self](const boost::system::error_code& error) { - if ( error.value() != boost::system::errc::operation_canceled ) - { - self->_connection_manager.stop(self); - } + stop_on_timeout(error, self); }); } self->do_write(); @@ -195,13 +207,14 @@ connection::do_write() [self](boost::system::error_code ec, std::size_t) { if ( !ec ) { + std::lock_guard _l(self->_conn_mutex); if ( status_type::READING == self->_status ) { // If we're still reading from the client then continue self->do_read(); return; } - else + else if(status_type::DONE == self->_status ) { // Initiate graceful connection closure. boost::system::error_code ignored_ec; diff --git a/src/served/net/connection.hpp b/src/served/net/connection.hpp index 7a3c085..7cae968 100644 --- a/src/served/net/connection.hpp +++ b/src/served/net/connection.hpp @@ -36,6 +36,8 @@ namespace served { namespace net { class connection_manager; +class connection; + /* * Manages the lifecycle of a single HTTP connection. @@ -46,9 +48,8 @@ class connection : public std::enable_shared_from_this { public: - enum status_type { READING = 0, DONE }; + enum status_type { READING = 0, DONE, STOPPED }; -private: boost::asio::io_service & _io_service; status_type _status; boost::asio::ip::tcp::socket _socket; @@ -63,6 +64,7 @@ class connection response _response; boost::asio::deadline_timer _read_timer; boost::asio::deadline_timer _write_timer; + std::mutex _conn_mutex; public: connection& operator=(const connection&) = delete; @@ -112,8 +114,10 @@ class connection void do_write(); }; + typedef std::shared_ptr connection_ptr; + } } // net, served #endif // SERVED_CONNECTION_HPP From 521d89cd21fc616f08e8c80e837185c44f2b3203 Mon Sep 17 00:00:00 2001 From: Adrien Devresse Date: Wed, 6 Jan 2021 21:20:41 +0100 Subject: [PATCH 26/26] Upgrade served version to 1.7.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19541bd..94f7716 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ SET (APPLICATION_NAME "Served HTTP REST Library") SET (APPLICATION_CODENAME "${PROJECT_NAME}") SET (APPLICATION_COPYRIGHT_YEARS "2014") SET (APPLICATION_VERSION_MAJOR 1) -SET (APPLICATION_VERSION_MINOR 6) +SET (APPLICATION_VERSION_MINOR 7) SET (APPLICATION_VERSION_PATCH 0) SET (APPLICATION_VERSION_TYPE DEVINTERNAL) SET (APPLICATION_VERSION_STRING "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}-${APPLICATION_VERSION_TYPE}")