diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e623f6..fe50a47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,8 @@ OPTION(BUILD_SHARED_LIBS "Should the shared lib be built") # Setting vars ################################################################# SET(HTTPP_VERSION_MAJOR "0") -SET(HTTPP_VERSION_MINOR "3") -SET(HTTPP_VERSION_PATCH "3~dev") +SET(HTTPP_VERSION_MINOR "4") +SET(HTTPP_VERSION_PATCH "0~dev") SET(CPACK_PACKAGE_VERSION_MAJOR ${HTTPP_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${HTTPP_VERSION_MINOR}) diff --git a/include/httpp/HttpClient.hpp b/include/httpp/HttpClient.hpp index 764ab9e..1c4f16b 100644 --- a/include/httpp/HttpClient.hpp +++ b/include/httpp/HttpClient.hpp @@ -14,10 +14,10 @@ # include # include # include -# include # include +# include "detail/config.hpp" # include "utils/Exception.hpp" # include "utils/ThreadPool.hpp" # include "http/Protocol.hpp" @@ -38,7 +38,8 @@ class HttpClient public: using Request = HTTP::client::Request; using Response = HTTP::client::Response; - using Future = std::future; + + using Future = detail::Future; using CompletionHandler = std::function; // AsyncHandler is garanteed to be always safe to call diff --git a/include/httpp/detail/config.hpp b/include/httpp/detail/config.hpp new file mode 100644 index 0000000..34907bb --- /dev/null +++ b/include/httpp/detail/config.hpp @@ -0,0 +1,87 @@ +/* + * Part of HTTPP. + * + * Distributed under the 3-clause BSD licence (See LICENCE.TXT file at the + * project root). + * + * Copyright (c) 2014 Thomas Sanchez. All rights reserved. + * + */ + +#ifndef _HTTPP__DETAIL_CONFIG_HPP_ +# define _HTTPP__DETAIL_CONFIG_HPP_ + +# include + +# define HTTPP_USE_BOOST_PROMISE 1 + +# if HTTPP_USE_BOOST_PROMISE +# include +# include +# define HTTPP_PROMISE_NAMESPACE boost +# else +# include +# define HTTPP_PROMISE_NAMESPACE std +# endif + +namespace HTTPP +{ + +namespace detail +{ + +template +using Promise = HTTPP_PROMISE_NAMESPACE::promise; + +template +using Future = decltype(std::declval>().get_future()); + +# if HTTPP_USE_BOOST_PROMISE + +using ExceptionPtr = boost::exception_ptr; + +template +static inline ExceptionPtr +make_exception_ptr(const T& ex) noexcept(noexcept(boost::copy_exception(ex))) +{ + return boost::copy_exception(ex); +} + +static inline ExceptionPtr +current_exception() noexcept(noexcept(boost::current_exception())) +{ + return boost::current_exception(); +} + +[[noreturn]] static void inline rethrow_exception(const ExceptionPtr& ex) +{ + boost::rethrow_exception(ex); +} + +# else + +using ExceptionPtr = std::exception_ptr; + +template +static inline ExceptionPtr make_exception_ptr(const T& ex) +{ + return std::make_exception_ptr(ex); +} + +static inline ExceptionPtr +current_exception() noexcept(noexcept(std::current_exception())) +{ + return std::current_exception(); +} + +[[noreturn]] static void inline rethrow_exception(const ExceptionPtr& ex) +{ + std::rethrow_exception(ex); +} + +# endif + +} // namespace detail +} // namespace HTTPP + +#endif diff --git a/include/httpp/utils/Exception.hpp b/include/httpp/utils/Exception.hpp index e865982..5ed087c 100644 --- a/include/httpp/utils/Exception.hpp +++ b/include/httpp/utils/Exception.hpp @@ -15,20 +15,28 @@ # include # include +# include # include namespace HTTPP { namespace UTILS { -struct OperationAborted : public std::runtime_error + +# if HTTPP_USE_BOOST_PROMISE +# define BASE_EXCEPTION public std::runtime_error, public virtual boost::exception +# else +# define BASE_EXCEPTION public std::runtime_error +# endif + +struct OperationAborted : BASE_EXCEPTION { OperationAborted() : std::runtime_error("Operation Aborted") { } }; -class RequestError : public std::runtime_error +class RequestError : BASE_EXCEPTION { public: RequestError(const std::string& str, HTTP::client::Request&& request) diff --git a/src/httpp/http/client/Connection.cpp b/src/httpp/http/client/Connection.cpp index e6ba732..ab7f5ab 100644 --- a/src/httpp/http/client/Connection.cpp +++ b/src/httpp/http/client/Connection.cpp @@ -66,7 +66,7 @@ Connection::~Connection() { BOOST_LOG_TRIVIAL(error) << "Destroy a not completed connection: " << this; - complete(std::make_exception_ptr( + complete(HTTPP::detail::make_exception_ptr( std::runtime_error("Destroy a non completed connection"))); } catch (const std::exception& ex) @@ -115,7 +115,7 @@ void Connection::init(std::map& so header.clear(); buffer.clear(); - promise = std::promise(); + promise = Promise(); completion_handler = CompletionHandler(); response = Response(); poll_action = 0; @@ -332,7 +332,7 @@ void Connection::buildResponse(CURLcode code) { if (code != CURLE_OK) { - complete(std::make_exception_ptr(RequestError( + complete(HTTPP::detail::make_exception_ptr(RequestError( curl_easy_strerror(code) + std::string(this->error_buffer), std::move(request)))); return; @@ -377,7 +377,7 @@ void Connection::buildResponse(CURLcode code) { BOOST_LOG_TRIVIAL(error) << "Error when building the response: " << exc.what(); - complete(std::make_exception_ptr(RequestNestedError( + complete(HTTPP::detail::make_exception_ptr(RequestNestedError( "Exception happened during buildResponse " + std::string(exc.what()), std::move(response.request)))); return; @@ -385,7 +385,7 @@ void Connection::buildResponse(CURLcode code) } -void Connection::complete(std::exception_ptr ex) +void Connection::complete(ExceptionPtr ex) { bool expected = false; if (!result_notified.compare_exchange_strong(expected, true)) diff --git a/src/httpp/http/client/Connection.hpp b/src/httpp/http/client/Connection.hpp index 712d213..89206a2 100644 --- a/src/httpp/http/client/Connection.hpp +++ b/src/httpp/http/client/Connection.hpp @@ -22,6 +22,7 @@ # include # include +# include "httpp/detail/config.hpp" # include "httpp/utils/ThreadPool.hpp" # include "httpp/http/Protocol.hpp" # include "httpp/http/client/Request.hpp" @@ -42,9 +43,15 @@ struct Connection : public std::enable_shared_from_this { using ConnectionPtr = std::shared_ptr; - // duplicate alias from HttpClient - using Future = std::future; - using CompletionHandler = std::function; + template + using Promise = HTTPP::detail::Promise; + + template + using Future = HTTPP::detail::Future; + + using ExceptionPtr = HTTPP::detail::ExceptionPtr; + + using CompletionHandler = std::function&&)>; Connection(Manager& manager, boost::asio::io_service& service); @@ -94,7 +101,7 @@ struct Connection : public std::enable_shared_from_this void cancel(); void buildResponse(CURLcode code); - void complete(std::exception_ptr ex = nullptr); + void complete(ExceptionPtr ex = ExceptionPtr()); void setSocket(curl_socket_t socket); template @@ -106,7 +113,7 @@ struct Connection : public std::enable_shared_from_this BOOST_LOG_TRIVIAL(error) << "Unknow poll operation requested: " << action; - complete(std::make_exception_ptr( + complete(HTTPP::detail::make_exception_ptr( std::runtime_error("Unknow poll operation requested"))); break; case CURL_POLL_IN: @@ -145,7 +152,7 @@ struct Connection : public std::enable_shared_from_this client::Request request; client::Response response; - std::promise promise; + Promise promise; CompletionHandler completion_handler; bool expect_continue = false; std::vector header; diff --git a/src/httpp/http/client/Manager.cpp b/src/httpp/http/client/Manager.cpp index 8e29bec..82940f3 100644 --- a/src/httpp/http/client/Manager.cpp +++ b/src/httpp/http/client/Manager.cpp @@ -63,9 +63,9 @@ Manager::Manager(UTILS::ThreadPool& io, UTILS::ThreadPool& dispatch) Manager::~Manager() { - std::vector> futures; + std::vector> futures; { - std::promise promise; + Promise promise; auto future = promise.get_future(); io.dispatch([this, &promise, &futures] @@ -99,7 +99,7 @@ Manager::~Manager() } { - std::promise promise; + Promise promise; auto future = promise.get_future(); io.dispatch([this, &promise] { @@ -264,7 +264,7 @@ void Manager::checkHandles() cancelled.first->poll_action = 0; cancelled.first->complete( - std::make_exception_ptr(UTILS::OperationAborted())); + HTTPP::detail::make_exception_ptr(UTILS::OperationAborted())); cancelled.second.set_value(); if (current_connections.count(cancelled.first)) @@ -291,7 +291,7 @@ void Manager::performOp(std::shared_ptr connection, if (ec != boost::asio::error::operation_aborted) { BOOST_LOG_TRIVIAL(warning) << "Error on socket: " << ec.message(); - connection->complete(std::make_exception_ptr( + connection->complete(HTTPP::detail::make_exception_ptr( std::runtime_error("Error on socket: " + ec.message()))); } @@ -313,9 +313,10 @@ void Manager::performOp(std::shared_ptr connection, { BOOST_LOG_TRIVIAL(error) << "Error happened in perfomOp: " << curl_multi_strerror(rc); - auto exc = std::make_exception_ptr(std::runtime_error(curl_multi_strerror(rc))); + auto exc = HTTPP::detail::make_exception_ptr( + std::runtime_error(curl_multi_strerror(rc))); connection->complete(exc); - std::rethrow_exception(exc); + HTTPP::detail::rethrow_exception(exc); } checkHandles(); @@ -365,10 +366,9 @@ void Manager::poll(std::shared_ptr connection, int action) } } - -std::future Manager::cancel_connection(std::shared_ptr connection) +Manager::Future Manager::cancel_connection(std::shared_ptr connection) { - auto promise = std::make_shared>(); + auto promise = std::make_shared>(); auto future = promise->get_future(); io.dispatch(std::bind(&Manager::cancel_connection_io_thread, @@ -379,10 +379,10 @@ std::future Manager::cancel_connection(std::shared_ptr connect } void Manager::cancel_connection_io_thread(std::shared_ptr connection, - std::shared_ptr> promise_ptr) + std::shared_ptr> promise_ptr) { - std::promise promise = std::move(*promise_ptr); + Promise promise = std::move(*promise_ptr); promise_ptr.reset(); auto it = current_connections.find(connection); @@ -390,7 +390,7 @@ void Manager::cancel_connection_io_thread(std::shared_ptr connection if (it == std::end(current_connections)) { BOOST_LOG_TRIVIAL(warning) << "Cannot cancel a completed connection"; - promise.set_exception(std::make_exception_ptr( + promise.set_exception(HTTPP::detail::make_exception_ptr( std::runtime_error("Connection already completed"))); return; } @@ -435,7 +435,7 @@ void Manager::cancelConnection(std::shared_ptr connection) int Manager::closeSocket(curl_socket_t curl_socket) { - std::promise promise; + Promise promise; auto future = promise.get_future(); io.dispatch([this, curl_socket, &promise] @@ -478,7 +478,7 @@ void Manager::handleRequest(Method method, Connection::ConnectionPtr conn) { BOOST_LOG_TRIVIAL(error) << "Error when configuring the request: " << exc.what(); - conn->complete(std::current_exception()); + conn->complete(HTTPP::detail::current_exception()); return; } @@ -488,9 +488,10 @@ void Manager::handleRequest(Method method, Connection::ConnectionPtr conn) { BOOST_LOG_TRIVIAL(error) << "Connection already present: " << conn; - conn->complete(std::make_exception_ptr(std::runtime_error( - "Cannot schedule an operation for an already " - "managed connection"))); + conn->complete( + HTTPP::detail::make_exception_ptr(std::runtime_error( + "Cannot schedule an operation for an already " + "managed connection"))); return; } @@ -501,8 +502,8 @@ void Manager::handleRequest(Method method, Connection::ConnectionPtr conn) BOOST_LOG_TRIVIAL(error) << "Error scheduling a new request: " << message; removeConnection(conn); - conn->complete( - std::make_exception_ptr(std::runtime_error(message))); + conn->complete(HTTPP::detail::make_exception_ptr( + std::runtime_error(message))); return; } diff --git a/src/httpp/http/client/Manager.hpp b/src/httpp/http/client/Manager.hpp index 256c6e2..8a491cc 100644 --- a/src/httpp/http/client/Manager.hpp +++ b/src/httpp/http/client/Manager.hpp @@ -21,6 +21,7 @@ # include # include +# include "httpp/detail/config.hpp" # include "httpp/http/Protocol.hpp" # include "httpp/utils/ThreadPool.hpp" @@ -37,6 +38,13 @@ struct Manager using Method = HTTPP::HTTP::Method; using ConnectionPtr = std::shared_ptr; + template + using Promise = HTTPP::detail::Promise; + + template + using Future = HTTPP::detail::Future; + + Manager(UTILS::ThreadPool& io, UTILS::ThreadPool& dispatch); Manager(const Manager&) = delete; Manager& operator=(const Manager&) = delete; @@ -72,9 +80,9 @@ struct Manager void poll(std::shared_ptr connection, int action); void cancelConnection(std::shared_ptr connection); - std::future cancel_connection(std::shared_ptr connection); + Future cancel_connection(std::shared_ptr connection); void cancel_connection_io_thread(std::shared_ptr connection, - std::shared_ptr> promise); + std::shared_ptr> promise); int closeSocket(curl_socket_t curl_socket); @@ -101,7 +109,7 @@ struct Manager }; std::map, State> current_connections; - std::map, std::promise> cancelled_connections; + std::map, Promise> cancelled_connections; }; } // namespace detail } // namespace client